// Example 226: Category Basics
// A category has objects, morphisms, composition, and identity.
// In Rust: types = objects, functions = morphisms.
// === Approach 1: Simple compose and identity functions ===
fn identity<A>(a: A) -> A {
a
}
fn compose<A, B, C>(f: impl Fn(B) -> C, g: impl Fn(A) -> B) -> impl Fn(A) -> C {
move |a| f(g(a))
}
// === Approach 2: Category trait (explicit abstraction) ===
trait Category {
type Obj;
// We represent morphisms as boxed closures for flexibility
fn id() -> Box<dyn Fn(Self::Obj) -> Self::Obj>;
fn compose(
f: Box<dyn Fn(Self::Obj) -> Self::Obj>,
g: Box<dyn Fn(Self::Obj) -> Self::Obj>,
) -> Box<dyn Fn(Self::Obj) -> Self::Obj>;
}
struct FnCategoryI32;
impl Category for FnCategoryI32 {
type Obj = i32;
fn id() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x)
}
fn compose(
f: Box<dyn Fn(i32) -> i32>,
g: Box<dyn Fn(i32) -> i32>,
) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |x| f(g(x)))
}
}
// === Approach 3: Kleisli category (a -> Option<b>) ===
fn kleisli_id<A>(a: A) -> Option<A> {
Some(a)
}
fn kleisli_compose<A, B, C>(
f: impl Fn(B) -> Option<C> + 'static,
g: impl Fn(A) -> Option<B> + 'static,
) -> impl Fn(A) -> Option<C> {
move |a| g(a).and_then(|b| f(b))
}
fn main() {
// Approach 1: compose and identity
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let h = |x: i32| x - 3;
let fg = compose(f, g);
assert_eq!(fg(5), 11); // (5*2)+1
// Left identity: compose(id, f) = f
assert_eq!(compose(identity, f)(5), f(5));
// Right identity: compose(f, id) = f
assert_eq!(compose(f, identity)(5), f(5));
// Associativity: compose(compose(f,g), h) = compose(f, compose(g,h))
assert_eq!(compose(compose(f, g), h)(10), compose(f, compose(g, h))(10));
println!("Approach 1 - Category laws verified");
// Approach 2: Explicit Category trait
let inc: Box<dyn Fn(i32) -> i32> = Box::new(|x| x + 1);
let dbl: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
let composed = FnCategoryI32::compose(inc, dbl);
assert_eq!(composed(5), 11);
println!("Approach 2 - FnCategory works: compose(+1, *2)(5) = {}", composed(5));
// Approach 3: Kleisli category
let safe_div = |x: i32| -> Option<i32> {
if x == 0 { None } else { Some(100 / x) }
};
let safe_succ = |x: i32| -> Option<i32> { Some(x + 1) };
let k = kleisli_compose(safe_succ, safe_div);
assert_eq!(k(5), Some(21));
assert_eq!(k(0), None);
// Identity laws
let k_id_left = kleisli_compose(kleisli_id, safe_div);
assert_eq!(k_id_left(5), safe_div(5));
let k_id_right = kleisli_compose(safe_div, kleisli_id);
assert_eq!(k_id_right(5), safe_div(5));
println!("Approach 3 - Kleisli category verified");
println!("โ All tests passed");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compose_basic() {
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
assert_eq!(compose(f, g)(5), 11);
}
#[test]
fn test_identity_laws() {
let f = |x: i32| x + 1;
assert_eq!(compose(identity, f)(10), f(10));
assert_eq!(compose(f, identity)(10), f(10));
}
#[test]
fn test_associativity() {
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let h = |x: i32| x - 3;
assert_eq!(
compose(compose(f, g), h)(7),
compose(f, compose(g, h))(7)
);
}
#[test]
fn test_kleisli_compose() {
let safe_div = |x: i32| -> Option<i32> {
if x == 0 { None } else { Some(100 / x) }
};
let safe_succ = |x: i32| -> Option<i32> { Some(x + 1) };
let k = kleisli_compose(safe_succ, safe_div);
assert_eq!(k(5), Some(21));
assert_eq!(k(0), None);
}
}
(* Example 226: Category Basics *)
(* A category has objects, morphisms, composition, and identity *)
(* === Approach 1: Category as composition and identity === *)
(* In OCaml, types are objects and functions are morphisms.
Composition is (.) and identity is Fun.id *)
let compose f g x = f (g x)
let id x = x
(* Category laws:
1. Left identity: compose id f = f
2. Right identity: compose f id = f
3. Associativity: compose (compose f g) h = compose f (compose g h) *)
let () =
let f x = x + 1 in
let g x = x * 2 in
let h x = x - 3 in
(* Left identity *)
assert (compose id f 5 = f 5);
(* Right identity *)
assert (compose f id 5 = f 5);
(* Associativity *)
assert (compose (compose f g) h 10 = compose f (compose g h) 10);
Printf.printf "Approach 1 - Category laws verified\n"
(* === Approach 2: Explicit Category module type === *)
module type CATEGORY = sig
type ('a, 'b) morphism
val id : ('a, 'a) morphism
val compose : ('b, 'c) morphism -> ('a, 'b) morphism -> ('a, 'c) morphism
end
(* The category of OCaml types and functions *)
module FnCategory : CATEGORY with type ('a, 'b) morphism = 'a -> 'b = struct
type ('a, 'b) morphism = 'a -> 'b
let id x = x
let compose f g x = f (g x)
end
let () =
let open FnCategory in
let f = fun x -> x + 1 in
let g = fun x -> x * 2 in
let result = compose f g 5 in
assert (result = 11);
assert (compose id f 5 = f 5);
Printf.printf "Approach 2 - FnCategory works: compose (+1) (*2) 5 = %d\n" result
(* === Approach 3: Kleisli category (morphisms a -> 'b option) === *)
module KleisliOption = struct
type ('a, 'b) morphism = 'a -> 'b option
let id x = Some x
let compose f g x =
match g x with
| None -> None
| Some y -> f y
end
let () =
let open KleisliOption in
let safe_div x = if x = 0 then None else Some (100 / x) in
let safe_succ x = Some (x + 1) in
assert (compose safe_succ safe_div 5 = Some 21);
assert (compose safe_succ safe_div 0 = None);
(* Identity law *)
assert (compose id safe_div 5 = safe_div 5);
assert (compose safe_div id 5 = safe_div 5);
Printf.printf "Approach 3 - Kleisli category verified\n"
let () = Printf.printf "โ All tests passed\n"