import Batteries.Data.Rat.Basic import Batteries.Control.Lawful.MonadLift -- THIS GETS ME LawfulMonad (OptionT m)! YES import Batteries.Control.OptionT inductive Ast | lit (n : Rat) | add (l r : Ast) | sub (l r : Ast) | mul (l r : Ast) | div (l r : Ast) instance : Coe Rat Ast where coe := .lit instance : OfScientific Ast where ofScientific a b c := .lit (.ofScientific a b c) instance : OfNat Ast n where ofNat := .lit n instance : Add Ast where add := .add instance : Sub Ast where sub := .sub instance : Mul Ast where mul := .mul instance : Div Ast where div := .div def Ast.interpret : Ast → Rat | lit n => n | add l r => l.interpret + r.interpret | sub l r => l.interpret - r.interpret | mul l r => l.interpret * r.interpret | div l r => l.interpret / r.interpret #eval Ast.interpret <| 3/2 + 0.5 abbrev M := OptionT (StateM (Array Rat)) def M.push (n : Rat) : M Unit := modify (·.push n) def M.pop : M Rat := OptionT.mk do let top ← getModify Array.pop return top.back? abbrev M.op (l r : M Unit) (f : Rat → Rat → Rat) : M Unit := do l let l ← pop r let r ← pop push (f l r) def Ast.compile : Ast → M Unit | lit n => M.push n | add l r => M.op l.compile r.compile (·+·) | sub l r => M.op l.compile r.compile (·-·) | mul l r => M.op l.compile r.compile (·*·) | div l r => M.op l.compile r.compile (·/·) def M.run (m : M Unit) : Option Rat := OptionT.run (do m; pop) #[] |>.fst #eval Ast.compile (3/2 + 0.5) |>.run @[simp] theorem M.push_pop : (do push x; pop) = pure x := by funext xs -- what on earth simp only [bind, OptionT.bind, OptionT.mk, StateT.bind, push, modify, modifyGet, MonadStateOf.modifyGet, monadLift, MonadLift.monadLift, OptionT.lift, StateT.modifyGet, pure, StateT.pure, pop, getModify, Array.pop_push, Array.back?_push, OptionT.pure] -- instance : LawfulMonad M where -- map_const := rfl -- id_map {a} x := by -- simp [Functor.map, OptionT.bind, OptionT.mk, StateT.instLawfulMonad] theorem Ast.compile_sound (ast : Ast) : ast.compile = M.push ast.interpret := by induction ast · rfl repeat ( simp only [M.op, compile, interpret, *] repeat rw [←bind_assoc, M.push_pop, pure_bind] )