Optics: functional rust grand tour
Tutorial Video
Text description (accessibility)
This video demonstrates the "Optics: functional rust grand tour" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Optics are composable data accessors originating from Haskell's lens library (Edward Kmett, 2012). Key difference from OCaml: 1. **HKT requirement**: Haskell's van Laarhoven encoding uses Functor/Applicative for optic unification requiring HKT; Rust uses explicit struct types per optic kind.
Tutorial
The Problem
Optics are composable data accessors originating from Haskell's lens library (Edward Kmett, 2012). They solve the deeply-nested update problem in immutable data: updating a field three levels deep requires rebuilding all intermediate values. Optics compose — a lens into a struct field composed with a prism for an enum variant gives a combined accessor that can get, set, and modify deeply nested optional values. The optic hierarchy includes Lens (exactly one focus), Prism (zero or one focus on enum variants), Traversal (zero or more foci), and Iso (lossless bidirectional conversion).
🎯 Learning Outcomes
Code Example
#![allow(clippy::all)]
//! # Functional Rust Grand Tour
//! Summary of functional programming patterns in Rust.
// Re-exports and summaries of patterns covered
/// Option: Maybe monad
pub fn option_demo() -> Option<i32> {
Some(42).map(|x| x * 2)
}
/// Result: Error monad
pub fn result_demo() -> Result<i32, &'static str> {
Ok(10).map(|x| x + 1)
}
/// Iterator: List operations
pub fn iter_demo() -> i32 {
(1..=10).filter(|x| x % 2 == 0).sum()
}
/// Pattern matching
pub fn match_demo(x: Option<i32>) -> &'static str {
match x {
Some(n) if n > 0 => "positive",
Some(_) => "other",
None => "none",
}
}
/// Higher-order functions
pub fn hof_demo<F: Fn(i32) -> i32>(f: F) -> i32 {
f(10)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all() {
assert_eq!(option_demo(), Some(84));
assert_eq!(result_demo(), Ok(11));
assert_eq!(iter_demo(), 30);
assert_eq!(match_demo(Some(5)), "positive");
assert_eq!(hof_demo(|x| x * 2), 20);
}
}Key Differences
^., .~, %~ for terse optic use; Rust uses method calls, more verbose but explicit.lens-rs and similar crates provide derive macros for automatic lens generation; OCaml uses ppx_lens for the same.OCaml Approach
OCaml optics use the same record-with-function approach:
type ('s, 'a) lens = { get: 's -> 'a; set: 's -> 'a -> 's }
let name_lens = { get = (fun u -> u.name); set = (fun u n -> { u with name = n }) }
let compose l1 l2 = { get = (fun s -> l2.get (l1.get s)); set = (fun s a -> l1.set s (l2.set (l1.get s) a)) }
Full Source
#![allow(clippy::all)]
//! # Functional Rust Grand Tour
//! Summary of functional programming patterns in Rust.
// Re-exports and summaries of patterns covered
/// Option: Maybe monad
pub fn option_demo() -> Option<i32> {
Some(42).map(|x| x * 2)
}
/// Result: Error monad
pub fn result_demo() -> Result<i32, &'static str> {
Ok(10).map(|x| x + 1)
}
/// Iterator: List operations
pub fn iter_demo() -> i32 {
(1..=10).filter(|x| x % 2 == 0).sum()
}
/// Pattern matching
pub fn match_demo(x: Option<i32>) -> &'static str {
match x {
Some(n) if n > 0 => "positive",
Some(_) => "other",
None => "none",
}
}
/// Higher-order functions
pub fn hof_demo<F: Fn(i32) -> i32>(f: F) -> i32 {
f(10)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all() {
assert_eq!(option_demo(), Some(84));
assert_eq!(result_demo(), Ok(11));
assert_eq!(iter_demo(), 30);
assert_eq!(match_demo(Some(5)), "positive");
assert_eq!(hof_demo(|x| x * 2), 20);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all() {
assert_eq!(option_demo(), Some(84));
assert_eq!(result_demo(), Ok(11));
assert_eq!(iter_demo(), 30);
assert_eq!(match_demo(Some(5)), "positive");
assert_eq!(hof_demo(|x| x * 2), 20);
}
}
Deep Comparison
Functional Rust Grand Tour
Summary of all FP patterns covered in this series