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
| Aspect | SendThunk<'a, A> | Thunk<'a, A> | Trampoline<A> | ArcLazy<'a, A> |
|---|---|---|---|---|
| Thread safety | Send | Not Send | Not Send | Send + Sync |
| HKT compatible | No (needs Send closures) | Yes | No (requires 'static) | Partial (SendRefFunctor) |
| Stack-safe | Partial (tail_rec_m only) | Partial (tail_rec_m only) | Yes (unlimited) | N/A (memoized) |
| Memoized | No | No | No | Yes |
| Lifetime | 'a (can borrow) | 'a (can borrow) | 'static only | 'a (can borrow) |
| Use case | Cross-thread lazy pipelines | Glue code, composition | Deep recursion, pipelines | Shared 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.
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.
Sourcepub fn defer(f: impl FnOnce() -> SendThunk<'a, A> + Send + 'a) -> Self
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 aSendThunk.
§Returns
A new SendThunk instance.
§Examples
use fp_library::types::*;
let thunk = SendThunk::defer(|| SendThunk::pure(42));
assert_eq!(thunk.evaluate(), 42);Sourcepub fn bind<B: 'a>(
self,
f: impl FnOnce(A) -> SendThunk<'a, B> + Send + 'a,
) -> SendThunk<'a, B>
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);Sourcepub fn map<B: 'a>(self, f: impl FnOnce(A) -> B + Send + 'a) -> SendThunk<'a, B>
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);Sourcepub fn tail_rec_m<S>(
f: impl Fn(S) -> SendThunk<'a, ControlFlow<A, S>> + Send + 'a,
initial: S,
) -> Selfwhere
S: Send + 'a,
pub fn tail_rec_m<S>(
f: impl Fn(S) -> SendThunk<'a, ControlFlow<A, S>> + Send + 'a,
initial: S,
) -> Selfwhere
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);Sourcepub fn arc_tail_rec_m<S>(
f: impl Fn(S) -> SendThunk<'a, ControlFlow<A, S>> + Send + Sync + 'a,
initial: S,
) -> Selfwhere
S: Send + 'a,
pub fn arc_tail_rec_m<S>(
f: impl Fn(S) -> SendThunk<'a, ControlFlow<A, S>> + Send + Sync + 'a,
initial: S,
) -> Selfwhere
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);Sourcepub fn into_arc_lazy(self) -> ArcLazy<'a, A>
pub fn into_arc_lazy(self) -> ArcLazy<'a, A>
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.
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
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.
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
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.
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, 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.
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
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.
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> InferableBrand_cdc7cd43dac7585f for SendThunk<'a, A>
Generated InferableBrand_cdc7cd43dac7585f implementation mapping SendThunk < 'a, A > back to SendThunkBrand.
impl<'a, A: 'a> InferableBrand_cdc7cd43dac7585f for SendThunk<'a, A>
Generated InferableBrand_cdc7cd43dac7585f implementation mapping SendThunk < 'a, A > back to SendThunkBrand.
Source§type Brand = SendThunkBrand
type Brand = SendThunkBrand
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.
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§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.
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
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 firstSendThunk.b: The secondSendThunk.
§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.
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) -> Selfwhere
Self: Sized,
fn send_defer(f: impl FnOnce() -> Self + Send + 'a) -> Selfwhere
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> 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