blob: be7b52dd90ca46b86f84ba0b2d9b1aca6b81bd44 [file] [edit]
/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Core
public import Init.Classical
/-!
# `repeatM`
`repeatM f a` iterates `f : α → m (α ⊕ β)`, recursing on `.inl` and terminating on `.inr`.
-/
variable {α : Type u} {m : Type u → Type v} [Monad m]
/-- The body of `repeatM`: run `f a`, recurse via `recur` on `.inl`, return on `.inr`. -/
public abbrev repeatM.body (f : α → m (α ⊕ β)) (recur : α → m β) (a : α) : m β := do
match ← f a with
| .inl a => recur a
| .inr b => pure b
/-- Pinning predicate for `repeatM.impl`: trivial unless `repeatM.body f` has a fixed point,
in which case `r` is logically pinned to that fixed point applied to `a`. -/
-- For monads like `List`, `Multiset`, no fixed point of `repeatM.body f` need exist:
-- e.g. for `List`, `f a = [.inr 0, .inl a]` forces `g a = [0] ++ g a`, unsatisfiable in
-- finite lists because `++` isn't idempotent. There this `Pred` collapses to `True`;
-- a future per-point `Acc` / `MonadAttach` branch could pin `r` for the cases where
-- execution from `a` is structurally well-founded.
abbrev repeatM.Pred (f : α → m (α ⊕ β)) (a : α) (r : m β) : Prop :=
open scoped Classical in
if h : ∃ g, repeatM.body f g = g then
r = h.choose a
else
True
instance [Nonempty β] {f : α → m (α ⊕ β)} {a : α} :
Nonempty (Subtype (repeatM.Pred f a)) :=
open scoped Classical in
if h : ∃ g, repeatM.body f g = g then
⟨⟨h.choose a, by simp only [repeatM.Pred, dite_eq_left h]⟩⟩
else
⟨⟨pure (Classical.choice inferInstance), by simp only [repeatM.Pred, dite_eq_right h]⟩⟩
/-- Computational core of `repeatM`: returns the loop value paired with its
`repeatM.Pred` proof. -/
partial def repeatM.impl [Nonempty β]
(f : α → m (α ⊕ β)) (a : α) :
Subtype (repeatM.Pred f a) :=
⟨repeatM.body f (repeatM.impl f · |>.val) a, by
simp only [repeatM.Pred]
split <;> rename_i h
· have key : (fun x => (repeatM.impl f x).val) = h.choose := funext fun x => by
simpa only [repeatM.Pred, dite_eq_left h] using (repeatM.impl f x).property
rw [key]; exact congrFun h.choose_spec a
· trivial⟩
/--
An erased version of `repeatM.impl` that eta-expands better in the compiler.
Can be removed once `repeatM.impl` optimizes to the same code.
-/
@[specialize] partial def repeatM.erased [Nonempty β] (f : α → m (α ⊕ β)) (a : α) : m β :=
repeatM.body f (repeatM.erased f ·) a
/-- `repeatM f a` iterates `f` at `a`, recursing on `.inl` and terminating on `.inr`. -/
@[implemented_by repeatM.erased] -- See comment above `repeatM.erased`.
public def repeatM [Nonempty β] (f : α → m (α ⊕ β)) (a : α) : m β :=
(repeatM.impl f a).val
-- Intentionally private: unfolding `repeatM` needs a `MonadTail m` instance and is done
-- in `Init.Internal.Order.While`.
theorem repeatM_eq [Nonempty β] {f : α → m (α ⊕ β)} (a : α)
(h : ∃ g, repeatM.body f g = g) :
repeatM f a = repeatM.body f (repeatM f) a := by
have key : (fun x => (repeatM.impl f x).val) = h.choose := funext fun x => by
simpa only [repeatM.Pred, dite_eq_left h] using (repeatM.impl f x).property
show (repeatM.impl f a).val = repeatM.body f (fun x => (repeatM.impl f x).val) a
rw [key, congrFun key a]; exact (congrFun h.choose_spec a).symm
namespace Lean
/-!
# `Loop` type backing `repeat`/`while`/`repeat ... until`
The parsers and elaborators for `repeat`, `while`, and `repeat ... until` live in
`Lean.Parser.Do` and `Lean.Elab.BuiltinDo.Repeat`. This module only provides the
`Loop` type (and `ForIn` instance) that those elaborators expand to.
-/
public structure Loop
@[inline, expose] public protected def Loop.forIn {β : Type u} {m : Type u → Type v} [Monad m]
(_ : Loop) (init : β) (f : Unit → β → m (ForInStep β)) : m β :=
haveI : Nonempty β := ⟨init⟩
repeatM (a := init) fun b => do
match ← f () b with
| .done b' => pure (.inr b')
| .yield b' => pure (.inl b')
public instance [Monad m] : ForIn m Loop Unit where
forIn := Loop.forIn
end Lean