Free Monad #
This file defines a general FreeM
monad construction for representing effectful programs
as pure syntax trees, separate from their interpretation.
The FreeM
monad generates a free monad from any type constructor f : Type → Type
, without
requiring f
to be a Functor
. This implementation uses the "freer monad" approach as the
traditional free monad is not safely definable in Lean due to termination checking.
In this construction, effectful programs are represented as trees of effects.
Each node (FreeM.liftBind
) represents a request to perform an effect, accompanied by a
continuation specifying how the computation proceeds after the effect.
The leaves (FreeM.pure
) represent completed computations with final results.
This separation of syntax from semantics enables multiple interpretations of the same program: execution, static analysis, optimization, pretty-printing, verification, and more.
A key insight is that FreeM F
satisfies the universal property of free monads: for any monad
M
and effect handler f : F → M
, there exists a unique way to interpret FreeM F
computations
in M
that respects the effect semantics given by f
.
This unique interpreter is FreeM.liftM f
Main Definitions #
FreeM
: The free monad constructionFreeM.liftM
: The canonical interpreter satisfying the universal propertyFreeM.liftM_unique
: Proof of the universal property
For elimination and interpretation theory, see Free/Fold.lean
.
See the Haskell freer-simple library for the Haskell implementation that inspired this approach.
Implementation Notes #
The FreeM
monad is defined using an inductive type with constructors .pure
and .liftBind
.
We implement Functor
and Monad
instances, and prove the corresponding LawfulFunctor
and LawfulMonad
instances.
For now we choose to make the constructors the simp-normal form, as opposed to the standard monad notation.
The file Free/Effects.lean
demonstrates practical applications by implementing State, Writer, and
Continuations monads using FreeM
with appropriate effect signatures.
The file Free/Fold.lean
provides the theory of the fold operation for free monads.
References #
- Oleg Kiselyov, Hiromi Ishii. Freer Monads, More Extensible Effects
- Bartosz Milewski. The Dao of Functional Programming
Tags #
Free monad, state monad
The Free monad over a type constructor F
.
A FreeM F a
is a tree of operations from the type constructor F
, with leaves of type a
.
It has two constructors: pure
for wrapping a value of type a
, and liftBind
for
representing an operation from F
followed by a continuation.
This construction provides a free monad for any type constructor F
, allowing for composable
effect descriptions that can be interpreted later. Unlike the traditional free monad,
this does not require F
to be a functor.
- pure
{F : Type u → Type v}
{α : Type w}
(a : α)
: FreeM F α
The action that does nothing and returns
a
. - liftBind
{F : Type u → Type v}
{α : Type w}
{ι : Type u}
(op : F ι)
(cont : ι → FreeM F α)
: FreeM F α
Invoke the operation
op
with contuationcont
.Note that Lean's inductive types prevent us splitting this into separate bind and lift constructors.
Instances For
Equations
- FreeM.instPure = { pure := fun {α : Type ?u.9} => FreeM.pure }
Bind operation for the FreeM
monad.
Equations
- (FreeM.pure a).bind f = f a
- (FreeM.liftBind op cont).bind f = FreeM.liftBind op fun (z : ι) => (cont z).bind f
Instances For
Equations
- FreeM.instBind = { bind := fun {α β : Type ?u.10} => FreeM.bind }
Map a function over a FreeM
monad.
Equations
- FreeM.map f (FreeM.pure a) = FreeM.pure (f a)
- FreeM.map f (FreeM.liftBind op cont) = FreeM.liftBind op fun (z : ι) => FreeM.map f (cont z)
Instances For
Lift an operation from the effect signature F
into the FreeM F
monad.
Equations
- FreeM.lift op = FreeM.liftBind op FreeM.pure
Instances For
Rewrite lift
to the constructor form so that simplification stays in constructor normal
form.
Interpret a FreeM F
computation into any monad m
by providing an interpretation
function for the effect signature F
.
This function defines the canonical interpreter from the free monad FreeM F
into the target
monad m
. It is the unique monad morphism that extends the effect handler
interp : ∀ {β}, F β → m β
via the universal property of FreeM
.
Equations
- FreeM.liftM interp (FreeM.pure a) = pure a
- FreeM.liftM interp (FreeM.liftBind op cont) = do let result ← interp op FreeM.liftM (fun {ι : Type ?u.158} => interp) (cont result)
Instances For
A predicate stating that interp : FreeM F α → m α
is an interpreter for the effect
handler handler : ∀ {α}, F α → m α
.
This means that interp
is a monad morphism from the free monad FreeM F
to the
monad m
, and that it extends the interpretation of individual operations
given by f
.
Formally, interp
satisfies the two equations:
interp (pure a) = pure a
interp (liftBind op k) = handler op >>= fun x => interp (k x)
Instances For
The universal property of the free monad FreeM
.
That is, liftM handler
is the unique interpreter that extends the effect handler handler
to
interpret FreeM F
computations in a monad m
.