Skip to content

Commit

Permalink
feat(library/init/control/coroutine_io): coroutine_io
Browse files Browse the repository at this point in the history
  • Loading branch information
Kha committed Aug 17, 2018
1 parent 1ba3c3c commit 1bade37
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
110 changes: 110 additions & 0 deletions library/init/control/coroutine_io.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/-
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
prelude
import init.io init.control.coroutine

/-! A variant of `coroutine` on top of `io`. Implementation. -/

universes u v w r s

namespace coroutine_io
variables {α δ β γ : Type}

export coroutine_result_io (done yielded)

/-- `resume c a` resumes/invokes the coroutine_io `c` with input `a`. -/
@[inline] def resume : coroutine_io α δ β → α → io (coroutine_result_io α δ β)
| (mk k) a := k a

@[inline] protected def pure (b : β) : coroutine_io α δ β :=
mk $ λ _, pure (done b)

/-- Read the input argument passed to the coroutine.
Remark: should we use a different name? I added an instance [monad_reader] later. -/
@[inline] protected def read : coroutine_io α δ α :=
mk $ λ a, pure (done a)

/-- Return the control to the invoker with result `d` -/
@[inline] protected def yield (d : δ) : coroutine_io α δ punit :=
mk $ λ a : α, pure $ yielded d (coroutine_io.pure ⟨⟩)

/-
TODO(Leo): following relations have been commented because Lean4 is currently
accepting non-terminating programs.
/-- Auxiliary relation for showing that bind/pipe terminate -/
inductive direct_subcoroutine_io : coroutine_io α δ β → coroutine_io α δ β → Prop
| mk : ∀ (k : α → coroutine_result α δ β) (a : α) (d : δ) (c : coroutine_io α δ β), k a = yielded d c → direct_subcoroutine_io c (mk k)
theorem direct_subcoroutine_wf : well_founded (@direct_subcoroutine_io α δ β) :=
begin
constructor, intro c,
apply @coroutine.ind _ _ _
(λ c, acc direct_subcoroutine_io c)
(λ r, ∀ (d : δ) (c : coroutine_io α δ β), r = yielded d c → acc direct_subcoroutine_io c),
{ intros k ih, dsimp at ih, constructor, intros c' h, cases h, apply ih h_a h_d, assumption },
{ intros, contradiction },
{ intros d c ih d₁ c₁ heq, injection heq, subst c, assumption }
end
/-- Transitive closure of direct_subcoroutine. It is not used here, but may be useful when defining
more complex procedures. -/
def subcoroutine_io : coroutine_io α δ β → coroutine_io α δ β → Prop :=
tc direct_subcoroutine_io
theorem subcoroutine_wf : well_founded (@subcoroutine_io α δ β) :=
tc.wf direct_subcoroutine_wf
-- Local instances for proving termination by well founded relation
def bind_wf_inst : has_well_founded (Σ' a : coroutine_io α δ β, (β → coroutine_io α δ γ)) :=
{ r := psigma.lex direct_subcoroutine_io (λ _, empty_relation),
wf := psigma.lex_wf direct_subcoroutine_wf (λ _, empty_wf) }
def pipe_wf_inst : has_well_founded (Σ' a : coroutine_io α δ β, coroutine_io δ γ β) :=
{ r := psigma.lex direct_subcoroutine_io (λ _, empty_relation),
wf := psigma.lex_wf direct_subcoroutine_wf (λ _, empty_wf) }
local attribute [instance] wf_inst₁ wf_inst₂
open well_founded_tactics
-/

protected def bind : coroutine_io α δ β → (β → coroutine_io α δ γ) → coroutine_io α δ γ
| (mk k) f := mk $ λ a, k a >>= λ r,
match r, rfl : ∀ (n : _), n = r → _ with
| done b, _ := coroutine_io.resume (f b) a
| yielded d c, h :=
-- have direct_subcoroutine_io c (mk k), { apply direct_subcoroutine.mk k a d, rw h },
pure $ yielded d (bind c f)
-- using_well_founded { dec_tac := unfold_wf_rel >> process_lex (tactic.assumption) }

def pipe : coroutine_io α δ β → coroutine_io δ γ β → coroutine_io α γ β
| (mk k₁) (mk k₂) := mk $ λ a, do
r ← k₁ a,
match r, rfl : ∀ (n : _), n = r → _ with
| done b, h := pure (done b)
| yielded d k₁', h := do
r ← k₂ d,
pure $ match r with
| done b := done b
| yielded r k₂' :=
-- have direct_subcoroutine_io k₁' (mk k₁), { apply direct_subcoroutine.mk k₁ a d, rw h },
yielded r (pipe k₁' k₂')
-- using_well_founded { dec_tac := unfold_wf_rel >> process_lex (tactic.assumption) }

instance : monad (coroutine_io α δ) :=
{ pure := @coroutine_io.pure _ _,
bind := @coroutine_io.bind _ _ }

instance : monad_reader α (coroutine_io α δ) :=
{ read := @coroutine_io.read _ _ }

instance (α δ : Type) : coroutine.monad_coroutine α δ (coroutine_io α δ) :=
{ yield := coroutine_io.yield }

end coroutine_io
10 changes: 10 additions & 0 deletions library/init/io.lean
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,13 @@ meta instance eio_unit.has_eval {ε : Type} [has_to_format ε] : has_eval (excep
match r with
| except.error e := tactic.fail e
| except.ok a := pure ()⟩


local attribute [reducible] io
/-- A variant of `coroutine` on top of `io` -/
mutual inductive coroutine_io, coroutine_result_io (α δ β: Type)
with coroutine_io : Type
| mk {} : (α → io coroutine_result_io) → coroutine_io
with coroutine_result_io : Type
| done {} : β → coroutine_result_io
| yielded {} : δ → coroutine_io → coroutine_result_io

0 comments on commit 1bade37

Please sign in to comment.