Monads are a powerful abstraction in functional programming that allow you to chain operations while handling effects like optionality, state, or IO. In Phunkie, monads extend the Applicative type class and add operations for sequencing computations.
A monad in Phunkie consists of:
The interface is defined as:
interface Monad extends FlatMap {
public function flatten(): Kind;
}
interface FlatMap extends Functor {
public function flatMap(callable $f): Kind;
}
// Chain computations that might fail
$result = Some(42)
->flatMap(fn($x) => Some($x + 1))
->flatMap(fn($x) => $x < 50 ? Some($x) : None());
// None propagates through the chain
$result = None()
->flatMap(fn($x) => Some($x + 1)); // None
// Sequence operations on lists
$result = ImmList(1, 2, 3)
->flatMap(fn($x) => ImmList($x, $x * 2));
// ImmList(1, 2, 2, 4, 3, 6)
// Flatten nested lists
$nested = ImmList(ImmList(1), ImmList(2, 3));
$flat = $nested->flatten(); // ImmList(1, 2, 3)
$f = Function1(fn($x) => $x + 1);
$g = Function1(fn($x) => $x * 2);
// Compose functions with flatMap
$h = $f->flatMap(fn($x) => $g);
$result = $h(5); // (5 + 1) * 2 = 12
All monads must satisfy three fundamental laws:
pure(a)->flatMap(f) === f(a)
$f = fn($x) => Some($x + 1);
Some(42)->flatMap($f) === $f(42)
m->flatMap(pure) === m
Some(42)->flatMap(fn($x) => Some($x)) === Some(42)
m->flatMap(f)->flatMap(g) === m->flatMap(fn($x) => f($x)->flatMap(g))
$f = fn($x) => Some($x + 1);
$g = fn($x) => Some($x * 2);
Some(42)->flatMap($f)->flatMap($g) ===
Some(42)->flatMap(fn($x) => $f($x)->flatMap($g))
Monads can be composed using monad transformers:
// Compose Option with List
$data = OptionT(ImmList(Some(1), None(), Some(2)));
// Map and flatMap work with the nested structure
$result = $data
->map(fn($x) => $x + 1)
->flatMap(fn($x) => OptionT(ImmList(Some($x * 2))));
The Identity monad (Id) is the simplest possible monad that wraps a value. It serves as a way to lift values into a monadic context:
use Phunkie\Cats\Id;
$id = new Id(42);
// Mapping over Id
$result = $id->map(fn($x) => $x + 1);
// Id(43)
// FlatMapping with Id
$result = $id->flatMap(fn($x) => new Id($x + 1));
// Id(43)
// Composition using andThen/compose
$id1 = new Id("Hello");
$id2 = new Id(" World");
$result = $id1->andThen($id2);
// Id("Hello World")
The Id monad is useful for: