phunkie

Monads

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.

What is a Monad?

A monad in Phunkie consists of:

  1. A type constructor M
  2. A way to wrap a value (pure/return)
  3. A way to compose monadic functions (flatMap/bind)

The interface is defined as:

interface Monad extends FlatMap {
    public function flatten(): Kind;
}

interface FlatMap extends Functor {
    public function flatMap(callable $f): Kind;
}

Common Monads in Phunkie

Option Monad

// 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

List Monad

// 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)

Function1 Monad

$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

Monad Laws

All monads must satisfy three fundamental laws:

  1. Left Identity: pure(a)->flatMap(f) === f(a)
$f = fn($x) => Some($x + 1);
Some(42)->flatMap($f) === $f(42)
  1. Right Identity: m->flatMap(pure) === m
Some(42)->flatMap(fn($x) => Some($x)) === Some(42)
  1. Associativity: 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))

Composing Monads

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))));

Best Practices

  1. Use monads to sequence operations with effects
  2. Leverage flatMap for dependent computations
  3. Use monad transformers to work with nested effects
  4. Ensure your monadic operations satisfy the monad laws
  5. Consider using applicative when operations are independent

Implementation Notes

The Identity Monad

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:

  1. Testing monad laws and transformations
  2. Providing a default monad when no specific effects are needed
  3. Serving as an identity for monad transformers
  4. Understanding basic monadic operations