A Free monad is a way to build a monad from any functor. It allows you to construct a program as a sequence of commands, separating the description of the program from its interpretation.
A Free monad consists of three constructors:
Pure(a)
: Wraps a pure valueSuspend(fa)
: Lifts a functor into the Free monadBind(target, f)
: Represents sequential compositionabstract class Free {
public static function pure($a)
{
return new Free\Pure($a);
}
public static function liftM(Kind $fa)
{
return new Free\Suspend($fa);
}
public function flatMap($f)
{
return new Free\Bind($this, $f);
}
}
Creates a Free monad containing a pure value:
$program = Free::pure(42);
// Pure(42)
Lifts a functor into the Free monad:
$program = Free::liftM(Some(42));
// Suspend(Some(42))
Chains Free monad operations:
$program = Free::liftM(Some(42))
->flatMap(fn($x) => Free::liftM(Some($x + 1)));
// Bind(Suspend(Some(42)), fn($x) => Suspend(Some($x + 1)))
Interprets a Free structure using a natural transformation:
$program->foldMap(new NaturalTransformation(optionToList));
// Converts from Option to ImmList context
Natural transformations provide a way to interpret Free structures by converting between functors:
$nt = new NaturalTransformation(
fn(Option $o) => $o->toList()
);
// NaturalTransformation[Option ~> ImmList]
$program = Free::liftM(new Query(“SELECT * FROM users”)) ->flatMap(fn($users) => Free::liftM(new Insert(“logs”, [“users” => count($users)])));
2. Separating Business Logic from Effects:
```php
$program = for_(
__($user) ->_(getUserInfo()),
__($perms) ->_(getPermissions($user)),
__($log) ->_(logAccess($user, $perms))
)->yields($user);
// Interpret with different effects (database, HTTP, mock, etc.)
$program->foldMap($interpreter);