# Functional Patterns — `fp.rs`
Inspired by **Laravel's pipeline**, **AdonisJS middleware chaining**, and standard functional programming utilities.
## 7.1 Core Combinators — `fp/compose.rs`
```rust
/// Thread a value through a sequence of transformations
/// Mirrors Laravel's pipeline / AdonisJS middleware chain
pub fn pipe<T>(value: T, fns: Vec<Box<dyn Fn(T) -> T>>) -> T
/// Compose two functions: compose(f, g)(x) = f(g(x))
pub fn compose<A, B, C>(f: impl Fn(B) -> C, g: impl Fn(A) -> B) -> impl Fn(A) -> C
/// Apply a side-effect then return the original value — Laravel: tap()
pub fn tap<T>(value: T, f: impl FnOnce(&T)) -> T
/// Apply function and return result — AdonisJS: apply
pub fn apply<T, U>(value: T, f: impl FnOnce(T) -> U) -> U
/// Return default if None — convenience over Option::unwrap_or_default
pub fn or_default<T: Default>(opt: Option<T>) -> T
/// Retry an operation N times before failing
pub fn retry<T, E>(times: usize, f: impl Fn() -> Result<T, E>) -> Result<T, E>
```
## 7.2 Lazy & Memoization — `fp/lazy.rs`
```rust
use once_cell::sync::OnceCell;
/// Lazily initialized value — computed only on first access
pub struct Lazy<T> {
cell: OnceCell<T>,
init: Box<dyn Fn() -> T + Send + Sync>,
}
impl<T> Lazy<T> {
pub fn new(init: impl Fn() -> T + Send + Sync + 'static) -> Self
pub fn get(&self) -> &T // computes on first call, returns cached after
pub fn is_initialized(&self) -> bool
}
/// Memoize a single-argument function using a HashMap cache
pub fn memoize<A, R>(f: impl Fn(A) -> R) -> impl FnMut(A) -> R
where
A: std::hash::Hash + Eq + Clone,
R: Clone,
/// Run a closure exactly once, cache the result (thread-safe)
/// Wraps `once_cell::sync::OnceCell`
pub fn once<T>(f: impl FnOnce() -> T) -> impl Fn() -> T
```
## 7.3 Helper Macros — `fp/macros.rs`
```rust
/// Build a Vec of boxed closures for use with pipe()
macro_rules! pipeline {
($($fn:expr),* $(,)?) => {
vec![$(Box::new($fn) as Box<dyn Fn(_) -> _>),*]
};
}
/// Unwrap or return Err with a RokError variant
macro_rules! require {
($opt:expr, $err:expr) => {
match $opt {
Some(v) => v,
None => return Err($err),
}
};
}
/// Conditional expression returning a value
macro_rules! when {
($cond:expr => $then:expr, else $else:expr) => {
if $cond { $then } else { $else }
};
}
```