Zulip Chat Archive
Stream: lean4
Topic: Free monad
Tage Johansson (Apr 14 2023 at 10:22):
I didn't find any free monad type implemented, so I tried to implement one myself. However, I get a strange error:
inductive FreeM (m : Type u → Type u) (α : Type u) : Type u where
| pure : α → FreeM m α
| joined : m (FreeM m α) → FreeM m α
(kernel) arg #3 of 'FreeM.joined' contains a non valid occurrence of the datatypes being declared
Can someone please explain what's wrong and how to make a free monad type in Lean?
Thanks,
Tage
Eric Wieser (Apr 14 2023 at 10:33):
Adding the word unsafe
would presumably fix it
Jannis Limperg (Apr 14 2023 at 10:34):
This is the strict positivity restriction, which (necessarily) disallows this type. Do we have any documentation on this? #positivity?
Tage Johansson (Apr 14 2023 at 11:07):
I can add unsafe
. But what implications would that have. I think that someone needs to know what he/she is doing when using such a keyword.
Or is there any other way to implement free monads in Lean that is more safe?
Eric Wieser (Apr 14 2023 at 11:12):
unsafe
means "anything that uses this construction will also be unsafe
", and any proof marked unsafe
cannot be trusted. If all you care about is executing (rather than verifying) code, then unsafe
is no concern
Eric Wieser (Apr 14 2023 at 11:13):
Lean forces you to add unsafe
here in the first place because inductive
types that recurse in this way cannot be trusted in proofs
Jannis Limperg (Apr 14 2023 at 11:18):
This blog post shows how (some representation of) the free monad can be defined in a way that doesn't fall foul of strict positivity.
Eric Rodriguez (Apr 14 2023 at 12:04):
funny thing I discovered whilst trying to learn from the blogpost: why does this code work?
import Mathlib.Logic.Unique
instance : Inhabited (∀ α, α → α) := ⟨fun _ ↦ id⟩
example : Unique (∀ α, α → α) :=
⟨inferInstance,
fun h ↦ by apply h
rfl⟩
but if you comment out the apply h
, it doesn't? :)
Eric Rodriguez (Apr 14 2023 at 12:05):
I found this in Lean3 where this solution was originally given by hint
, but I think the sneaky thing is even less obvious in Lean4
Eric Rodriguez (Apr 14 2023 at 12:13):
(oh, this isn't true, don't mind me - I did a very cursory reading of the thing and now realise that this is nonsense... - the Lean issue caused is funny though!)
Eric Rodriguez (Apr 14 2023 at 12:42):
but without choice, ∀ α, α → α
is unique, right?
Eric Rodriguez (Apr 14 2023 at 12:43):
or well it's undecidable whether it has anything else than id
Kevin Buzzard (Apr 14 2023 at 12:47):
This sounds like the "theorems for free" paper. If you don't allow stuff like "if alpha = nat then \lam x, 0 else id" then yes, you can only have id. I guess the theorems for free paper explains the assumptions necessary to make this true (but possibly not provable).
Arthur Adjedj (Apr 14 2023 at 12:48):
It's false with choice, undecidable without, and true with parametricity, though I do not know of any proof-assistant that implements parametricity in its core theory.
Eric Rodriguez (Apr 14 2023 at 12:49):
the blogpost actually mentions theorems for free
Verity Scheel (Apr 14 2023 at 12:50):
parametricity (which gives you uniqueness of ∀ α, α → α
) and LEM are essentially opposites, and you can prove a strong result about this in HoTT for example: <https://homotopytypetheory.org/2016/02/24/parametricity-and-excluded-middle/>
but parametricity is a hard result to internalize into a theory (in particular, if you had a generic proof of parametricity for polymorphic functions, the proof won’t itself be parametric)
Verity Scheel (Apr 14 2023 at 12:52):
also, undecidable isn’t the right word to be using here, it should technically be called independent (of the axioms), as in the independence of the continuum hypothesis
Eric Wieser (Apr 14 2023 at 12:52):
@Eric Rodriguez, that works because you didn't write what you intended to:
def oops : Unique (∀ α, α → α) :=
⟨inferInstance,
fun h ↦ by apply h
rfl⟩
#check oops -- oops : Unique (∀ (α : Prop), α → α)
Eric Wieser (Apr 14 2023 at 12:53):
If you use Unique (∀ α : Sort u, α → α)
then you'll find you can't prove it
Eric Rodriguez (Apr 14 2023 at 12:55):
no, I know Eric, just thought it was an interesting thing that may be overlooked if you're on autopilot :)
Eric Rodriguez (Apr 14 2023 at 12:55):
I think it adds weight to the "Sort _
should not mean a free universe metavariable but just a newly instantiated universe" argument
Kyle Miller (Apr 14 2023 at 13:28):
Jannis Limperg said:
This is the strict positivity restriction, which (necessarily) disallows this type.
Is strict positivity too strict in this case? Is it possible to prove a contradiction if it were allowed as a type?
Ah, maybe if α = Empty
, you could have m := fun X => if card X = 0 then Unit else Empty
.
Eric Rodriguez (Apr 14 2023 at 13:37):
Verity Scheel said:
parametricity (which gives you uniqueness of
∀ α, α → α
) and LEM are essentially opposites, and you can prove a strong result about this in HoTT for example: <https://homotopytypetheory.org/2016/02/24/parametricity-and-excluded-middle/>but parametricity is a hard result to internalize into a theory (in particular, if you had a generic proof of parametricity for polymorphic functions, the proof won’t itself be parametric)
this proof reminds me of Diaconescu's theorem!
Tage Johansson (Apr 14 2023 at 15:48):
I was finally able to implement my FreeM
based on the article you posted. The definition is quite uggly but I have implemented Monad for it and I have also made a run function FreeM.run [Monad m] (FreeM m a) : m a
.
I have not implemented LawfulMonad
though so I can't be absolutely sure it is correct.
Thans for the help!
/--
- A free monad, which turns any functor `m` into a monad.
- The usual inductive definition of `FreeM` doesn't work in Lean due to strict positivity checking.
- Therefore we use an EQUIVALENT formulation as described
- [here](https://blog.poisson.chat/posts/2021-10-20-initial-final-free-monad.html).
-/
def FreeM (m : Type u → Type v) (α : Type u) : Type _ :=
((FreeMonad : Type u → Type u) → [Monad FreeMonad] → ((a : Type u) → m (FreeMonad a) → FreeMonad a) → FreeMonad α)
def FreeM.pure (x : α) : FreeM m α :=
fun _ _ _ => Pure.pure x
def FreeM.bind (x : FreeM m α) (f : α → FreeM m β) : FreeM m β :=
fun FreeMonad _ join => Bind.bind (m := FreeMonad) (x FreeMonad join) (fun a => f a FreeMonad join)
def FreeM.run [Monad m] (f : FreeM m α) : m α :=
f m fun _ x => x >>= id
instance : Monad (FreeM m) where
pure := FreeM.pure
bind := FreeM.bind
Kyle Miller (Apr 14 2023 at 16:51):
@Tage Johansson Here's it cleaned up a little:
class FreeMonad (f : Type u → Type v) (m : Type w → Type u) extends Monad m where
is_free : (a : Type w) → f (m a) → m a
def FreeM (f : Type u → Type v) (α : Type u) : Type _ :=
((m : Type u → Type u) → [FreeMonad f m] → m α)
def FreeM.pure (x : α) : FreeM f α :=
fun _ _ => Pure.pure x
def FreeM.bind (x : FreeM f α) (g : α → FreeM f β) : FreeM f β :=
fun m _ => x m >>= fun y => g y m -- or `x m >>= (g · m)` for short
instance [Monad m] : FreeMonad m m where
is_free _ := Monad.join -- or `(· >>= id)` to be mathlib-free
def FreeM.run [Monad m] (f : FreeM m α) : m α := f m
instance : Monad (FreeM f) where
pure := FreeM.pure
bind := FreeM.bind
(I also switched m
to f
when it's just a function rather than a monad.)
Kyle Miller (Apr 14 2023 at 17:20):
To define a LawfulMonad
instance I think FreeM
needs to be modified to be
def FreeM (f : Type u → Type v) (α : Type u) : Type _ :=
((m : Type u → Type u) → [FreeMonad f m] → [LawfulMonad m] → m α)
Kyle Miller (Apr 14 2023 at 17:29):
Yep, with this it's lawful:
class FreeMonad (f : Type u → Type v) (m : Type w → Type u) extends Monad m where
is_free : (a : Type w) → f (m a) → m a
def FreeM (f : Type u → Type v) (α : Type u) : Type _ :=
((m : Type u → Type u) → [FreeMonad f m] → [LawfulMonad m] → m α)
def FreeM.pure (x : α) : FreeM f α :=
fun _ _ => Pure.pure x
def FreeM.bind (x : FreeM f α) (g : α → FreeM f β) : FreeM f β :=
fun m _ => x m >>= (g · m)
instance [Monad m] : FreeMonad m m where
is_free _ := (· >>= id)
def FreeM.run [Monad m] [LawfulMonad m] (f : FreeM m α) : m α := f m
instance : Monad (FreeM f) where
pure := FreeM.pure
bind := FreeM.bind
-- Probably there are more precise proofs!
instance : LawfulMonad (FreeM f) where
map_const := rfl
id_map x := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure]
comp_map g h x := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure]
seqLeft_eq x y := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure, Seq.seq, SeqLeft.seqLeft]
seqRight_eq x y := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure, Seq.seq, SeqRight.seqRight]
pure_seq g x := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure, Seq.seq, pure]
bind_pure_comp f x := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure, pure, bind]
bind_map f x := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure, pure, bind, Seq.seq]
pure_bind x f := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure, pure, bind]
bind_assoc x f g := by funext m; funext; simp [Functor.map, FreeM.bind, FreeM.pure, pure, bind]
Eric Wieser (Apr 14 2023 at 17:32):
isn't extends Monad m
unsafe there because Lean can't infer f
?
Kyle Miller (Apr 14 2023 at 17:37):
Hmm, I suppose so from the FreeMonad.toMonad
instance; that's an unfortunate restriction. It shouldn't be hard to turn that into a [Monad m]
argument to the class and fix everything else up.
Tage Johansson (Apr 14 2023 at 18:11):
Eric Wieser said:
isn't
extends Monad m
unsafe there because Lean can't inferf
?
Sorry, but what do you mean? What is unsafe? Why is it "unsafe" to extend with Monad
?
Eric Wieser (Apr 14 2023 at 18:28):
It's the standard "don't write Foo (A B) extends Bar A
because the generated Foo.toBar
instance has a B
that Lean has no way of solving" problem
Last updated: Dec 20 2023 at 11:08 UTC