import Do.Basic
import Do.LazyList
Local Mutation
open Lean
Disable the automatic monadic lifting feature described in the paper. We want to make it clear that we do not depend on it.
set_option autoLift false syntax "let" "mut" ident ":=" term ";" stmt : stmt syntax ident ":=" term : stmt syntax "if" term "then" stmt "else" stmt:1 : stmt declare_syntax_cat expander -- generic syntax for traversal-like functions S_y/R/B/L syntax "expand!" expander "in" stmt:1 : stmt syntax "mut" ident : expander -- corresponds to `S_y` -- generic traversal rules macro_rules | `(stmt| expand! $exp in let $exp: TSyntax `expanderx ← $x: TSyntax `idents; $s: TSyntax `stmts') => `(stmt| let $s': TSyntax `stmtx ← expand! $x: TSyntax `identexp in $exp: TSyntax `expanders; expand! $s: TSyntax `stmtexp in $exp: TSyntax `expanders') -- subsumes (R3, B4, L4) | `(stmt| expand! $s': TSyntax `stmtexp in let mut $exp: TSyntax `expanderx := $x: TSyntax `idente; $e: TSyntax `terms') => `(stmt| let mut $s': TSyntax `stmtx := $x: TSyntax `idente; expand! $e: TSyntax `termexp in $exp: TSyntax `expanders') -- subsumes (R4, B5, L5) | `(stmt| expand! $_ in $s': TSyntax `stmtx:ident := $x: TSyntax `idente) => `(stmt| $e: TSyntax `termx:ident := $x: TSyntax `idente) -- subsumes (R5, B6, L6) | `(stmt| expand! $e: TSyntax `termexp in if $exp: TSyntax `expandere then $e: TSyntax `terms₁ else $s₁: TSyntax `stmts₂) => `(stmt| if $s₂: TSyntax `stmte then expand! $e: TSyntax `termexp in $exp: TSyntax `expanders₁ else expand! $s₁: TSyntax `stmtexp in $exp: TSyntax `expanders₂) -- subsumes (S6, R6, B7, L7) | `(stmt| expand! $s₂: TSyntax `stmtexp in $exp: TSyntax `expanders) => do lets: TSyntax `stmts' ←s': TSyntax `stmtexpandStmtexpandStmt: TSyntax `stmt → MacroM (TSyntax `stmt)s `(stmt| expand! $s: TSyntax `stmtexp in $exp: TSyntax `expanders') macro_rules | `(d! let mut $s': TSyntax `stmtx := $x: TSyntax `idente; $e: TSyntax `terms) => `(let $s: TSyntax `stmtx := $x: TSyntax `idente; StateT.run' (d! expand! mut $e: TSyntax `termx in $x: TSyntax `idents) $s: TSyntax `stmtx) -- (D3) | `(d! $x: TSyntax `identx:ident := $_:term) => -- `s!"..."` is an interpolated string. For more information, see https://leanprover.github.io/lean4/doc/stringinterp.html.x: TSyntax `identthrow <|throw: {ε : Type} → {m : Type → Type} → [self : MonadExcept ε m] → {α : Type} → ε → m αMacro.Exception.errorMacro.Exception.error: Syntax → String → Macro.Exceptionx s!"variable '{x: TSyntax `identx.x: TSyntax `identgetId}' is not reassignable in this scope" | `(d! if $getId: Ident → Namee then $e: TSyntax `terms₁ else $s₁: TSyntax `stmts₂) => `(if $s₂: TSyntax `stmte then d! $e: TSyntax `terms₁ else d! $s₁: TSyntax `stmts₂) -- (D4) macro_rules | `(stmt| expand! mut $_ in $s₂: TSyntax `stmte:term) => `(stmt| StateT.lift $e: TSyntax `terme) -- (S1) | `(stmt| expand! mut $e: TSyntax `termy in let $y: TSyntax `identx ← $x: TSyntax `idents; $s: TSyntax `stmts') => -- (S2) ifs': TSyntax `stmtx ==x: TSyntax `identy theny: TSyntax `identthrow <|throw: {ε : Type} → {m : Type → Type} → [self : MonadExcept ε m] → {α : Type} → ε → m αMacro.Exception.errorMacro.Exception.error: Syntax → String → Macro.Exceptionx s!"cannot shadow 'mut' variable '{x: TSyntax `identx.x: TSyntax `identgetId}'" else `(stmt| let $getId: Ident → Namex ← expand! mut $x: TSyntax `identy in $y: TSyntax `idents; let $s: TSyntax `stmty ← get; expand! mut $y: TSyntax `identy in $y: TSyntax `idents') | `(stmt| expand! mut $s': TSyntax `stmty in let mut $y: TSyntax `identx := $x: TSyntax `idente; $e: TSyntax `terms') => -- (S3) ifs': TSyntax `stmtx ==x: TSyntax `identy theny: TSyntax `identthrow <|throw: {ε : Type} → {m : Type → Type} → [self : MonadExcept ε m] → {α : Type} → ε → m αMacro.Exception.errorMacro.Exception.error: Syntax → String → Macro.Exceptionx s!"cannot shadow 'mut' variable '{x: TSyntax `identx.x: TSyntax `identgetId}'" else `(stmt| let mut $getId: Ident → Namex := $x: TSyntax `idente; expand! mut $e: TSyntax `termy in $y: TSyntax `idents') | `(stmt| expand! mut $s': TSyntax `stmty in $y: TSyntax `identx:ident := $x: TSyntax `idente) => ife: TSyntax `termx ==x: TSyntax `identy then `(stmt| set $y: TSyntax `idente) -- (S5) else `(stmt| $e: TSyntax `termx:ident := $x: TSyntax `idente) -- (S4) macro:0 "let" "mut"e: TSyntax `termx:ident "←"x: TSyntax `idents:stmt:1 ";"s: TSyntax `stmts':stmt : stmt => `(let y ← $s': TSyntax `stmts; let mut $s: TSyntax `stmtx := y; $x: TSyntax `idents') -- (A3) macro:0s': TSyntax `stmtx:ident "←"x: TSyntax `idents:stmt:1 : stmt => `(let y ← $s: TSyntax `stmts; $s: TSyntax `stmtx:ident := y) -- (A4) -- a variant of (A4) since we technically cannot make the above macro a `stmt` macro:0x: TSyntax `identx:ident "←"x: TSyntax `idents:stmt:1 ";"s: TSyntax `stmts':stmt : stmt => `(let y ← $s': TSyntax `stmts; $s: TSyntax `stmtx:ident := y; $x: TSyntax `idents') macro "if"s': TSyntax `stmte:term "then"e: TSyntax `terms₁:stmt:1 : stmt => `(if $s₁: TSyntax `stmte then $e: TSyntax `terms₁ else pure ()) -- (A5) macro "unless"s₁: TSyntax `stmte:term "do'"e: TSyntax `terms₂:stmt:1 : stmt => `(if $s₂: TSyntax `stmte then pure () else $e: TSyntax `terms₂) -- (A6) /- The `variable` command instructs Lean to insert the declared variables as bound variables in definitions that refer to them. -/ variable [s₂: TSyntax `stmtMonadMonad: (Type → Type u_1) → Type (max 1 u_1)m] variable (m: Type u_1 → Type u_2mama: m αma' :ma': m αmm: Type u_1 → Type u_2α) /- Mark `map_eq_pure_bind` as a simplification lemma. It is a theorem for `f <$> x = x >>= pure (f a)` -/ attribute [local simp]α: Type ?u.10009map_eq_pure_bind /- Remark: an `example` in Lean is like a "nameless" definition, and it does not update the environment. It is useful for writing tests. -/ /- The instance `[LawfulMonad m]` contains the monadic laws. For more information, see https://github.com/leanprover/lean4/blob/v4.0.0-m4/src/Init/Control/Lawful.lean -/map_eq_pure_bind: ∀ {m : Type u_1 → Type u_2} {α β : Type u_1} [inst : Monad m] [inst_1 : LawfulMonad m] (f : α → β) (x : m α), f <$> x = do let a ← x pure (f a)example [example: ∀ {m : Type u_1 → Type u_2} {α : Type u_1} [inst : Monad m] (ma : m α) [inst_1 : LawfulMonad m], (do let y ← ma let x : α := y StateT.run' (StateT.lift (pure x)) x) = maLawfulMonadLawfulMonad: (m : Type u_1 → Type u_2) → [inst : Monad m] → Propm] : (do' let mutm: Type u_1 → Type u_2x ←x: αma;ma: m αpurepure: {f : Type u_1 → Type u_2} → [self : Pure f] → {α : Type u_1} → α → f αx :x: αmm: Type u_1 → Type u_2α) =α: Type u_1ma :=ma: m αm: Type ?u.10102 → Type ?u.10058
α: Type ?u.10102
inst✝¹: Monad m
ma, ma': m α(do let y ← ma let x : α := y StateT.run' (StateT.lift (pure x)) x) = maGoals accomplished! 🐙example [example: ∀ {m : Type u_1 → Type u_2} {α : Type u_1} [inst : Monad m] (ma ma' : m α) [inst_1 : LawfulMonad m], (do let y ← ma let x : α := y StateT.run' (do let y ← StateT.lift ma' let _ ← get set y let x ← get StateT.lift (pure x)) x) = do let _ ← ma ma'LawfulMonadLawfulMonad: (m : Type u_1 → Type u_2) → [inst : Monad m] → Propm] : (do' let mutm: Type u_1 → Type u_2x ←x: αma; x ←ma: m αma';ma': m αpurepure: {f : Type u_1 → Type u_2} → [self : Pure f] → {α : Type u_1} → α → f αx) = (x: αma >>= fun _ =>ma: m αma') :=ma': m αm: Type ?u.10970 → Type ?u.10750
α: Type ?u.10970
inst✝¹: Monad m
ma, ma': m α(do let y ← ma let x : α := y StateT.run' (do let y ← StateT.lift ma' let _ ← get set y let x ← get StateT.lift (pure x)) x) = do let _ ← ma ma'/- The command `#check_failure <term>` succeeds only if `<term>` fails to be elaborated. -/Goals accomplished! 🐙variable (b :b: BoolBool) -- The following equivalence is true even if `m` does not satisfy the monadic laws.Bool: Typeexample : (do' ifexample: ∀ {m : Type → Type u_1} {α : Type} [inst : Monad m] (ma : m α) (b : Bool), (if b = true then discard ma else pure ()) = if b = true then discard ma else pure ()b then {b: Booldiscarddiscard: {f : Type → Type u_1} → {α : Type} → [inst : Functor f] → f α → f PUnitma }) = (ifma: m αb thenb: Booldiscarddiscard: {f : Type → Type u_1} → {α : Type} → [inst : Functor f] → f α → f PUnitma elsema: m αpure ()) :=pure: {f : Type → Type u_1} → [self : Pure f] → {α : Type} → α → f αrfl theoremrfl: ∀ {α : Type u_1} {a : α}, a = asimple [simple: ∀ [inst : LawfulMonad m], (do let y ← ma let x : α := y StateT.run' (do if b = true then do let y ← StateT.lift ma' let _ ← get set y else StateT.lift (pure ()) let x ← get StateT.lift (pure x)) x) = do let x ← ma if b = true then ma' else pure xLawfulMonadLawfulMonad: (m : Type → Type u_1) → [inst : Monad m] → Propm] : (do' let mutm: Type → Type u_1x ←x: αma; ifma: m αb then { x ←b: Boolma' };ma': m αpurepure: {f : Type → Type u_1} → [self : Pure f] → {α : Type} → α → f αx) = (x: αma >>= funma: m αx => ifx: αb thenb: Boolma' elsema': m αpurepure: {f : Type → Type u_1} → [self : Pure f] → {α : Type} → α → f αx) :=x: αm: Type → Type u_1
α: Type
inst✝¹: Monad m
ma, ma': m α
b: Bool(do let y ← ma let x : α := y StateT.run' (do if b = true then do let y ← StateT.lift ma' let _ ← get set y else StateT.lift (pure ()) let x ← get StateT.lift (pure x)) x) = do let x ← ma if b = true then ma' else pure xm: Type → Type u_1
α: Type
inst✝¹: Monad m
ma, ma': m α
false(do let y ← ma let x : α := y StateT.run' (do if false = true then do let y ← StateT.lift ma' let _ ← get set y else StateT.lift (pure ()) let x ← get StateT.lift (pure x)) x) = do let x ← ma if false = true then ma' else pure xm: Type → Type u_1
α: Type
inst✝¹: Monad m
ma, ma': m α(do let y ← ma let x : α := y StateT.run' (do if true = true then do let y ← StateT.lift ma' let _ ← get set y else StateT.lift (pure ()) let x ← get StateT.lift (pure x)) x) = do let x ← ma if true = true then ma' else pure xm: Type → Type u_1
α: Type
inst✝¹: Monad m
ma, ma': m α
false(do let y ← ma let x : α := y StateT.run' (do if false = true then do let y ← StateT.lift ma' let _ ← get set y else StateT.lift (pure ()) let x ← get StateT.lift (pure x)) x) = do let x ← ma if false = true then ma' else pure xm: Type → Type u_1
α: Type
inst✝¹: Monad m
ma, ma': m α(do let y ← ma let x : α := y StateT.run' (do if true = true then do let y ← StateT.lift ma' let _ ← get set y else StateT.lift (pure ()) let x ← get StateT.lift (pure x)) x) = do let x ← ma if true = true then ma' else pure xGoals accomplished! 🐙example [example: ∀ {m : Type u_1 → Type u_2} {α : Type u_1} [inst : Monad m] (ma ma' : m α) (b : Bool) [inst_1 : LawfulMonad m] (f : α → α → α), (do let y ← ma let x : α := y StateT.run' (do let y ← if b = true then do let y ← StateT.lift ma let _ ← get set y let _ ← get StateT.lift ma' else StateT.lift ma' let x ← get StateT.lift (pure (f x y))) x) = do let x ← ma if b = true then do let x ← ma let y ← ma' pure (f x y) else do let y ← ma' pure (f x y)LawfulMonadLawfulMonad: (m : Type u_1 → Type u_2) → [inst : Monad m] → Propm] (m: Type u_1 → Type u_2f :f: α → α → αα →α: Type u_1α →α: Type u_1α) : (do' let mutα: Type u_1x ←x: αma; letma: m αy ← ify: αb then { x ←b: Boolma;ma: m αma' } else {ma': m αma' };ma': m αpure (pure: {f : Type u_1 → Type u_2} → [self : Pure f] → {α : Type u_1} → α → f αff: α → α → αxx: αy)) = (y: αma >>= funma: m αx => ifx: αb thenb: Boolma >>= funma: m αx =>x: αma' >>= funma': m αy =>y: αpure (pure: {f : Type u_1 → Type u_2} → [self : Pure f] → {α : Type u_1} → α → f αff: α → α → αxx: αy) elsey: αma' >>= funma': m αy =>y: αpure (pure: {f : Type u_1 → Type u_2} → [self : Pure f] → {α : Type u_1} → α → f αff: α → α → αxx: αy)) :=y: αm: Type ?u.17614 → Type ?u.17083
α: Type ?u.17614
inst✝¹: Monad m
ma, ma': m α
b: Bool
f: α → α → α(do let y ← ma let x : α := y StateT.run' (do let y ← if b = true then do let y ← StateT.lift ma let _ ← get set y let _ ← get StateT.lift ma' else StateT.lift ma' let x ← get StateT.lift (pure (f x y))) x) = do let x ← ma if b = true then do let x ← ma let y ← ma' pure (f x y) else do let y ← ma' pure (f x y)m: Type ?u.17614 → Type ?u.17083
α: Type ?u.17614
inst✝¹: Monad m
ma, ma': m α
f: α → α → α
false(do let y ← ma let x : α := y StateT.run' (do let y ← if false = true then do let y ← StateT.lift ma let _ ← get set y let _ ← get StateT.lift ma' else StateT.lift ma' let x ← get StateT.lift (pure (f x y))) x) = do let x ← ma if false = true then do let x ← ma let y ← ma' pure (f x y) else do let y ← ma' pure (f x y)m: Type ?u.17614 → Type ?u.17083
α: Type ?u.17614
inst✝¹: Monad m
ma, ma': m α
f: α → α → α(do let y ← ma let x : α := y StateT.run' (do let y ← if true = true then do let y ← StateT.lift ma let _ ← get set y let _ ← get StateT.lift ma' else StateT.lift ma' let x ← get StateT.lift (pure (f x y))) x) = do let x ← ma if true = true then do let x ← ma let y ← ma' pure (f x y) else do let y ← ma' pure (f x y)m: Type ?u.17614 → Type ?u.17083
α: Type ?u.17614
inst✝¹: Monad m
ma, ma': m α
f: α → α → α
false(do let y ← ma let x : α := y StateT.run' (do let y ← if false = true then do let y ← StateT.lift ma let _ ← get set y let _ ← get StateT.lift ma' else StateT.lift ma' let x ← get StateT.lift (pure (f x y))) x) = do let x ← ma if false = true then do let x ← ma let y ← ma' pure (f x y) else do let y ← ma' pure (f x y)m: Type ?u.17614 → Type ?u.17083
α: Type ?u.17614
inst✝¹: Monad m
ma, ma': m α
f: α → α → α(do let y ← ma let x : α := y StateT.run' (do let y ← if true = true then do let y ← StateT.lift ma let _ ← get set y let _ ← get StateT.lift ma' else StateT.lift ma' let x ← get StateT.lift (pure (f x y))) x) = do let x ← ma if true = true then do let x ← ma let y ← ma' pure (f x y) else do let y ← ma' pure (f x y)/- Nondeterminism example from Section 2. -/ defGoals accomplished! 🐙choose := @choose: {α : Type u_1} → List α → LazyList αList.toLazy defList.toLazy: {α : Type u_1} → List α → LazyList αex :ex: LazyList NatLazyListLazyList: Type → TypeNat := do' let mutNat: Typex :=x: Nat0; let0: Naty ←y: Natchoose [choose: {α : Type} → List α → LazyList α0,0: Nat1,1: Nat2,2: Nat3]; x :=3: Natx +x: Nat1;1: Natguard (guard: {f : Type → Type} → [inst : Alternative f] → (p : Prop) → [inst : Decidable p] → f Unitx <x: Nat3);3: Natpure (pure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f αx +x: Naty) -- Generate all solutionsy: Natex.ex: LazyList NattoListtoList: {α : Type} → LazyList α → List α