Skip to main content

Thunk

Struct Thunk 

Source
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

AspectThunk<'a, A>Trampoline<A>
HKT compatibleYesNo (requires 'static)
Stack-safePartial (tail_rec_m only)Yes (unlimited)
Lifetime'a (can borrow)'static only
Thread safetyNot SendNot Send (A: 'static)
Use caseGlue code, compositionDeep 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:

§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.
Source

pub fn new(f: impl FnOnce() -> A + 'a) -> Self

Creates a new Thunk from a thunk.

§Type Signature

forall A. (() -> A) -> Thunk A

§Parameters
  • f: The thunk to wrap.
§Returns

A new Thunk instance.

§Examples
use fp_library::types::*;

let thunk = Thunk::new(|| 42);
assert_eq!(thunk.evaluate(), 42);
Source

pub fn pure(a: A) -> Self

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);
Source

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 a Thunk.
§Returns

A new Thunk instance.

§Examples
use fp_library::{
	brands::*,
	functions::*,
	types::*,
};

let thunk = Thunk::defer(|| pure::<ThunkBrand, _>(42));
assert_eq!(thunk.evaluate(), 42);
Source

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);
Source

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);
Source

pub fn evaluate(self) -> A

Forces evaluation and returns the result.

§Type Signature

forall A. Thunk A -> A

§Returns

The result of the computation.

§Examples
use fp_library::{
	brands::*,
	functions::*,
};

let thunk = pure::<ThunkBrand, _>(42);
assert_eq!(thunk.evaluate(), 42);
Source

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);
Source

pub fn into_arc_lazy(self) -> Lazy<'a, A, ArcLazyConfig>
where A: Send + Sync + 'a,

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.
Source§

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.
Source§

fn defer(f: impl FnOnce() -> Self + 'a) -> Self
where 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.
Source§

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.
Source§

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.
Source§

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.
Source§

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.
Source§

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.
Source§

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.
Source§

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.
Source§

fn from(trampoline: Trampoline<A>) -> Self

§Type Signature

forall A. crate A -> Thunk A

§Parameters
  • trampoline: The trampoline to convert.
§Returns

A thunk that evaluates the trampoline.

§Examples
use fp_library::types::*;
let task = Trampoline::pure(42);
let thunk = Thunk::from(task);
assert_eq!(thunk.evaluate(), 42);
Source§

impl<'a, A: 'a> InferableBrand_cdc7cd43dac7585f for Thunk<'a, A>

Generated InferableBrand_cdc7cd43dac7585f implementation mapping Thunk < 'a, A > back to ThunkBrand.

Source§

type Brand = ThunkBrand

The canonical brand for this type.
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.
Source§

fn empty() -> Self

Returns the identity Thunk.

§Type Signature

forall A. Monoid A => () -> Thunk A

§Returns

A Thunk producing the identity value of A.

§Examples
use fp_library::{
	classes::*,
	types::*,
};

let t: Thunk<String> = Thunk::empty();
assert_eq!(t.evaluate(), "");
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.
Source§

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 first Thunk.
  • b: The second Thunk.
§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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pipe for T

Source§

fn pipe<B>(self, f: impl FnOnce(Self) -> B) -> B

Pipes self into a function, enabling left-to-right composition. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.