Zulip Chat Archive
Stream: lean4
Topic: SatisfiesM with Cps monad
Chris Bailey (Mar 19 2023 at 00:40):
Is it possible to write one of the SatisfiesM_M_eq
style lemmas for the continuation monad, or is this one of the cases where the continuation monad doesn't behave nicely? E.g.:
example (p : α → Prop) (x : CpsM α) : SatisfiesM p x → SatisfiesM p (x.run) := sorry
It seems like with a lawful definition of the monad instance, the subtype pulls you into a tug of war with the goal x (k := fun a => a.val)) = (x (k := id)).val
that the lawful definitions just don't allow you to solve, but I wanted to check whether I'm crazy. The full definition of CpsM I'm using is:
def CpsT (m : Type u → Type v) (α : Type u) := (δ : Type u) → (α → m δ) → m δ
namespace CpsT
def runK {α β : Type u} {m : Type u → Type v} (x : CpsT m α) (k : α → m β) : m β :=
x β k
def run {α : Type u} {m : Type u → Type v} [Monad m] (x : CpsT m α) : m α :=
runK x (fun a => pure a)
instance : Monad (CpsT m) where
map f x := fun δ k => x δ fun a => k (f a)
pure a := fun _ k => k a
bind x f := (fun δ k => x δ fun a => f a δ k)
instance : LawfulMonad (CpsT m) := by
refine' { .. } <;> intros <;> rfl
@[reducible]
def CpsM := CpsT Id
Eric Wieser (Jul 13 2023 at 17:24):
I don't have an answer to your question about docs#SatisfiesM, but... Does Cps
exist anywhere in core/std4 already?
Kyle Miller (Jul 13 2023 at 18:14):
I ended up implementing a CPS monad transformer independently, though it's a bit different (δ
is fixed). Pasting it here: (Edit: I guess I was bad at searching before writing this, since it's already in mathlib docs#ContT)
import Lean
structure ContT (α : Type u) (m : Type u → Type v) (β : Type w) where
run : (β → m α) → m α
section
variable {m : Type u → Type v} {α : Type u}
/-- Variant of `run`. -/
def ContT.delimit [Monad m] (x : ContT α m α) : m α := x.run pure
/-- Early exit. -/
def ContT.leave [Monad m] (a : α) : ContT α m β := .mk fun _ => pure a
/-- To lift one-argument functions into `ContT`. Synonym for `ContT.mk`. -/
def ContT.ofFn (f : (β → m α) → m α) : ContT α m β := .mk f
/-- To lift two-argument functions into `ContT`. -/
def ContT.ofFn₂ {β' : β → Sort _} (f : ((b : β) → β' b → m α) → m α) :
ContT α m ((b : β) × β' b) := .mk <| fun g => f fun b b' => g ⟨b, b'⟩
instance [Monad m] : MonadLift m (ContT α m) where
monadLift x := .mk fun f => do f (← x)
@[always_inline]
instance [Monad m] (ε) [MonadExceptOf ε m] : MonadExceptOf ε (ContT α m) where
throw e := liftM (m := m) (throw e)
tryCatch := fun x f => .mk fun g => tryCatchThe ε (x.run g) (fun e => (f e).run g)
def ContT.pure (b : β) : ContT α m β := .mk (fun f => f b)
def ContT.bind (x : ContT α m β) (f : β → ContT α m γ) : ContT α m γ :=
.mk (fun g => x.run (fun b => (f b).run g))
instance : Monad (ContT α m) where
pure := ContT.pure
bind := ContT.bind
-- From Chris
instance : LawfulMonad (ContT α m) := by
refine' { .. } <;> intros <;> rfl
end
Kyle Miller (Jul 13 2023 at 18:16):
One application is using it to make it so local declaration functions in MetaM
can put their new variables on the LHS and save on some nesting:
open Lean Meta
def mkIndic (x : Expr) : MetaM Expr := ContT.delimit do
let arg ← .ofFn <| withLocalDeclD `a (← inferType x)
let eq ← mkEq x arg
mkForallFVars #[arg] eq
def arity (f : Expr) : MetaM Nat := ContT.delimit do
let ⟨args, _⟩ ← .ofFn₂ <| forallTelescope (← inferType f)
return args.size
I'm not sure if this is a great idea (I don't like how it gives you less control over how long local declarations are still around), but it's interesting
Kyle Miller (Jul 13 2023 at 19:00):
@Chris Bailey In case you still care, that example isn't true:
axiom bad (α : Type) (p : α → Prop) (x : CpsM α) : SatisfiesM p x → SatisfiesM p (x.run)
example : False := by
have h := bad Bool (· = true)
simp [SatisfiesM] at h
have := Classical.propDecidable
let x : CpsM {a // a = true} := fun δ f =>
if h1 : δ = Bool then
h1.mpr false
else
f ⟨_, rfl⟩
obtain ⟨⟨_, rfl⟩, h'⟩ := h x
simp [run, runK, Functor.map] at h'
(this is using import Mathlib
)
Eric Wieser (Jul 13 2023 at 21:14):
What's ContT.delimit in the mathlib spelling?
Kyle Miller (Jul 13 2023 at 21:27):
I don't see it, at least not in that module.
When you combine it with monad lifting, it's sort of implementing delimited continuations (which is why I called it ContT.delimit
), in particular the reset
operator; the ContT
constructor is shift
.
Eric Wieser (Jul 13 2023 at 22:17):
I was able to work out what I needed in #5863
Eric Wieser (Jul 13 2023 at 22:17):
I think ofFun
would probably be handy, as right now I have to use a show
Eric Wieser (Jul 13 2023 at 22:18):
The other stumbling block is that I can't seem to throw an exception from within ContT; I haven't yet worked out if this is to be expected, or just a trivial missing instance.
Eric Wieser (Jul 14 2023 at 11:25):
Ah, fixed in #5897
Chris Bailey (Jul 21 2023 at 14:46):
Kyle Miller said:
Chris Bailey In case you still care, that example isn't true
Thank you, mystery solved.
Last updated: Dec 20 2023 at 11:08 UTC