Effect system sim
Tutorial Video
Text description (accessibility)
This video demonstrates the "Effect system sim" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. This example explores advanced type theory concepts applied to Rust. Key difference from OCaml: 1. **HKT gap**: Haskell and partly OCaml have higher
Tutorial
The Problem
This example explores advanced type theory concepts applied to Rust. Church encoding, Scott encoding, finally tagless, free monads, and effect systems are all techniques from the typed lambda calculus and category theory research community. They demonstrate how to encode data and control as pure functions, how to build extensible DSLs without committing to a representation, and how to model effects as values rather than side effects. These patterns originate in Haskell and OCaml research and require adapting to Rust's ownership model.
🎯 Learning Outcomes
Code Example
#![allow(clippy::all)]
//! # Effect System Simulation
//!
//! Simulate algebraic effects using Result and continuation passing.
/// Effect type for IO operations.
#[derive(Debug, Clone)]
pub enum IoEffect {
Print(String),
Read,
}
/// Effect type for state operations.
#[derive(Debug, Clone)]
pub enum StateEffect<S> {
Get,
Put(S),
}
/// Simple effect handler using callbacks.
pub fn handle_io<T>(effect: IoEffect, on_print: impl Fn(&str) -> T, on_read: impl Fn() -> T) -> T {
match effect {
IoEffect::Print(s) => on_print(&s),
IoEffect::Read => on_read(),
}
}
/// State handler.
pub fn handle_state<S: Clone, T>(
effect: StateEffect<S>,
state: &mut S,
on_get: impl Fn(&S) -> T,
on_put: impl Fn() -> T,
) -> T {
match effect {
StateEffect::Get => on_get(state),
StateEffect::Put(new_state) => {
*state = new_state;
on_put()
}
}
}
/// Run a stateful computation.
pub fn run_state<S: Clone, T>(init: S, f: impl FnOnce(&mut S) -> T) -> (T, S) {
let mut state = init;
let result = f(&mut state);
(result, state)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handle_io() {
let result = handle_io(IoEffect::Print("hello".into()), |s| s.len(), || 0);
assert_eq!(result, 5);
}
#[test]
fn test_run_state() {
let (result, final_state) = run_state(0i32, |s| {
*s += 10;
*s *= 2;
*s
});
assert_eq!(result, 20);
assert_eq!(final_state, 20);
}
}Key Differences
OCaml Approach
OCaml is the natural home for these patterns — they originate in the ML/Haskell research community:
(* OCaml's polymorphism and first-class modules make these patterns
more elegant than in Rust. Higher-kinded types are emulated in OCaml
using functors and first-class modules rather than GATs. *)
Full Source
#![allow(clippy::all)]
//! # Effect System Simulation
//!
//! Simulate algebraic effects using Result and continuation passing.
/// Effect type for IO operations.
#[derive(Debug, Clone)]
pub enum IoEffect {
Print(String),
Read,
}
/// Effect type for state operations.
#[derive(Debug, Clone)]
pub enum StateEffect<S> {
Get,
Put(S),
}
/// Simple effect handler using callbacks.
pub fn handle_io<T>(effect: IoEffect, on_print: impl Fn(&str) -> T, on_read: impl Fn() -> T) -> T {
match effect {
IoEffect::Print(s) => on_print(&s),
IoEffect::Read => on_read(),
}
}
/// State handler.
pub fn handle_state<S: Clone, T>(
effect: StateEffect<S>,
state: &mut S,
on_get: impl Fn(&S) -> T,
on_put: impl Fn() -> T,
) -> T {
match effect {
StateEffect::Get => on_get(state),
StateEffect::Put(new_state) => {
*state = new_state;
on_put()
}
}
}
/// Run a stateful computation.
pub fn run_state<S: Clone, T>(init: S, f: impl FnOnce(&mut S) -> T) -> (T, S) {
let mut state = init;
let result = f(&mut state);
(result, state)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handle_io() {
let result = handle_io(IoEffect::Print("hello".into()), |s| s.len(), || 0);
assert_eq!(result, 5);
}
#[test]
fn test_run_state() {
let (result, final_state) = run_state(0i32, |s| {
*s += 10;
*s *= 2;
*s
});
assert_eq!(result, 20);
assert_eq!(final_state, 20);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handle_io() {
let result = handle_io(IoEffect::Print("hello".into()), |s| s.len(), || 0);
assert_eq!(result, 5);
}
#[test]
fn test_run_state() {
let (result, final_state) = run_state(0i32, |s| {
*s += 10;
*s *= 2;
*s
});
assert_eq!(result, 20);
assert_eq!(final_state, 20);
}
}
Deep Comparison
Effect System Simulation
Algebraic effects separate what from how:
Rust doesn't have native effect systems, but we can simulate with: