Applicative Laws
Tutorial Video
Text description (accessibility)
This video demonstrates the "Applicative Laws" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Applicative functors sit between functors and monads — they enable combining multiple independent computations. Key difference from OCaml: 1. **HKT requirement**: These morphisms ideally require higher
Tutorial
The Problem
Applicative functors sit between functors and monads — they enable combining multiple independent computations. Four laws: identity, homomorphism, interchange, and composition. Option and Result are applicatives: applying a wrapped function to a wrapped value. Applicatives enable parallel computation that monads cannot (monad bind is sequential; applicative apply can be parallel).
🎯 Learning Outcomes
Code Example
#![allow(clippy::all)]
//! # Applicative Laws
//! Applicatives extend Functors with pure and apply.
pub trait Applicative<A>: Sized {
fn pure(a: A) -> Self;
fn ap<B>(self, f: Self) -> Self
where
Self: Sized;
}
impl<A> Applicative<A> for Option<A> {
fn pure(a: A) -> Self {
Some(a)
}
fn ap<B>(self, _f: Self) -> Self {
self
}
}
pub fn identity_law<A: Clone + PartialEq>(v: Option<A>) -> bool {
let mapped = v.clone().map(|x| x);
mapped == v
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pure() {
assert_eq!(Option::pure(42), Some(42));
}
}Key Differences
recursion-schemes are widely used; in Rust and OCaml, direct recursion is more common in practice.OCaml Approach
OCaml's pattern matching and recursive types make morphism implementations natural. The Fix type and F-algebra/coalgebra patterns translate directly, though without Haskell's typeclass machinery:
(* OCaml recursive schemes use:
- Recursive variant types for F-algebras
- Higher-order functions for the morphism
- GADTs for type-safe fixed points in advanced cases *)
Full Source
#![allow(clippy::all)]
//! # Applicative Laws
//! Applicatives extend Functors with pure and apply.
pub trait Applicative<A>: Sized {
fn pure(a: A) -> Self;
fn ap<B>(self, f: Self) -> Self
where
Self: Sized;
}
impl<A> Applicative<A> for Option<A> {
fn pure(a: A) -> Self {
Some(a)
}
fn ap<B>(self, _f: Self) -> Self {
self
}
}
pub fn identity_law<A: Clone + PartialEq>(v: Option<A>) -> bool {
let mapped = v.clone().map(|x| x);
mapped == v
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pure() {
assert_eq!(Option::pure(42), Some(42));
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pure() {
assert_eq!(Option::pure(42), Some(42));
}
}