113 lines
4.4 KiB
Text
113 lines
4.4 KiB
Text
/-
|
||
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_core (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 }
|
||
|
||
instance : monad_io (coroutine_io α δ) :=
|
||
{ monad_lift := λ _ x, mk (λ _, done <$> x) }
|
||
|
||
end coroutine_io
|