Skip to main content

SendThunk

Struct SendThunk 

Source
pub struct SendThunk<'a, A>(/* private fields */);
Expand description

A thread-safe deferred computation that produces a value of type A.

SendThunk is the Send-capable counterpart of Thunk. It wraps a Box<dyn FnOnce() -> A + Send + 'a>, so it can be transferred across thread boundaries. Like Thunk, it is NOT memoized and does not cache results.

The key advantage over Thunk is that into_arc_lazy can wrap the closure lazily in an ArcLazy without forcing evaluation first, because the inner closure satisfies Send.

§Higher-Kinded Type Representation

The higher-kinded representation of this type constructor is SendThunkBrand, which is fully polymorphic over the result type.

§Trade-offs vs Other Lazy Types

AspectSendThunk<'a, A>Thunk<'a, A>Trampoline<A>ArcLazy<'a, A>
Thread safetySendNot SendNot SendSend + Sync
HKT compatibleNo (needs Send closures)YesNo (requires 'static)Partial (SendRefFunctor)
Stack-safePartial (tail_rec_m only)Partial (tail_rec_m only)Yes (unlimited)N/A (memoized)
MemoizedNoNoNoYes
Lifetime'a (can borrow)'a (can borrow)'static only'a (can borrow)
Use caseCross-thread lazy pipelinesGlue code, compositionDeep recursion, pipelinesShared cached values

§HKT Trait Limitations

Standard HKT traits such as Functor, Pointed, Semimonad, and Semiapplicative cannot be implemented for SendThunkBrand because their signatures do not require Send on the mapping or binding functions. Since SendThunk stores a Box<dyn FnOnce() -> A + Send>, composing it with a non-Send closure would violate the Send invariant.

Use the inherent methods (map, bind) instead, which accept Send closures explicitly.

§Algebraic Properties

SendThunk satisfies the monad laws through its inherent methods, even though it cannot implement the HKT Monad trait (due to the Send bound requirement):

  • pure(a).bind(f) ≡ f(a) (left identity).
  • m.bind(|x| pure(x)) ≡ m (right identity).
  • m.bind(f).bind(g) ≡ m.bind(|x| f(x).bind(g)) (associativity).

§Stack Safety

Like Thunk, SendThunk::bind chains are not stack-safe. Each nested bind adds a frame to the call stack.

§Type Parameters

  • 'a: The lifetime of the computation.
  • A: The type of the value produced by the computation.

Implementations§

Source§

impl<'a, A: 'a> SendThunk<'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 + Send + 'a) -> Self

Creates a new SendThunk from a thread-safe closure.

§Type Signature

forall A. (() -> A) -> SendThunk A

§Parameters
  • f: The thread-safe closure to wrap.
§Returns

A new SendThunk instance.

§Examples
use fp_library::types::*;

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

pub fn pure(a: A) -> Self
where A: Send + 'a,

Returns a pure value (already computed).

§Type Signature

forall A. A -> SendThunk A

§Parameters
  • a: The value to wrap.
§Returns

A new SendThunk instance containing the value.

§Examples
use fp_library::types::*;

let thunk = SendThunk::pure(42);
assert_eq!(thunk.evaluate(), 42);
Source

pub fn defer(f: impl FnOnce() -> SendThunk<'a, A> + Send + 'a) -> Self

Defers a computation that returns a SendThunk.

§Type Signature

forall A. (() -> SendThunk A) -> SendThunk A

§Parameters
  • f: The thunk that returns a SendThunk.
§Returns

A new SendThunk instance.

§Examples
use fp_library::types::*;

let thunk = SendThunk::defer(|| SendThunk::pure(42));
assert_eq!(thunk.evaluate(), 42);
Source

pub fn bind<B: 'a>( self, f: impl FnOnce(A) -> SendThunk<'a, B> + Send + 'a, ) -> SendThunk<'a, B>

Monadic bind: chains computations.

Note: Each bind adds to the call stack. For deep recursion, consider converting to Trampoline.

§Type Signature

forall A B. (SendThunk A, A -> SendThunk B) -> SendThunk B

§Type Parameters
  • B: The type of the result of the new computation.
§Parameters
  • self: The send thunk instance.
  • f: The function to apply to the result of the computation.
§Returns

A new SendThunk instance representing the chained computation.

§Examples
use fp_library::types::*;

let thunk = SendThunk::pure(21).bind(|x| SendThunk::pure(x * 2));
assert_eq!(thunk.evaluate(), 42);
Source

pub fn map<B: 'a>(self, f: impl FnOnce(A) -> B + Send + 'a) -> SendThunk<'a, B>

Functor map: transforms the result.

§Type Signature

forall A B. (SendThunk A, A -> B) -> SendThunk B

§Type Parameters
  • B: The type of the result of the transformation.
§Parameters
  • self: The send thunk instance.
  • f: The function to apply to the result of the computation.
§Returns

A new SendThunk instance with the transformed result.

§Examples
use fp_library::types::*;

let thunk = SendThunk::pure(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. SendThunk A -> A

§Returns

The result of the computation.

§Examples
use fp_library::types::*;

let thunk = SendThunk::pure(42);
assert_eq!(thunk.evaluate(), 42);
Source

pub fn tail_rec_m<S>( f: impl Fn(S) -> SendThunk<'a, ControlFlow<A, S>> + Send + 'a, initial: S, ) -> Self
where S: Send + 'a,

Performs tail-recursive monadic computation.

The step function f is called in a loop, avoiding stack growth. Each iteration evaluates f(state) and inspects the resulting ControlFlow: ControlFlow::Continue(next) continues with next, while ControlFlow::Break(a) breaks out and returns a.

§Step Function

The function f is bounded by Fn, so it is callable multiple times by shared reference. Each iteration of the loop calls f without consuming it, so no Clone bound is needed.

§Type Signature

forall A S. (S -> SendThunk (ControlFlow A S), S) -> SendThunk A

§Type Parameters
  • S: The type of the loop state.
§Parameters
  • f: The step function that produces the next state or the final result.
  • initial: The initial state.
§Returns

A SendThunk that, when evaluated, runs the tail-recursive loop.

§Examples
use {
	core::ops::ControlFlow,
	fp_library::types::*,
};

let result = SendThunk::tail_rec_m(
	|x| {
		SendThunk::pure(
			if x < 1000 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
		)
	},
	0,
);
assert_eq!(result.evaluate(), 1000);
Source

pub fn arc_tail_rec_m<S>( f: impl Fn(S) -> SendThunk<'a, ControlFlow<A, S>> + Send + Sync + 'a, initial: S, ) -> Self
where S: Send + 'a,

Arc-wrapped version of tail_rec_m.

Wraps the closure in Arc internally so it can be shared across thread boundaries. The step function must be Send + Sync (rather than just Send as in tail_rec_m).

§Type Signature

forall A S. (S -> SendThunk (ControlFlow A S), S) -> SendThunk A

§Type Parameters
  • S: The type of the loop state.
§Parameters
  • f: The step function that produces the next state or the final result.
  • initial: The initial state.
§Returns

A SendThunk that, when evaluated, runs the tail-recursive loop.

§Examples
use {
	core::ops::ControlFlow,
	fp_library::types::*,
	std::sync::{
		Arc,
		atomic::{
			AtomicUsize,
			Ordering,
		},
	},
};

let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = Arc::clone(&counter);
let result = SendThunk::arc_tail_rec_m(
	move |x| {
		counter_clone.fetch_add(1, Ordering::SeqCst);
		SendThunk::pure(
			if x < 100 { ControlFlow::Continue(x + 1) } else { ControlFlow::Break(x) },
		)
	},
	0,
);
assert_eq!(result.evaluate(), 100);
assert_eq!(counter.load(Ordering::SeqCst), 101);
Source

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

Converts this SendThunk into a memoized ArcLazy value.

Unlike Thunk::into_arc_lazy, this does not evaluate eagerly. The inner Send closure is passed directly into ArcLazy::new, so evaluation is deferred until the ArcLazy is first accessed.

§Type Signature

forall A. SendThunk A -> ArcLazy A

§Returns

A thread-safe ArcLazy that evaluates this thunk on first access.

§Examples
use fp_library::types::*;

let thunk = SendThunk::new(|| 42);
let lazy = thunk.into_arc_lazy();
assert_eq!(*lazy.evaluate(), 42);

Trait Implementations§

Source§

impl<'a, A> Debug for SendThunk<'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 send thunk without evaluating it.

§Type Signature

forall A. (&SendThunk A, &mut fmt) -> fmt

§Parameters
  • &self: The send thunk to format.
  • f: The formatter.
§Returns

The formatting result.

§Examples
use fp_library::types::*;
let thunk = SendThunk::pure(42);
assert_eq!(format!("{:?}", thunk), "SendThunk(<unevaluated>)");
Source§

impl<'a, A: Send + Sync + 'a> From<SendThunk<'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(thunk: SendThunk<'a, A>) -> Self

Converts a SendThunk into an ArcLazy without eager evaluation.

Because SendThunk already satisfies Send, the inner closure can be passed directly into ArcLazy, deferring computation until first access.

§Type Signature

forall A. SendThunk A -> Lazy A

§Parameters
  • thunk: The send thunk to convert.
§Returns

A new ArcLazy wrapping the deferred computation.

§Examples
use fp_library::{
	classes::*,
	types::*,
};
let thunk = SendThunk::new(|| 42);
let lazy: ArcLazy<i32> = ArcLazy::from(thunk);
assert_eq!(*lazy.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, E: 'a> From<SendThunk<'a, A>> for TrySendThunk<'a, A, E>
where A: Send + 'a,

§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(thunk: SendThunk<'a, A>) -> Self

Converts a SendThunk into a TrySendThunk that always succeeds.

§Type Signature

forall A E. SendThunk A -> TrySendThunk A E

§Parameters
  • thunk: The send thunk to convert.
§Returns

A new TrySendThunk wrapping the send thunk as a success.

§Examples
use fp_library::types::*;
let thunk = SendThunk::pure(42);
let try_thunk: TrySendThunk<i32, ()> = TrySendThunk::from(thunk);
assert_eq!(try_thunk.evaluate(), Ok(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> InferableBrand_cdc7cd43dac7585f<'a, SendThunkBrand, A> for SendThunk<'a, A>

Generated InferableBrand_cdc7cd43dac7585f implementation for SendThunk < 'a, A > with brand SendThunkBrand.

Source§

type Marker = Val

Dispatch marker: Val for owned types, Ref for references.
Source§

impl<'a, A: Monoid + Send + 'a> Monoid for SendThunk<'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 SendThunk.

§Type Signature

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

§Returns

A SendThunk producing the identity value of A.

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

let t: SendThunk<String> = SendThunk::empty();
assert_eq!(t.evaluate(), "");
Source§

impl<'a, A: Semigroup + Send + 'a> Semigroup for SendThunk<'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 SendThunks by combining their results.

§Type Signature

forall A. Semigroup A => (SendThunk A, SendThunk A) -> SendThunk A

§Parameters
  • a: The first SendThunk.
  • b: The second SendThunk.
§Returns

A new SendThunk containing the combined result.

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

let t1 = SendThunk::pure("Hello".to_string());
let t2 = SendThunk::pure(" World".to_string());
let t3 = append::<_>(t1, t2);
assert_eq!(t3.evaluate(), "Hello World");
Source§

impl<'a, A: Send + 'a> SendDeferrable<'a> for SendThunk<'a, A>

§Type Parameters
  • 'a: The lifetime of the computation.
  • A: The type of the value produced by the computation.
Source§

fn send_defer(f: impl FnOnce() -> Self + Send + 'a) -> Self
where Self: Sized,

Creates a SendThunk from a thread-safe computation that produces it.

§Type Signature

forall A. (() -> SendThunk A) -> SendThunk A

§Parameters
  • f: A thread-safe thunk that produces the send thunk.
§Returns

The deferred send thunk.

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

let task: SendThunk<i32> = SendDeferrable::send_defer(|| SendThunk::pure(42));
assert_eq!(task.evaluate(), 42);

Auto Trait Implementations§

§

impl<'a, A> Freeze for SendThunk<'a, A>

§

impl<'a, A> !RefUnwindSafe for SendThunk<'a, A>

§

impl<'a, A> Send for SendThunk<'a, A>

§

impl<'a, A> !Sync for SendThunk<'a, A>

§

impl<'a, A> Unpin for SendThunk<'a, A>

§

impl<'a, A> UnsafeUnpin for SendThunk<'a, A>

§

impl<'a, A> !UnwindSafe for SendThunk<'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.