pub struct Thunk<'a, A>(/* private fields */);Expand description
A deferred computation that produces a value of type A.
Thunk is NOT memoized and does not cache results. Since evaluate takes
self by value, a Thunk can only be evaluated once. If you need the result more than once,
wrap it in Lazy via into_rc_lazy.
Unlike Trampoline, Thunk does NOT require 'static and CAN implement
HKT traits like Functor, Semimonad, etc.
§Higher-Kinded Type Representation
The higher-kinded representation of this type constructor is ThunkBrand,
which is fully polymorphic over the result type.
§Trade-offs vs Trampoline
| Aspect | Thunk<'a, A> | Trampoline<A> |
|---|---|---|
| HKT compatible | Yes | No (requires 'static) |
| Stack-safe | Partial (tail_rec_m only) | Yes (unlimited) |
| Lifetime | 'a (can borrow) | 'static only |
| Thread safety | Not Send | Not Send (A: 'static) |
| Use case | Glue code, composition | Deep recursion, pipelines |
§Algebraic Properties
Thunk is a proper Monad:
pure(a).evaluate() == a(left identity).thunk.bind(pure) == thunk(right identity).thunk.bind(f).bind(g) == thunk.bind(|a| f(a).bind(g))(associativity).
§Stack Safety
Thunk::bind chains are not stack-safe. Each nested bind adds a
frame to the call stack, so sufficiently deep chains will cause a stack overflow.
For stack-safe recursion within Thunk, use tail_rec_m, which
uses an internal loop to avoid growing the stack.
For unlimited stack safety on all operations (including bind chains of arbitrary
depth), convert to Trampoline instead, which is built
on the Free monad and guarantees O(1) stack usage.
§Limitations
Cannot implement Traversable: The Traversable trait
requires Self::Of<'a, B>: Clone (i.e., Thunk<'a, B>: Clone) in both traverse and
sequence. Thunk wraps Box<dyn FnOnce() -> A>, which cannot implement Clone
because FnOnce closures are consumed on invocation and Box<dyn FnOnce> does not
support cloning. Since the trait bounds on Traversable are fixed, there is no way
to implement the trait for Thunk without changing its internal representation.
This is an intentional trade-off: Thunk prioritizes zero-overhead deferred execution
and lifetime flexibility over structural cloning.
Implemented typeclasses:
Functor,Foldable,Semimonad/Monad,Semiapplicative/Applicative- Not
Traversable(requiresClone)
§Type Parameters
'a: The lifetime of the computation.A: The type of the value produced by the computation.
Implementations§
Source§impl<'a, A: 'a> Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the value produced by the computation.
impl<'a, A: 'a> Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.A: The type of the value produced by the computation.
Sourcepub fn pure(a: A) -> Selfwhere
A: 'a,
pub fn pure(a: A) -> Selfwhere
A: 'a,
Returns a pure value (already computed).
§Type Signature
forall A. A -> Thunk A
§Parameters
a: The value to wrap.
§Returns
A new Thunk instance containing the value.
§Examples
use fp_library::{
brands::*,
classes::*,
functions::*,
};
let thunk = pure::<ThunkBrand, _>(42);
assert_eq!(thunk.evaluate(), 42);Sourcepub fn defer(f: impl FnOnce() -> Thunk<'a, A> + 'a) -> Self
pub fn defer(f: impl FnOnce() -> Thunk<'a, A> + 'a) -> Self
Defers a computation that returns a Thunk.
§Type Signature
forall A. (() -> Thunk A) -> Thunk A
§Parameters
f: The thunk that returns aThunk.
§Returns
A new Thunk instance.
§Examples
use fp_library::{
brands::*,
functions::*,
types::*,
};
let thunk = Thunk::defer(|| pure::<ThunkBrand, _>(42));
assert_eq!(thunk.evaluate(), 42);Sourcepub fn bind<B: 'a>(self, f: impl FnOnce(A) -> Thunk<'a, B> + 'a) -> Thunk<'a, B>
pub fn bind<B: 'a>(self, f: impl FnOnce(A) -> Thunk<'a, B> + 'a) -> Thunk<'a, B>
Monadic bind: chains computations.
Note: Each bind adds to the call stack. For deep recursion,
use Trampoline instead.
This inherent method accepts FnOnce for maximum flexibility. The HKT-level
Semimonad::bind requires Fn instead,
because some types (such as Vec) need to call the function multiple times.
Prefer this inherent method when you do not need HKT generality.
§Type Signature
forall A B. (Thunk A, A -> Thunk B) -> Thunk B
§Type Parameters
B: The type of the result of the new computation.
§Parameters
self: The thunk instance.f: The function to apply to the result of the computation.
§Returns
A new Thunk instance representing the chained computation.
§Examples
use fp_library::{
brands::*,
functions::*,
};
let thunk = pure::<ThunkBrand, _>(21).bind(|x| pure::<ThunkBrand, _>(x * 2));
assert_eq!(thunk.evaluate(), 42);Sourcepub fn map<B: 'a>(self, f: impl FnOnce(A) -> B + 'a) -> Thunk<'a, B>
pub fn map<B: 'a>(self, f: impl FnOnce(A) -> B + 'a) -> Thunk<'a, B>
Functor map: transforms the result.
This inherent method accepts FnOnce, which is more permissive than the
HKT Functor::map free function. The HKT version requires Fn because
the trait signature must support containers with multiple elements (e.g., Vec).
Since Thunk contains exactly one value, FnOnce suffices here. Prefer
this method when you do not need HKT polymorphism and want to pass a
non-reusable closure.
§Type Signature
forall A B. (Thunk A, A -> B) -> Thunk B
§Type Parameters
B: The type of the result of the transformation.
§Parameters
self: The thunk instance.f: The function to apply to the result of the computation.
§Returns
A new Thunk instance with the transformed result.
§Examples
use fp_library::{
brands::*,
functions::*,
};
let thunk = pure::<ThunkBrand, _>(21).map(|x| x * 2);
assert_eq!(thunk.evaluate(), 42);Sourcepub fn into_rc_lazy(self) -> Lazy<'a, A, RcLazyConfig>
pub fn into_rc_lazy(self) -> Lazy<'a, A, RcLazyConfig>
Converts this Thunk into a memoized Lazy value.
The computation will be evaluated at most once; subsequent accesses return the cached result.
§Type Signature
forall A. Thunk A -> Lazy A RcLazyConfig
§Returns
A memoized Lazy value that evaluates this thunk on first access.
§Examples
use fp_library::types::*;
let thunk = Thunk::new(|| 42);
let lazy = thunk.into_rc_lazy();
assert_eq!(*lazy.evaluate(), 42);Sourcepub fn into_arc_lazy(self) -> Lazy<'a, A, ArcLazyConfig>
pub fn into_arc_lazy(self) -> Lazy<'a, A, ArcLazyConfig>
Evaluates this Thunk and wraps the result in a thread-safe ArcLazy.
The thunk is evaluated eagerly because its inner closure is !Send
(it is stored as Box<dyn FnOnce() -> A + 'a>), so it cannot be
placed inside an Arc-based lazy value that requires Send. By
evaluating first, only the resulting A (which is Send + Sync)
needs to cross the thread-safety boundary.
§Type Signature
forall A. Thunk A -> Lazy A ArcLazyConfig
§Returns
A thread-safe ArcLazy containing the eagerly evaluated result.
§Examples
use fp_library::types::*;
let thunk = Thunk::new(|| 42);
let lazy = thunk.into_arc_lazy();
assert_eq!(*lazy.evaluate(), 42);Trait Implementations§
Source§impl<'a, A> Debug for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the computed value.
impl<'a, A> Debug for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.A: The type of the computed value.
Source§fn fmt(&self, f: &mut Formatter<'_>) -> Result
fn fmt(&self, f: &mut Formatter<'_>) -> Result
Formats the thunk without evaluating it.
§Type Signature
forall A. (&Thunk A, &mut fmt) -> fmt
§Parameters
&self: The thunk to format.f: The formatter.
§Returns
The formatting result.
§Examples
use fp_library::types::*;
let thunk = Thunk::pure(42);
assert_eq!(format!("{:?}", thunk), "Thunk(<unevaluated>)");Source§impl<'a, A: 'a> Deferrable<'a> for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the value produced by the computation.
impl<'a, A: 'a> Deferrable<'a> for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.A: The type of the value produced by the computation.
Source§fn defer(f: impl FnOnce() -> Self + 'a) -> Selfwhere
Self: Sized,
fn defer(f: impl FnOnce() -> Self + 'a) -> Selfwhere
Self: Sized,
Creates a Thunk from a computation that produces it.
§Type Signature
forall A. (() -> Thunk A) -> Thunk A
§Parameters
f: A thunk that produces the thunk.
§Returns
The deferred thunk.
§Examples
use fp_library::{
brands::*,
classes::Deferrable,
functions::*,
types::*,
};
let task: Thunk<i32> = Deferrable::defer(|| Thunk::pure(42));
assert_eq!(task.evaluate(), 42);Source§impl<'a, A, Config> From<Lazy<'a, A, Config>> for Thunk<'a, A>where
A: Clone + 'a,
Config: LazyConfig,
§Type Parameters
'a: The lifetime of the computation.
A: The type of the value produced by the computation.
Config: The memoization configuration.
impl<'a, A, Config> From<Lazy<'a, A, Config>> for Thunk<'a, A>where
A: Clone + 'a,
Config: LazyConfig,
§Type Parameters
'a: The lifetime of the computation.A: The type of the value produced by the computation.Config: The memoization configuration.
Source§fn from(lazy: Lazy<'a, A, Config>) -> Self
fn from(lazy: Lazy<'a, A, Config>) -> Self
Converts a Lazy value into a Thunk by cloning the memoized value.
This conversion clones the cached value on each evaluation.
The cost depends on the Clone implementation of A.
§Type Signature
forall A Config. LazyConfig Config => Lazy A Config -> Thunk A Config
§Parameters
lazy: The lazy value to convert.
§Returns
A thunk that evaluates the lazy value.
§Examples
use fp_library::types::*;
let lazy = Lazy::<_, RcLazyConfig>::pure(42);
let thunk = Thunk::from(lazy);
assert_eq!(thunk.evaluate(), 42);Source§impl<'a, A: 'a> From<SendThunk<'a, A>> for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the value produced by the computation.
impl<'a, A: 'a> From<SendThunk<'a, A>> for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.A: The type of the value produced by the computation.
Source§fn from(send_thunk: SendThunk<'a, A>) -> Self
fn from(send_thunk: SendThunk<'a, A>) -> Self
Converts a SendThunk into a Thunk by erasing the Send bound.
This is a zero-cost unsizing coercion: the inner
Box<dyn FnOnce() -> A + Send + 'a> is coerced to
Box<dyn FnOnce() -> A + 'a>, which the compiler performs
without any runtime overhead.
§Type Signature
forall A. SendThunk A -> Thunk A
§Parameters
send_thunk: The send thunk to convert.
§Returns
A Thunk wrapping the same deferred computation.
§Examples
use fp_library::types::*;
let send_thunk = SendThunk::pure(42);
let thunk = Thunk::from(send_thunk);
assert_eq!(thunk.evaluate(), 42);Source§impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the computed value.
impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig>
§Type Parameters
'a: The lifetime of the computation.A: The type of the computed value.
Source§fn from(eval: Thunk<'a, A>) -> Self
fn from(eval: Thunk<'a, A>) -> Self
§Type Signature
forall A. Thunk A -> Lazy A
§Parameters
eval: The thunk to convert.
§Returns
A new Lazy instance that will evaluate the thunk on first access.
§Examples
use fp_library::{
classes::*,
types::*,
};
let thunk = Thunk::new(|| 42);
let lazy: RcLazy<i32> = Lazy::from(thunk);
assert_eq!(*lazy.evaluate(), 42);Source§impl<'a, A: Send + Sync + 'a> From<Thunk<'a, A>> for Lazy<'a, A, ArcLazyConfig>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the computed value.
impl<'a, A: Send + Sync + 'a> From<Thunk<'a, A>> for Lazy<'a, A, ArcLazyConfig>
§Type Parameters
'a: The lifetime of the computation.A: The type of the computed value.
Source§fn from(eval: Thunk<'a, A>) -> Self
fn from(eval: Thunk<'a, A>) -> Self
Converts a Thunk into an ArcLazy by eagerly evaluating the thunk.
Thunk is !Send, so the value must be computed immediately to cross
into the thread-safe ArcLazy world.
§Type Signature
forall A. Thunk A -> Lazy A
§Parameters
eval: The thunk to convert.
§Returns
A new Lazy instance containing the eagerly evaluated value.
§Examples
use fp_library::{
classes::*,
types::*,
};
let thunk = Thunk::new(|| 42);
let lazy: ArcLazy<i32> = ArcLazy::from(thunk);
assert_eq!(*lazy.evaluate(), 42);Source§impl<'a, A> From<Thunk<'a, A>> for SendThunk<'a, A>where
A: Send + 'a,
§Type Parameters
'a: The lifetime of the computation.
A: The type of the value.
impl<'a, A> From<Thunk<'a, A>> for SendThunk<'a, A>where
A: Send + 'a,
§Type Parameters
'a: The lifetime of the computation.A: The type of the value.
Source§fn from(thunk: Thunk<'a, A>) -> Self
fn from(thunk: Thunk<'a, A>) -> Self
Converts a Thunk into a SendThunk.
The Thunk closure is not Send, so the conversion eagerly
evaluates it and wraps the owned result in a new SendThunk.
§Type Signature
forall A. Thunk A -> SendThunk A
§Parameters
thunk: The thunk to convert.
§Returns
A new SendThunk wrapping the evaluated result.
§Examples
use fp_library::types::*;
let thunk = Thunk::pure(42);
let send_thunk = SendThunk::from(thunk);
assert_eq!(send_thunk.evaluate(), 42);Source§impl<'a, A: 'a, E: 'a> From<Thunk<'a, A>> for TryThunk<'a, A, E>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the success value.
E: The type of the error value.
impl<'a, A: 'a, E: 'a> From<Thunk<'a, A>> for TryThunk<'a, A, E>
§Type Parameters
'a: The lifetime of the computation.A: The type of the success value.E: The type of the error value.
Source§fn from(eval: Thunk<'a, A>) -> Self
fn from(eval: Thunk<'a, A>) -> Self
§Type Signature
forall A E. Thunk A -> TryThunk A E
§Parameters
eval: The thunk to convert.
§Returns
A new TryThunk instance that wraps the thunk.
§Examples
use fp_library::types::*;
let thunk = Thunk::new(|| 42);
let try_thunk: TryThunk<i32, ()> = TryThunk::from(thunk);
assert_eq!(try_thunk.evaluate(), Ok(42));Source§impl<A: 'static> From<Thunk<'static, A>> for Trampoline<A>
§Type Parameters
A: The type of the value produced by the computation.
impl<A: 'static> From<Thunk<'static, A>> for Trampoline<A>
§Type Parameters
A: The type of the value produced by the computation.
Source§fn from(thunk: Thunk<'static, A>) -> Self
fn from(thunk: Thunk<'static, A>) -> Self
Converts a 'static Thunk into a Trampoline.
This lifts a non-stack-safe Thunk into the stack-safe Trampoline
execution model. The resulting Trampoline evaluates the thunk when run.
§Type Signature
forall A. Thunk A -> Trampoline A
§Parameters
thunk: The thunk to convert.
§Returns
A trampoline that evaluates the thunk.
§Examples
use fp_library::types::*;
let thunk = Thunk::new(|| 42);
let trampoline = Trampoline::from(thunk);
assert_eq!(trampoline.evaluate(), 42);Source§impl<A: 'static> From<Trampoline<A>> for Thunk<'static, A>
§Type Parameters
A: The type of the value produced by the computation.
impl<A: 'static> From<Trampoline<A>> for Thunk<'static, A>
§Type Parameters
A: The type of the value produced by the computation.
Source§fn from(trampoline: Trampoline<A>) -> Self
fn from(trampoline: Trampoline<A>) -> Self
Source§impl<'a, A: Monoid + 'a> Monoid for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the value produced by the computation.
impl<'a, A: Monoid + 'a> Monoid for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.A: The type of the value produced by the computation.
Source§impl<'a, A: Semigroup + 'a> Semigroup for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.
A: The type of the value produced by the computation.
impl<'a, A: Semigroup + 'a> Semigroup for Thunk<'a, A>
§Type Parameters
'a: The lifetime of the computation.A: The type of the value produced by the computation.
Source§fn append(a: Self, b: Self) -> Self
fn append(a: Self, b: Self) -> Self
Combines two Thunks by combining their results.
§Type Signature
forall A. Semigroup A => (Thunk A, Thunk A) -> Thunk A
§Parameters
a: The firstThunk.b: The secondThunk.
§Returns
A new Thunk containing the combined result.
§Examples
use fp_library::{
brands::*,
classes::*,
functions::*,
};
let t1 = pure::<ThunkBrand, _>("Hello".to_string());
let t2 = pure::<ThunkBrand, _>(" World".to_string());
let t3 = append::<_>(t1, t2);
assert_eq!(t3.evaluate(), "Hello World");Auto Trait Implementations§
impl<'a, A> Freeze for Thunk<'a, A>
impl<'a, A> !RefUnwindSafe for Thunk<'a, A>
impl<'a, A> !Send for Thunk<'a, A>
impl<'a, A> !Sync for Thunk<'a, A>
impl<'a, A> Unpin for Thunk<'a, A>
impl<'a, A> UnsafeUnpin for Thunk<'a, A>
impl<'a, A> !UnwindSafe for Thunk<'a, A>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more