#[fp_macros::document_module]
mod inner {
use {
crate::{
Apply,
brands::{
ArcBrand,
LazyBrand,
RcBrand,
},
classes::{
CloneFn,
Deferrable,
LiftFn,
Monoid,
RefFoldable,
RefFoldableWithIndex,
RefFunctor,
RefFunctorWithIndex,
RefLift,
RefPointed,
RefSemiapplicative,
RefSemimonad,
Semigroup,
SendCloneFn,
SendDeferrable,
SendLiftFn,
SendRefFoldable,
SendRefFoldableWithIndex,
SendRefFunctor,
SendRefFunctorWithIndex,
SendRefLift,
SendRefPointed,
SendRefSemiapplicative,
SendRefSemimonad,
WithIndex,
},
dispatch::Ref,
impl_kind,
kinds::*,
types::{
SendThunk,
Thunk,
Trampoline,
},
},
fp_macros::*,
std::{
cell::LazyCell,
fmt,
hash::{
Hash,
Hasher,
},
rc::Rc,
sync::{
Arc,
LazyLock,
},
},
};
pub use crate::classes::LazyConfig;
pub struct RcLazyConfig;
impl LazyConfig for RcLazyConfig {
type Lazy<'a, A: 'a> = Rc<LazyCell<A, Box<dyn FnOnce() -> A + 'a>>>;
type PointerBrand = RcBrand;
type Thunk<'a, A: 'a> = dyn FnOnce() -> A + 'a;
#[document_signature]
#[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
#[document_parameters("The initializer thunk.")]
#[document_returns("A new lazy cell.")]
#[document_examples]
fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A> {
Rc::new(LazyCell::new(f))
}
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The borrow lifetime.",
"The type of the value."
)]
#[document_parameters("The lazy cell to evaluate.")]
#[document_returns("A reference to the value.")]
#[document_examples]
fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A {
LazyCell::force(lazy)
}
}
pub struct ArcLazyConfig;
impl LazyConfig for ArcLazyConfig {
type Lazy<'a, A: 'a> = Arc<LazyLock<A, Box<dyn FnOnce() -> A + Send + 'a>>>;
type PointerBrand = ArcBrand;
type Thunk<'a, A: 'a> = dyn FnOnce() -> A + Send + 'a;
#[document_signature]
#[document_type_parameters("The lifetime of the computation.", "The type of the value.")]
#[document_parameters("The initializer thunk.")]
#[document_returns("A new lazy cell.")]
#[document_examples]
fn lazy_new<'a, A: 'a>(f: Box<Self::Thunk<'a, A>>) -> Self::Lazy<'a, A> {
Arc::new(LazyLock::new(f))
}
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The borrow lifetime.",
"The type of the value."
)]
#[document_parameters("The lazy cell to evaluate.")]
#[document_returns("A reference to the value.")]
#[document_examples]
fn evaluate<'a, 'b, A: 'a>(lazy: &'b Self::Lazy<'a, A>) -> &'b A {
LazyLock::force(lazy)
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration (determines Rc vs Arc)."
)]
pub struct Lazy<'a, A, Config: LazyConfig = RcLazyConfig>(
pub(crate) Config::Lazy<'a, A>,
)
where
A: 'a;
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration (determines Rc vs Arc)."
)]
#[document_parameters("The instance to clone.")]
impl<'a, A, Config: LazyConfig> Clone for Lazy<'a, A, Config>
where
A: 'a,
{
#[document_signature]
#[document_returns("A new `Lazy` instance that shares the same underlying memoized value.")]
#[document_examples]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration (determines Rc vs Arc)."
)]
#[document_parameters("The lazy instance.")]
impl<'a, A, Config: LazyConfig> Lazy<'a, A, Config>
where
A: 'a,
{
#[document_signature]
#[document_returns("A reference to the memoized value.")]
#[document_examples]
#[inline]
pub fn evaluate(&self) -> &A {
Config::evaluate(&self.0)
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
#[document_parameters("The lazy instance.")]
impl<'a, A> Lazy<'a, A, RcLazyConfig>
where
A: 'a,
{
#[document_signature]
#[document_parameters("The closure that produces the value.")]
#[document_returns("A new `Lazy` instance.")]
#[inline]
#[document_examples]
pub fn new(f: impl FnOnce() -> A + 'a) -> Self {
Lazy(RcLazyConfig::lazy_new(Box::new(f)))
}
#[document_signature]
#[document_parameters("The pre-computed value to wrap.")]
#[document_returns("A new `Lazy` instance containing the value.")]
#[inline]
#[document_examples]
pub fn pure(a: A) -> Self {
Lazy(RcLazyConfig::lazy_new(Box::new(move || a)))
}
#[document_signature]
#[document_returns("An owned clone of the memoized value.")]
#[document_examples]
#[inline]
pub fn evaluate_owned(&self) -> A
where
A: Clone, {
self.evaluate().clone()
}
#[document_signature]
#[document_type_parameters("The type of the result.")]
#[document_parameters("The function to apply to the memoized value.")]
#[document_returns("A new `Lazy` instance containing the mapped value.")]
#[document_examples]
#[inline]
pub fn ref_map<B: 'a>(
&self,
f: impl Fn(&A) -> B + 'a,
) -> Lazy<'a, B, RcLazyConfig> {
let this = self.clone();
RcLazy::new(move || f(this.evaluate()))
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A> From<Thunk<'a, A>> for Lazy<'a, A, RcLazyConfig> {
#[document_signature]
#[document_parameters("The thunk to convert.")]
#[document_returns("A new `Lazy` instance that will evaluate the thunk on first access.")]
#[document_examples]
fn from(eval: Thunk<'a, A>) -> Self {
Self::new(move || eval.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: 'static> From<Trampoline<A>> for Lazy<'a, A, RcLazyConfig> {
#[document_signature]
#[document_parameters("The trampoline to convert.")]
#[document_returns(
"A new `Lazy` instance that will evaluate the trampoline on first access."
)]
#[document_examples]
fn from(task: Trampoline<A>) -> Self {
Self::new(move || task.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Send + Sync + 'a> From<Thunk<'a, A>> for Lazy<'a, A, ArcLazyConfig> {
#[document_signature]
#[document_parameters("The thunk to convert.")]
#[document_returns("A new `Lazy` instance containing the eagerly evaluated value.")]
#[document_examples]
fn from(eval: Thunk<'a, A>) -> Self {
Self::pure(eval.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Send + Sync + 'static> From<Trampoline<A>> for Lazy<'a, A, ArcLazyConfig> {
#[document_signature]
#[document_parameters("The trampoline to convert.")]
#[document_returns("A new `Lazy` instance containing the eagerly evaluated value.")]
#[document_examples]
fn from(task: Trampoline<A>) -> Self {
Self::pure(task.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Send + Sync + 'a> From<SendThunk<'a, A>> for Lazy<'a, A, ArcLazyConfig> {
#[document_signature]
#[document_parameters("The send thunk to convert.")]
#[document_returns("A new `ArcLazy` wrapping the deferred computation.")]
#[document_examples]
fn from(thunk: SendThunk<'a, A>) -> Self {
Self::new(move || thunk.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Clone + Send + Sync + 'a> From<Lazy<'a, A, RcLazyConfig>>
for Lazy<'a, A, ArcLazyConfig>
{
#[document_signature]
#[document_parameters("The `RcLazy` instance to convert.")]
#[document_returns(
"A new `ArcLazy` instance containing a clone of the eagerly evaluated value."
)]
#[document_examples]
fn from(source: Lazy<'a, A, RcLazyConfig>) -> Self {
Self::pure(source.evaluate().clone())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Clone + 'a> From<Lazy<'a, A, ArcLazyConfig>> for Lazy<'a, A, RcLazyConfig> {
#[document_signature]
#[document_parameters("The `ArcLazy` instance to convert.")]
#[document_returns(
"A new `RcLazy` instance containing a clone of the eagerly evaluated value."
)]
#[document_examples]
fn from(source: Lazy<'a, A, ArcLazyConfig>) -> Self {
Self::pure(source.evaluate().clone())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
#[document_parameters("The lazy instance.")]
impl<'a, A> Lazy<'a, A, ArcLazyConfig>
where
A: Send + Sync + 'a,
{
#[document_signature]
#[document_parameters("The closure that produces the value.")]
#[document_returns("A new `Lazy` instance.")]
#[inline]
#[document_examples]
pub fn new(f: impl FnOnce() -> A + Send + 'a) -> Self {
Lazy(ArcLazyConfig::lazy_new(Box::new(f)))
}
#[document_signature]
#[document_parameters("The pre-computed value to wrap.")]
#[document_returns("A new `Lazy` instance containing the value.")]
#[inline]
#[document_examples]
pub fn pure(a: A) -> Self {
Lazy(ArcLazyConfig::lazy_new(Box::new(move || a)))
}
#[document_signature]
#[document_returns("An owned clone of the memoized value.")]
#[document_examples]
#[inline]
pub fn evaluate_owned(&self) -> A
where
A: Clone, {
self.evaluate().clone()
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
#[document_parameters("The lazy value to map over.")]
impl<'a, A: Send + Sync + 'a> Lazy<'a, A, ArcLazyConfig> {
#[document_signature]
#[document_type_parameters("The type of the result.")]
#[document_parameters("The function to apply to the memoized value.")]
#[document_returns("A new `ArcLazy` instance containing the mapped value.")]
#[document_examples]
#[inline]
pub fn ref_map<B: Send + Sync + 'a>(
&self,
f: impl Fn(&A) -> B + Send + 'a,
) -> Lazy<'a, B, ArcLazyConfig> {
let this = self.clone();
ArcLazy::new(move || f(this.evaluate()))
}
}
pub type RcLazy<'a, A> = Lazy<'a, A, RcLazyConfig>;
pub type ArcLazy<'a, A> = Lazy<'a, A, ArcLazyConfig>;
impl_kind! {
impl<Config: LazyConfig> for LazyBrand<Config> {
type Of<'a, A: 'a>: 'a = Lazy<'a, A, Config>;
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A> Deferrable<'a> for Lazy<'a, A, RcLazyConfig>
where
A: Clone + 'a,
{
#[document_signature]
#[document_parameters("The thunk that produces the lazy value.")]
#[document_returns("A new `Lazy` value.")]
#[document_examples]
fn defer(f: impl FnOnce() -> Self + 'a) -> Self
where
Self: Sized, {
RcLazy::new(move || f().evaluate().clone())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A> SendDeferrable<'a> for Lazy<'a, A, ArcLazyConfig>
where
A: Clone + Send + Sync + 'a,
{
#[document_signature]
#[document_parameters("The thunk that produces the lazy value.")]
#[document_returns("A new `ArcLazy` value.")]
#[document_examples]
fn send_defer(f: impl FnOnce() -> Self + Send + 'a) -> Self
where
Self: Sized, {
ArcLazy::new(move || f().evaluate().clone())
}
}
impl RefFunctor for LazyBrand<RcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The type of the value.",
"The type of the result."
)]
#[document_parameters("The function to apply.", "The memoized value.")]
#[document_returns("A new memoized value.")]
#[document_examples]
fn ref_map<'a, A: 'a, B: 'a>(
f: impl Fn(&A) -> B + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
fa.ref_map(f)
}
}
impl SendRefFunctor for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The type of the value.",
"The type of the result."
)]
#[document_parameters("The function to apply.", "The memoized value.")]
#[document_returns("A new memoized value.")]
#[document_examples]
fn send_ref_map<'a, A: Send + Sync + 'a, B: Send + Sync + 'a>(
f: impl Fn(&A) -> B + Send + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
fa.ref_map(f)
}
}
impl SendRefPointed for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters("The lifetime of the value.", "The type of the value.")]
#[document_parameters("A reference to the value to wrap.")]
#[document_returns("A new thread-safe memoized value containing a clone of the input.")]
#[document_examples]
fn send_ref_pure<'a, A: Clone + Send + Sync + 'a>(
a: &A
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
let cloned = a.clone();
ArcLazy::new(move || cloned)
}
}
impl SendRefLift for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The type of the first value.",
"The type of the second value.",
"The type of the result."
)]
#[document_parameters(
"The function to lift.",
"The first memoized value.",
"The second memoized value."
)]
#[document_returns("A new thread-safe memoized value containing the result.")]
#[document_examples]
fn send_ref_lift2<'a, A: Send + Sync + 'a, B: Send + Sync + 'a, C: Send + Sync + 'a>(
func: impl Fn(&A, &B) -> C + Send + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
fb: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) {
let fa = fa.clone();
let fb = fb.clone();
ArcLazy::new(move || func(fa.evaluate(), fb.evaluate()))
}
}
impl SendRefSemiapplicative for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the thread-safe cloneable function wrapper.",
"The type of the input value.",
"The type of the output value."
)]
#[document_parameters("The memoized wrapped by-ref function.", "The memoized value.")]
#[document_returns("A new thread-safe memoized value containing the result.")]
#[document_examples]
fn send_ref_apply<
'a,
FnBrand: 'a + SendCloneFn<Ref>,
A: Send + Sync + 'a,
B: Send + Sync + 'a,
>(
ff: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as SendCloneFn<Ref>>::Of<'a, A, B>>),
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
let ff = ff.clone();
let fa = fa.clone();
ArcLazy::new(move || {
let f = ff.evaluate();
let a = fa.evaluate();
(**f)(a)
})
}
}
impl SendRefSemimonad for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The type of the value inside the context.",
"The type of the value in the resulting context."
)]
#[document_parameters(
"The memoized value.",
"A thread-safe function that receives a reference and returns a new memoized value."
)]
#[document_returns("A new thread-safe memoized value produced by the function.")]
#[document_examples]
fn send_ref_bind<'a, A: Send + Sync + 'a, B: Send + Sync + 'a>(
ma: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
f: impl Fn(&A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + Send + 'a,
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
f(ma.evaluate())
}
}
impl SendRefFoldable for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The brand of the cloneable function to use.",
"The type of the computed value.",
"The monoid type."
)]
#[document_parameters("The mapping function.", "The Lazy to fold.")]
#[document_returns("The monoid value.")]
#[document_examples]
fn send_ref_fold_map<'a, FnBrand, A: Send + Sync + 'a + Clone, M>(
func: impl Fn(&A) -> M + Send + Sync + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> M
where
FnBrand: SendLiftFn + 'a,
M: Monoid + Send + Sync + 'a, {
func(fa.evaluate())
}
}
impl SendRefFoldableWithIndex for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The brand of the cloneable function to use.",
"The type of the computed value.",
"The monoid type."
)]
#[document_parameters("The function to apply.", "The Lazy to fold.")]
#[document_returns("The monoid value.")]
#[document_examples]
fn send_ref_fold_map_with_index<
'a,
FnBrand,
A: Send + Sync + 'a + Clone,
R: Monoid + Send + Sync + 'a,
>(
f: impl Fn((), &A) -> R + Send + Sync + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> R
where
FnBrand: SendLiftFn + 'a, {
f((), fa.evaluate())
}
}
impl SendRefFunctorWithIndex for LazyBrand<ArcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the input value.",
"The type of the output value."
)]
#[document_parameters("The function to apply.", "The Lazy to map over.")]
#[document_returns("A new Lazy containing the mapped value.")]
#[document_examples]
fn send_ref_map_with_index<'a, A: Send + Sync + 'a, B: Send + Sync + 'a>(
f: impl Fn((), &A) -> B + Send + Sync + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
Self::send_ref_map(move |a| f((), a), fa)
}
}
impl RefPointed for LazyBrand<RcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the value.",
"The type of the value. Must be `Clone`."
)]
#[document_parameters("A reference to the value to wrap.")]
#[document_returns("A new memoized value containing a clone of the input.")]
#[document_examples]
fn ref_pure<'a, A: Clone + 'a>(
a: &A
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
let cloned = a.clone();
RcLazy::new(move || cloned)
}
}
impl RefLift for LazyBrand<RcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The type of the first value.",
"The type of the second value.",
"The type of the result."
)]
#[document_parameters(
"The function to lift.",
"The first memoized value.",
"The second memoized value."
)]
#[document_returns("A new memoized value containing the result.")]
#[document_examples]
fn ref_lift2<'a, A: 'a, B: 'a, C: 'a>(
func: impl Fn(&A, &B) -> C + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
fb: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) {
let fa = fa.clone();
let fb = fb.clone();
RcLazy::new(move || func(fa.evaluate(), fb.evaluate()))
}
}
impl RefSemiapplicative for LazyBrand<RcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the cloneable function wrapper.",
"The type of the input value.",
"The type of the output value."
)]
#[document_parameters("The memoized wrapped by-ref function.", "The memoized value.")]
#[document_returns("A new memoized value containing the result of applying the function.")]
#[document_examples]
fn ref_apply<'a, FnBrand: 'a + CloneFn<Ref>, A: 'a, B: 'a>(
ff: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn<Ref>>::Of<'a, A, B>>),
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
let ff = ff.clone();
let fa = fa.clone();
RcLazy::new(move || {
let f = ff.evaluate();
let a = fa.evaluate();
(**f)(a)
})
}
}
impl RefSemimonad for LazyBrand<RcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The type of the value inside the context.",
"The type of the value in the resulting context."
)]
#[document_parameters(
"The memoized value.",
"A function that receives a reference to the value and returns a new memoized value."
)]
#[document_returns("A new memoized value produced by the function.")]
#[document_examples]
fn ref_bind<'a, A: 'a, B: 'a>(
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
f: impl Fn(&A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
f(fa.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the reference.",
"The type of the computed value.",
"The memoization configuration."
)]
#[document_parameters("The lazy value to display.")]
impl<'a, A: fmt::Display + 'a, Config: LazyConfig> fmt::Display for Lazy<'a, A, Config> {
#[document_signature]
#[document_parameters("The formatter.")]
#[document_returns("The formatting result.")]
#[document_examples]
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Display::fmt(self.evaluate(), f)
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Semigroup + Clone + 'a> Semigroup for Lazy<'a, A, RcLazyConfig> {
#[document_signature]
#[document_parameters("The first lazy value.", "The second lazy value.")]
#[document_returns("A new `RcLazy` containing the combined result.")]
#[document_examples]
fn append(
a: Self,
b: Self,
) -> Self {
RcLazy::new(move || Semigroup::append(a.evaluate().clone(), b.evaluate().clone()))
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Semigroup + Clone + Send + Sync + 'a> Semigroup for Lazy<'a, A, ArcLazyConfig> {
#[document_signature]
#[document_parameters("The first lazy value.", "The second lazy value.")]
#[document_returns("A new `ArcLazy` containing the combined result.")]
#[document_examples]
fn append(
a: Self,
b: Self,
) -> Self {
ArcLazy::new(move || Semigroup::append(a.evaluate().clone(), b.evaluate().clone()))
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Monoid + Clone + 'a> Monoid for Lazy<'a, A, RcLazyConfig> {
#[document_signature]
#[document_returns("An `RcLazy` producing the identity value of `A`.")]
#[document_examples]
fn empty() -> Self {
RcLazy::new(|| Monoid::empty())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
impl<'a, A: Monoid + Clone + Send + Sync + 'a> Monoid for Lazy<'a, A, ArcLazyConfig> {
#[document_signature]
#[document_returns("An `ArcLazy` producing the identity value of `A`.")]
#[document_examples]
fn empty() -> Self {
ArcLazy::new(|| Monoid::empty())
}
}
#[document_type_parameters(
"The lifetime of the reference.",
"The type of the computed value.",
"The memoization configuration."
)]
#[document_parameters("The lazy value to hash.")]
impl<'a, A: Hash + 'a, Config: LazyConfig> Hash for Lazy<'a, A, Config> {
#[document_signature]
#[document_type_parameters("The type of the hasher.")]
#[document_parameters("The hasher state.")]
#[document_examples]
fn hash<H: Hasher>(
&self,
state: &mut H,
) {
self.evaluate().hash(state)
}
}
#[document_type_parameters("The memoization configuration (determines Rc vs Arc).")]
impl<Config: LazyConfig> RefFoldable for LazyBrand<Config> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The brand of the cloneable function to use.",
"The type of the elements in the structure.",
"The type of the monoid."
)]
#[document_parameters("The mapping function.", "The Lazy to fold.")]
#[document_returns("The monoid value.")]
#[document_examples]
fn ref_fold_map<'a, FnBrand, A: 'a + Clone, M>(
func: impl Fn(&A) -> M + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> M
where
FnBrand: LiftFn + 'a,
M: Monoid + 'a, {
func(fa.evaluate())
}
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The brand of the cloneable function to use.",
"The type of the elements in the structure.",
"The type of the accumulator."
)]
#[document_parameters(
"The function to apply to each element reference and the accumulator.",
"The initial value of the accumulator.",
"The `Lazy` to fold."
)]
#[document_returns("The final accumulator value.")]
#[document_examples]
fn ref_fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
func: impl Fn(&A, B) -> B + 'a,
initial: B,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> B
where
FnBrand: LiftFn + 'a, {
func(fa.evaluate(), initial)
}
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The brand of the cloneable function to use.",
"The type of the elements in the structure.",
"The type of the accumulator."
)]
#[document_parameters(
"The function to apply to the accumulator and each element reference.",
"The initial value of the accumulator.",
"The `Lazy` to fold."
)]
#[document_returns("The final accumulator value.")]
#[document_examples]
fn ref_fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
func: impl Fn(B, &A) -> B + 'a,
initial: B,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> B
where
FnBrand: LiftFn + 'a, {
func(initial, fa.evaluate())
}
}
#[document_type_parameters("The memoization configuration (determines Rc vs Arc).")]
impl<Config: LazyConfig> WithIndex for LazyBrand<Config> {
type Index = ();
}
#[document_type_parameters("The memoization configuration (determines Rc vs Arc).")]
impl<Config: LazyConfig> RefFoldableWithIndex for LazyBrand<Config> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The brand of the cloneable function to use.",
"The type of the computed value.",
"The monoid type."
)]
#[document_parameters(
"The function to apply to the index and value reference.",
"The Lazy to fold."
)]
#[document_returns("The monoid value.")]
#[document_examples]
fn ref_fold_map_with_index<'a, FnBrand, A: 'a + Clone, R: Monoid + 'a>(
f: impl Fn((), &A) -> R + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> R
where
FnBrand: LiftFn + 'a, {
f((), fa.evaluate())
}
}
impl RefFunctorWithIndex for LazyBrand<RcLazyConfig> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the input value.",
"The type of the output value."
)]
#[document_parameters(
"The function to apply to the index and value reference.",
"The Lazy to map over."
)]
#[document_returns("A new Lazy containing the mapped value.")]
#[document_examples]
fn ref_map_with_index<'a, A: 'a, B: 'a>(
f: impl Fn((), &A) -> B + 'a,
fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
Self::ref_map(move |a| f((), a), fa)
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration."
)]
#[document_parameters("The lazy value to compare.")]
impl<'a, A: PartialEq + 'a, Config: LazyConfig> PartialEq for Lazy<'a, A, Config> {
#[document_signature]
#[document_parameters("The other lazy value to compare with.")]
#[document_returns("`true` if the evaluated values are equal.")]
#[document_examples]
fn eq(
&self,
other: &Self,
) -> bool {
self.evaluate() == other.evaluate()
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration."
)]
#[document_parameters("The lazy value to compare.")]
impl<'a, A: PartialOrd + 'a, Config: LazyConfig> PartialOrd for Lazy<'a, A, Config> {
#[document_signature]
#[document_parameters("The other lazy value to compare with.")]
#[document_returns(
"The ordering between the evaluated values, or `None` if not comparable."
)]
#[document_examples]
fn partial_cmp(
&self,
other: &Self,
) -> Option<std::cmp::Ordering> {
self.evaluate().partial_cmp(other.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration."
)]
impl<'a, A: Eq + 'a, Config: LazyConfig> Eq for Lazy<'a, A, Config> {}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration."
)]
#[document_parameters("The lazy value to compare.")]
impl<'a, A: Ord + 'a, Config: LazyConfig> Ord for Lazy<'a, A, Config> {
#[document_signature]
#[document_parameters("The other lazy value to compare with.")]
#[document_returns("The ordering between the evaluated values.")]
#[document_examples]
fn cmp(
&self,
other: &Self,
) -> std::cmp::Ordering {
self.evaluate().cmp(other.evaluate())
}
}
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value.",
"The memoization configuration."
)]
#[document_parameters("The lazy value to format.")]
impl<'a, A, Config: LazyConfig> fmt::Debug for Lazy<'a, A, Config>
where
A: 'a,
{
#[document_signature]
#[document_parameters("The formatter.")]
#[document_returns("The formatting result.")]
#[document_examples]
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
f.write_str("Lazy(..)")
}
}
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
#[document_parameters(
"The function that receives a lazy self-reference and produces the value."
)]
#[document_returns("A new `RcLazy` instance.")]
#[document_examples]
pub fn rc_lazy_fix<'a, A: Clone + 'a>(
f: impl FnOnce(RcLazy<'a, A>) -> A + 'a
) -> RcLazy<'a, A> {
use std::{
cell::OnceCell,
rc::Weak,
};
#[expect(
clippy::type_complexity,
reason = "Nested smart pointers are inherent to the fix-point construction"
)]
let cell: Rc<OnceCell<Weak<LazyCell<A, Box<dyn FnOnce() -> A + 'a>>>>> = Rc::new(OnceCell::new());
let cell_clone = cell.clone();
let lazy = RcLazy::new(move || {
#[expect(
clippy::expect_used,
reason = "Invariant: cell set immediately after closure creation, Weak upgrade always succeeds"
)]
let weak = cell_clone.get().expect("rc_lazy_fix: cell not initialized");
#[expect(
clippy::expect_used,
reason = "Invariant: cell set immediately after closure creation, Weak upgrade always succeeds"
)]
let self_ref = Lazy(weak.upgrade().expect("rc_lazy_fix: outer lazy was dropped"));
f(self_ref)
});
let _ = cell.set(Rc::downgrade(&lazy.0));
lazy
}
#[document_signature]
#[document_type_parameters(
"The lifetime of the computation.",
"The type of the computed value."
)]
#[document_parameters(
"The function that receives a lazy self-reference and produces the value."
)]
#[document_returns("A new `ArcLazy` instance.")]
#[document_examples]
pub fn arc_lazy_fix<'a, A: Clone + Send + Sync + 'a>(
f: impl FnOnce(ArcLazy<'a, A>) -> A + Send + 'a
) -> ArcLazy<'a, A> {
use std::sync::{
OnceLock,
Weak,
};
#[expect(
clippy::type_complexity,
reason = "Nested smart pointers are inherent to the fix-point construction"
)]
let cell: Arc<OnceLock<Weak<LazyLock<A, Box<dyn FnOnce() -> A + Send + 'a>>>>> =
Arc::new(OnceLock::new());
let cell_clone = cell.clone();
let lazy = ArcLazy::new(move || {
#[expect(
clippy::expect_used,
reason = "Invariant: cell set immediately after closure creation, Weak upgrade always succeeds"
)]
let weak = cell_clone.get().expect("arc_lazy_fix: cell not initialized");
#[expect(
clippy::expect_used,
reason = "Invariant: cell set immediately after closure creation, Weak upgrade always succeeds"
)]
let self_ref = Lazy(weak.upgrade().expect("arc_lazy_fix: outer lazy was dropped"));
f(self_ref)
});
let _ = cell.set(Arc::downgrade(&lazy.0));
lazy
}
}
pub use inner::*;
#[cfg(test)]
#[expect(
clippy::unwrap_used,
clippy::panic,
reason = "Tests use panicking operations for brevity and clarity"
)]
mod tests {
use {
super::inner::*,
crate::types::{
Thunk,
Trampoline,
},
quickcheck_macros::quickcheck,
std::{
cell::RefCell,
rc::Rc,
sync::Arc,
},
};
#[test]
fn test_memo_caching() {
let counter = Rc::new(RefCell::new(0));
let counter_clone = counter.clone();
let memo = RcLazy::new(move || {
*counter_clone.borrow_mut() += 1;
42
});
assert_eq!(*counter.borrow(), 0);
assert_eq!(*memo.evaluate(), 42);
assert_eq!(*counter.borrow(), 1);
assert_eq!(*memo.evaluate(), 42);
assert_eq!(*counter.borrow(), 1);
}
#[test]
fn test_memo_sharing() {
let counter = Rc::new(RefCell::new(0));
let counter_clone = counter.clone();
let memo = RcLazy::new(move || {
*counter_clone.borrow_mut() += 1;
42
});
let shared = memo.clone();
assert_eq!(*memo.evaluate(), 42);
assert_eq!(*counter.borrow(), 1);
assert_eq!(*shared.evaluate(), 42);
assert_eq!(*counter.borrow(), 1);
}
#[test]
fn test_arc_memo_thread_safety() {
use std::{
sync::atomic::{
AtomicUsize,
Ordering,
},
thread,
};
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
let memo = ArcLazy::new(move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
42
});
let mut handles = vec![];
for _ in 0 .. 10 {
let memo_clone = memo.clone();
handles.push(thread::spawn(move || {
assert_eq!(*memo_clone.evaluate(), 42);
}));
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_memo_from_eval() {
let eval = Thunk::new(|| 42);
let memo = RcLazy::from(eval);
assert_eq!(*memo.evaluate(), 42);
}
#[test]
fn test_memo_from_task() {
let task = Trampoline::pure(42);
let memo = RcLazy::from(task);
assert_eq!(*memo.evaluate(), 42);
}
#[test]
fn test_rc_lazy_to_arc_lazy() {
let rc = RcLazy::new(|| "hello".to_string());
let arc: ArcLazy<String> = ArcLazy::from(rc);
assert_eq!(*arc.evaluate(), "hello");
}
#[test]
fn test_arc_lazy_to_rc_lazy() {
let arc = ArcLazy::new(|| "world".to_string());
let rc: RcLazy<String> = RcLazy::from(arc);
assert_eq!(*rc.evaluate(), "world");
}
#[test]
fn test_rc_to_arc_eager_evaluation() {
let counter = Rc::new(RefCell::new(0));
let counter_clone = counter.clone();
let rc = RcLazy::new(move || {
*counter_clone.borrow_mut() += 1;
99
});
assert_eq!(*counter.borrow(), 0);
let arc: ArcLazy<i32> = ArcLazy::from(rc);
assert_eq!(*counter.borrow(), 1);
assert_eq!(*arc.evaluate(), 99);
assert_eq!(*counter.borrow(), 1);
}
#[test]
fn test_arc_to_rc_eager_evaluation() {
use std::sync::atomic::{
AtomicUsize,
Ordering,
};
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
let arc = ArcLazy::new(move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
77
});
assert_eq!(counter.load(Ordering::SeqCst), 0);
let rc: RcLazy<i32> = RcLazy::from(arc);
assert_eq!(counter.load(Ordering::SeqCst), 1);
assert_eq!(*rc.evaluate(), 77);
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[quickcheck]
fn prop_rc_to_arc_preserves_value(x: i32) -> bool {
let rc = RcLazy::new(move || x);
let arc: ArcLazy<i32> = ArcLazy::from(rc);
*arc.evaluate() == x
}
#[quickcheck]
fn prop_arc_to_rc_preserves_value(x: i32) -> bool {
let arc = ArcLazy::new(move || x);
let rc: RcLazy<i32> = RcLazy::from(arc);
*rc.evaluate() == x
}
#[test]
fn test_defer() {
use crate::classes::deferrable::defer;
let memo: RcLazy<i32> = defer(|| RcLazy::new(|| 42));
assert_eq!(*memo.evaluate(), 42);
}
#[test]
fn test_send_defer() {
use crate::classes::send_deferrable::send_defer;
let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
assert_eq!(*memo.evaluate(), 42);
}
#[test]
fn test_rc_lazy_pure() {
let lazy = RcLazy::pure(42);
assert_eq!(*lazy.evaluate(), 42);
let shared = lazy.clone();
assert_eq!(*shared.evaluate(), 42);
}
#[test]
fn test_arc_lazy_pure() {
let lazy = ArcLazy::pure(42);
assert_eq!(*lazy.evaluate(), 42);
let shared = lazy.clone();
assert_eq!(*shared.evaluate(), 42);
}
#[test]
fn test_arc_lazy_pure_thread_safety() {
use std::thread;
let lazy = ArcLazy::pure(42);
let mut handles = vec![];
for _ in 0 .. 10 {
let lazy_clone = lazy.clone();
handles.push(thread::spawn(move || {
assert_eq!(*lazy_clone.evaluate(), 42);
}));
}
for handle in handles {
handle.join().unwrap();
}
}
#[quickcheck]
fn prop_rc_memo_get_memoization(x: i32) -> bool {
let memo = RcLazy::new(move || x.wrapping_mul(2));
let result1 = *memo.evaluate();
let result2 = *memo.evaluate();
result1 == result2
}
#[quickcheck]
fn prop_arc_memo_get_memoization(x: i32) -> bool {
let memo = ArcLazy::new(move || x.wrapping_mul(2));
let result1 = *memo.evaluate();
let result2 = *memo.evaluate();
result1 == result2
}
#[quickcheck]
fn prop_rc_memo_clone_shares_state(x: i32) -> bool {
let memo1 = RcLazy::new(move || x);
let memo2 = memo1.clone();
let result1 = *memo1.evaluate();
let result2 = *memo2.evaluate();
result1 == result2
}
#[quickcheck]
fn prop_arc_memo_clone_shares_state(x: i32) -> bool {
let memo1 = ArcLazy::new(move || x);
let memo2 = memo1.clone();
let result1 = *memo1.evaluate();
let result2 = *memo2.evaluate();
result1 == result2
}
#[quickcheck]
fn prop_memo_get_original_then_clone(x: String) -> bool {
let value = x.clone();
let memo = RcLazy::new(move || value.clone());
let memo_clone = memo.clone();
let result1 = memo.evaluate().clone();
let result2 = memo_clone.evaluate().clone();
result1 == result2
}
#[quickcheck]
fn prop_memo_deterministic(
x: i32,
y: i32,
) -> bool {
let memo1 = RcLazy::new(move || x.wrapping_add(y));
let memo2 = RcLazy::new(move || x.wrapping_add(y));
*memo1.evaluate() == *memo2.evaluate()
}
#[test]
fn test_arc_lazy_ref_map() {
let memo = ArcLazy::new(|| 10);
let mapped = memo.ref_map(|x| *x * 2);
assert_eq!(*mapped.evaluate(), 20);
}
#[test]
fn test_arc_lazy_ref_map_thread_safety() {
use std::thread;
let memo = ArcLazy::new(|| 10);
let mapped = memo.ref_map(|x| *x * 3);
let mut handles = vec![];
for _ in 0 .. 5 {
let m = mapped.clone();
handles.push(thread::spawn(move || {
assert_eq!(*m.evaluate(), 30);
}));
}
for handle in handles {
handle.join().unwrap();
}
}
#[test]
fn test_rc_lazy_semigroup() {
use crate::classes::semigroup::append;
let a = RcLazy::pure("Hello".to_string());
let b = RcLazy::pure(" World".to_string());
let c = append(a, b);
assert_eq!(*c.evaluate(), "Hello World");
}
#[test]
fn test_arc_lazy_semigroup() {
use crate::classes::semigroup::append;
let a = ArcLazy::pure("Hello".to_string());
let b = ArcLazy::pure(" World".to_string());
let c = append(a, b);
assert_eq!(*c.evaluate(), "Hello World");
}
#[test]
fn test_rc_lazy_semigroup_associativity() {
use crate::classes::semigroup::append;
let a = RcLazy::pure("a".to_string());
let b = RcLazy::pure("b".to_string());
let c = RcLazy::pure("c".to_string());
let ab_c = append(append(a.clone(), b.clone()), c.clone());
let a_bc = append(a, append(b, c));
assert_eq!(*ab_c.evaluate(), *a_bc.evaluate());
}
#[test]
fn test_rc_lazy_monoid() {
use crate::classes::monoid::empty;
let t: RcLazy<String> = empty();
assert_eq!(*t.evaluate(), "");
}
#[test]
fn test_arc_lazy_monoid() {
use crate::classes::monoid::empty;
let t: ArcLazy<String> = empty();
assert_eq!(*t.evaluate(), "");
}
#[test]
fn test_rc_lazy_monoid_identity() {
use crate::classes::{
monoid::empty,
semigroup::append,
};
let a = RcLazy::pure("hello".to_string());
let left: RcLazy<String> = append(empty(), a.clone());
assert_eq!(*left.evaluate(), *a.evaluate());
let right: RcLazy<String> = append(a.clone(), empty());
assert_eq!(*right.evaluate(), *a.evaluate());
}
#[test]
fn test_rc_lazy_ref_fold_right() {
use crate::functions::*;
let lazy = RcLazy::pure(10);
let result = explicit::fold_right::<
crate::brands::RcFnBrand,
crate::brands::LazyBrand<RcLazyConfig>,
_,
_,
_,
_,
>(|a: &i32, b| *a + b, 5, &lazy);
assert_eq!(result, 15);
}
#[test]
fn test_rc_lazy_ref_fold_left() {
use crate::functions::*;
let lazy = RcLazy::pure(10);
let result = explicit::fold_left::<
crate::brands::RcFnBrand,
crate::brands::LazyBrand<RcLazyConfig>,
_,
_,
_,
_,
>(|b, a: &i32| b + *a, 5, &lazy);
assert_eq!(result, 15);
}
#[test]
fn test_rc_lazy_ref_fold_map() {
use crate::functions::*;
let lazy = RcLazy::pure(10);
let result = explicit::fold_map::<
crate::brands::RcFnBrand,
crate::brands::LazyBrand<RcLazyConfig>,
_,
_,
_,
_,
>(|a: &i32| a.to_string(), &lazy);
assert_eq!(result, "10");
}
#[test]
fn test_rc_lazy_partial_eq() {
let a = RcLazy::pure(42);
let b = RcLazy::pure(42);
let c = RcLazy::pure(99);
assert!(a == b);
assert!(b != c);
}
#[test]
fn test_arc_lazy_partial_eq() {
let a = ArcLazy::pure(42);
let b = ArcLazy::pure(42);
let c = ArcLazy::pure(99);
assert!(a == b);
assert!(b != c);
}
#[test]
fn test_rc_lazy_partial_ord() {
let a = RcLazy::pure(1);
let b = RcLazy::pure(2);
let c = RcLazy::pure(2);
assert!(a < b);
assert!(b > a);
assert!(b >= c);
assert!(c <= b);
}
#[test]
fn test_arc_lazy_partial_ord() {
let a = ArcLazy::pure(1);
let b = ArcLazy::pure(2);
assert!(a < b);
assert!(b > a);
}
#[test]
fn test_rc_lazy_fix_constant() {
let fixed = rc_lazy_fix(|_self_ref: RcLazy<i32>| 42);
assert_eq!(*fixed.evaluate(), 42);
}
#[test]
fn test_rc_lazy_fix_cell_initialized() {
let fixed = rc_lazy_fix(|_self_ref: RcLazy<String>| String::from("initialized"));
assert_eq!(fixed.evaluate().as_str(), "initialized");
}
#[test]
fn test_rc_lazy_fix_self_reference_plumbing() {
let fixed = rc_lazy_fix(|_self_ref: RcLazy<i32>| 7);
assert_eq!(*fixed.evaluate(), 7);
}
#[test]
fn test_rc_lazy_fix_memoization() {
let counter = Rc::new(RefCell::new(0));
let counter_clone = counter.clone();
let fixed = rc_lazy_fix(move |_self_ref: RcLazy<i32>| {
*counter_clone.borrow_mut() += 1;
100
});
assert_eq!(*counter.borrow(), 0);
assert_eq!(*fixed.evaluate(), 100);
assert_eq!(*counter.borrow(), 1);
assert_eq!(*fixed.evaluate(), 100);
assert_eq!(*counter.borrow(), 1);
}
#[test]
fn test_rc_lazy_fix_clone_sharing() {
let fixed = rc_lazy_fix(|_self_ref: RcLazy<i32>| 55);
let cloned = fixed.clone();
assert_eq!(*fixed.evaluate(), 55);
assert_eq!(*cloned.evaluate(), 55);
}
#[test]
fn test_arc_lazy_fix_constant() {
let fixed = arc_lazy_fix(|_self_ref: ArcLazy<i32>| 42);
assert_eq!(*fixed.evaluate(), 42);
}
#[test]
fn test_arc_lazy_fix_cell_initialized() {
let fixed = arc_lazy_fix(|_self_ref: ArcLazy<String>| String::from("initialized"));
assert_eq!(fixed.evaluate().as_str(), "initialized");
}
#[test]
fn test_arc_lazy_fix_self_reference_plumbing() {
let fixed = arc_lazy_fix(|_self_ref: ArcLazy<i32>| 7);
assert_eq!(*fixed.evaluate(), 7);
}
#[test]
fn test_arc_lazy_fix_memoization() {
use std::sync::atomic::{
AtomicUsize,
Ordering,
};
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
let fixed = arc_lazy_fix(move |_self_ref: ArcLazy<i32>| {
counter_clone.fetch_add(1, Ordering::SeqCst);
100
});
assert_eq!(counter.load(Ordering::SeqCst), 0);
assert_eq!(*fixed.evaluate(), 100);
assert_eq!(counter.load(Ordering::SeqCst), 1);
assert_eq!(*fixed.evaluate(), 100);
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_arc_lazy_ref_fold_right() {
use crate::{
brands::*,
functions::*,
};
let lazy = ArcLazy::new(|| 10);
let result = explicit::fold_right::<RcFnBrand, LazyBrand<ArcLazyConfig>, _, _, _, _>(
|a: &i32, b| *a + b,
5,
&lazy,
);
assert_eq!(result, 15);
}
#[test]
fn test_arc_lazy_ref_fold_left() {
use crate::{
brands::*,
functions::*,
};
let lazy = ArcLazy::new(|| 10);
let result = explicit::fold_left::<RcFnBrand, LazyBrand<ArcLazyConfig>, _, _, _, _>(
|b, a: &i32| b + *a,
5,
&lazy,
);
assert_eq!(result, 15);
}
#[test]
fn test_arc_lazy_ref_fold_map() {
use crate::{
brands::*,
functions::*,
};
let lazy = ArcLazy::new(|| 10);
let result = explicit::fold_map::<ArcFnBrand, LazyBrand<ArcLazyConfig>, _, _, _, _>(
|a: &i32| a.to_string(),
&lazy,
);
assert_eq!(result, "10");
}
#[quickcheck]
fn prop_arc_lazy_ref_fold_right(x: i32) -> bool {
use crate::{
brands::*,
functions::*,
};
let rc_lazy = RcLazy::new(move || x);
let arc_lazy = ArcLazy::new(move || x);
let rc_result = explicit::fold_right::<RcFnBrand, LazyBrand<RcLazyConfig>, _, _, _, _>(
|a: &i32, b| *a + b,
0,
&rc_lazy,
);
let arc_result = explicit::fold_right::<RcFnBrand, LazyBrand<ArcLazyConfig>, _, _, _, _>(
|a: &i32, b| *a + b,
0,
&arc_lazy,
);
rc_result == arc_result
}
#[quickcheck]
fn prop_arc_lazy_ref_map_identity(x: i32) -> bool {
let lazy = ArcLazy::new(move || x);
let mapped = lazy.clone().ref_map(|a| *a);
*lazy.evaluate() == *mapped.evaluate()
}
#[quickcheck]
fn prop_arc_lazy_ref_map_composition(x: i32) -> bool {
let f = |a: &i32| a.wrapping_mul(2);
let g = |a: &i32| a.wrapping_add(1);
let lazy1 = ArcLazy::new(move || x);
let composed = lazy1.ref_map(|a| g(&f(a)));
let lazy2 = ArcLazy::new(move || x);
let chained = lazy2.ref_map(f).ref_map(g);
*composed.evaluate() == *chained.evaluate()
}
#[quickcheck]
fn prop_arc_lazy_ref_map_memoization(x: i32) -> bool {
let lazy = ArcLazy::new(move || x);
let mapped = lazy.ref_map(|a| a.wrapping_mul(3));
let r1 = *mapped.evaluate();
let r2 = *mapped.evaluate();
r1 == r2
}
#[test]
fn test_rc_lazy_fix_self_reference() {
let lazy = rc_lazy_fix(|self_ref: RcLazy<Vec<i32>>| {
let _ = self_ref; vec![1, 2, 3]
});
assert_eq!(*lazy.evaluate(), vec![1, 2, 3]);
}
#[test]
fn test_rc_lazy_fix_uses_self_ref() {
let counter = Rc::new(RefCell::new(0));
let counter_clone = counter.clone();
let lazy = rc_lazy_fix(move |self_ref: RcLazy<i32>| {
*counter_clone.borrow_mut() += 1;
let _ = self_ref;
42
});
assert_eq!(*lazy.evaluate(), 42);
assert_eq!(*counter.borrow(), 1);
assert_eq!(*lazy.evaluate(), 42);
assert_eq!(*counter.borrow(), 1);
}
#[test]
fn test_arc_lazy_fix_self_reference() {
let lazy = arc_lazy_fix(|self_ref: ArcLazy<Vec<i32>>| {
let _ = self_ref;
vec![1, 2, 3]
});
assert_eq!(*lazy.evaluate(), vec![1, 2, 3]);
}
#[test]
fn test_arc_lazy_fix_uses_self_ref() {
use std::sync::atomic::{
AtomicUsize,
Ordering,
};
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
let lazy = arc_lazy_fix(move |self_ref: ArcLazy<i32>| {
counter_clone.fetch_add(1, Ordering::SeqCst);
let _ = self_ref;
42
});
assert_eq!(*lazy.evaluate(), 42);
assert_eq!(counter.load(Ordering::SeqCst), 1);
assert_eq!(*lazy.evaluate(), 42);
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_arc_lazy_fix_thread_safety() {
use std::thread;
let lazy = arc_lazy_fix(|self_ref: ArcLazy<i32>| {
let _ = self_ref;
42
});
let mut handles = vec![];
for _ in 0 .. 10 {
let lazy_clone = lazy.clone();
handles.push(thread::spawn(move || {
assert_eq!(*lazy_clone.evaluate(), 42);
}));
}
for handle in handles {
handle.join().unwrap();
}
}
#[quickcheck]
fn prop_rc_lazy_fix_constant(x: i32) -> bool {
let fixed = rc_lazy_fix(move |_: RcLazy<i32>| x);
*fixed.evaluate() == x
}
#[quickcheck]
fn prop_arc_lazy_fix_constant(x: i32) -> bool {
let fixed = arc_lazy_fix(move |_: ArcLazy<i32>| x);
*fixed.evaluate() == x
}
#[quickcheck]
fn ref_functor_identity(x: i32) -> bool {
let lazy = RcLazy::pure(x);
*lazy.clone().ref_map(|v| *v).evaluate() == *lazy.evaluate()
}
#[quickcheck]
fn ref_functor_composition(x: i32) -> bool {
let f = |a: &i32| a.wrapping_add(1);
let g = |a: &i32| a.wrapping_mul(2);
let lazy = RcLazy::pure(x);
let lhs = *lazy.clone().ref_map(move |v| f(&g(v))).evaluate();
let rhs = *lazy.ref_map(g).ref_map(f).evaluate();
lhs == rhs
}
#[quickcheck]
fn deferrable_transparency(x: i32) -> bool {
let lazy = RcLazy::pure(x);
let deferred: RcLazy<i32> = crate::classes::Deferrable::defer(move || RcLazy::pure(x));
*deferred.evaluate() == *lazy.evaluate()
}
#[quickcheck]
fn rc_lazy_semigroup_associativity(
a: String,
b: String,
c: String,
) -> bool {
use crate::classes::semigroup::append;
let la = RcLazy::pure(a.clone());
let lb = RcLazy::pure(b.clone());
let lc = RcLazy::pure(c.clone());
let la2 = RcLazy::pure(a);
let lb2 = RcLazy::pure(b);
let lc2 = RcLazy::pure(c);
let lhs = append(append(la, lb), lc).evaluate().clone();
let rhs = append(la2, append(lb2, lc2)).evaluate().clone();
lhs == rhs
}
#[quickcheck]
fn arc_lazy_semigroup_associativity(
a: String,
b: String,
c: String,
) -> bool {
use crate::classes::semigroup::append;
let la = ArcLazy::pure(a.clone());
let lb = ArcLazy::pure(b.clone());
let lc = ArcLazy::pure(c.clone());
let la2 = ArcLazy::pure(a);
let lb2 = ArcLazy::pure(b);
let lc2 = ArcLazy::pure(c);
let lhs = append(append(la, lb), lc).evaluate().clone();
let rhs = append(la2, append(lb2, lc2)).evaluate().clone();
lhs == rhs
}
#[quickcheck]
fn rc_lazy_monoid_left_identity(x: String) -> bool {
use crate::classes::{
monoid::empty,
semigroup::append,
};
let a = RcLazy::pure(x.clone());
let lhs: RcLazy<String> = append(empty(), a);
*lhs.evaluate() == x
}
#[quickcheck]
fn rc_lazy_monoid_right_identity(x: String) -> bool {
use crate::classes::{
monoid::empty,
semigroup::append,
};
let a = RcLazy::pure(x.clone());
let rhs: RcLazy<String> = append(a, empty());
*rhs.evaluate() == x
}
#[quickcheck]
fn arc_lazy_monoid_left_identity(x: String) -> bool {
use crate::classes::{
monoid::empty,
semigroup::append,
};
let a = ArcLazy::pure(x.clone());
let lhs: ArcLazy<String> = append(empty(), a);
*lhs.evaluate() == x
}
#[quickcheck]
fn arc_lazy_monoid_right_identity(x: String) -> bool {
use crate::classes::{
monoid::empty,
semigroup::append,
};
let a = ArcLazy::pure(x.clone());
let rhs: ArcLazy<String> = append(a, empty());
*rhs.evaluate() == x
}
#[test]
fn test_rc_lazy_config_pointer_brand() {
fn assert_brand_is_rc<C: LazyConfig<PointerBrand = crate::brands::RcBrand>>() {}
assert_brand_is_rc::<RcLazyConfig>();
}
#[test]
fn test_arc_lazy_config_pointer_brand() {
fn assert_brand_is_arc<C: LazyConfig<PointerBrand = crate::brands::ArcBrand>>() {}
assert_brand_is_arc::<ArcLazyConfig>();
}
#[test]
fn test_panic_poisoning() {
use std::panic;
let memo: RcLazy<i32> = RcLazy::new(|| {
panic!("initializer panic");
});
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let _ = memo.evaluate();
}));
assert!(result.is_err(), "First evaluate should panic");
let result2 = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let _ = memo.evaluate();
}));
assert!(result2.is_err(), "Second evaluate should also panic (poisoned)");
}
#[test]
fn test_rc_lazy_fix_shared_self_ref() {
let lazy = rc_lazy_fix(|self_ref: RcLazy<i32>| {
let _captured = self_ref.clone();
100
});
assert_eq!(*lazy.evaluate(), 100);
let cloned = lazy.clone();
assert_eq!(*cloned.evaluate(), 100);
}
#[test]
fn test_rc_lazy_fix_knot_tying_ptr_eq() {
let stash: Rc<RefCell<Option<RcLazy<i32>>>> = Rc::new(RefCell::new(None));
let stash_clone = stash.clone();
let lazy = rc_lazy_fix(move |self_ref: RcLazy<i32>| {
*stash_clone.borrow_mut() = Some(self_ref);
42
});
assert_eq!(*lazy.evaluate(), 42);
let self_ref = stash.borrow().clone().unwrap();
assert!(Rc::ptr_eq(&lazy.0, &self_ref.0));
assert_eq!(*self_ref.evaluate(), 42);
}
#[test]
fn test_rc_lazy_fix_knot_tying_shared_cache() {
let counter = Rc::new(RefCell::new(0));
let stash: Rc<RefCell<Option<RcLazy<i32>>>> = Rc::new(RefCell::new(None));
let counter_clone = counter.clone();
let stash_clone = stash.clone();
let lazy = rc_lazy_fix(move |self_ref: RcLazy<i32>| {
*stash_clone.borrow_mut() = Some(self_ref);
*counter_clone.borrow_mut() += 1;
100
});
assert_eq!(*counter.borrow(), 0);
assert_eq!(*lazy.evaluate(), 100);
assert_eq!(*counter.borrow(), 1);
let self_ref = stash.borrow().clone().unwrap();
assert_eq!(*self_ref.evaluate(), 100);
assert_eq!(*counter.borrow(), 1);
}
#[test]
#[should_panic]
fn test_rc_lazy_fix_reentrant_panics() {
let lazy = rc_lazy_fix(|self_ref: RcLazy<i32>| {
*self_ref.evaluate() + 1
});
let _ = lazy.evaluate();
}
#[test]
fn test_arc_lazy_fix_knot_tying_ptr_eq() {
use std::sync::Mutex;
let stash: Arc<Mutex<Option<ArcLazy<i32>>>> = Arc::new(Mutex::new(None));
let stash_clone = stash.clone();
let lazy = arc_lazy_fix(move |self_ref: ArcLazy<i32>| {
*stash_clone.lock().unwrap() = Some(self_ref);
42
});
assert_eq!(*lazy.evaluate(), 42);
let self_ref = stash.lock().unwrap().clone().unwrap();
assert!(Arc::ptr_eq(&lazy.0, &self_ref.0));
assert_eq!(*self_ref.evaluate(), 42);
}
#[test]
fn test_arc_lazy_fix_knot_tying_shared_cache() {
use std::sync::{
Mutex,
atomic::{
AtomicUsize,
Ordering,
},
};
let counter = Arc::new(AtomicUsize::new(0));
let stash: Arc<Mutex<Option<ArcLazy<i32>>>> = Arc::new(Mutex::new(None));
let counter_clone = counter.clone();
let stash_clone = stash.clone();
let lazy = arc_lazy_fix(move |self_ref: ArcLazy<i32>| {
*stash_clone.lock().unwrap() = Some(self_ref);
counter_clone.fetch_add(1, Ordering::SeqCst);
100
});
assert_eq!(counter.load(Ordering::SeqCst), 0);
assert_eq!(*lazy.evaluate(), 100);
assert_eq!(counter.load(Ordering::SeqCst), 1);
let self_ref = stash.lock().unwrap().clone().unwrap();
assert_eq!(*self_ref.evaluate(), 100);
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_arc_lazy_fix_knot_tying_cross_thread() {
use std::{
sync::Mutex,
thread,
};
let stash: Arc<Mutex<Option<ArcLazy<i32>>>> = Arc::new(Mutex::new(None));
let stash_clone = stash.clone();
let lazy = arc_lazy_fix(move |self_ref: ArcLazy<i32>| {
*stash_clone.lock().unwrap() = Some(self_ref);
77
});
assert_eq!(*lazy.evaluate(), 77);
let self_ref = stash.lock().unwrap().clone().unwrap();
let handle = thread::spawn(move || *self_ref.evaluate());
assert_eq!(handle.join().unwrap(), 77);
}
#[test]
fn m_do_ref_lazy_manual() {
use crate::{
brands::LazyBrand,
functions::*,
};
let lazy_a = RcLazy::new(|| 10i32);
let result =
explicit::bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(&lazy_a, move |a: &i32| {
ref_pure::<LazyBrand<RcLazyConfig>, _>(&(*a * 2))
});
assert_eq!(*result.evaluate(), 20);
}
#[test]
fn m_do_ref_lazy_macro() {
use {
crate::{
brands::LazyBrand,
functions::*,
},
fp_macros::m_do,
};
let lazy_a = RcLazy::new(|| 10i32);
let result = m_do!(ref LazyBrand<RcLazyConfig> {
a: &i32 <- lazy_a;
pure(*a * 2)
});
assert_eq!(*result.evaluate(), 20);
}
#[test]
fn m_do_ref_lazy_multi_bind() {
use {
crate::{
brands::LazyBrand,
functions::*,
},
fp_macros::m_do,
};
let lazy_a = RcLazy::new(|| 10i32);
let lazy_b = RcLazy::new(|| 20i32);
let result = m_do!(ref LazyBrand<RcLazyConfig> {
a: &i32 <- lazy_a;
let a_val = *a;
b: &i32 <- lazy_b.clone();
pure(a_val + *b)
});
assert_eq!(*result.evaluate(), 30);
}
#[test]
fn m_do_ref_lazy_untyped() {
use {
crate::{
brands::LazyBrand,
functions::*,
},
fp_macros::m_do,
};
let lazy_a = RcLazy::new(|| 10i32);
let result = m_do!(ref LazyBrand<RcLazyConfig> {
a <- lazy_a;
pure(*a * 3)
});
assert_eq!(*result.evaluate(), 30);
}
#[test]
fn a_do_ref_lazy() {
use {
crate::{
brands::LazyBrand,
functions::*,
},
fp_macros::a_do,
};
let lazy_a = RcLazy::new(|| 10i32);
let lazy_b = RcLazy::new(|| 20i32);
let result = a_do!(ref LazyBrand<RcLazyConfig> {
a: &i32 <- lazy_a;
b: &i32 <- lazy_b;
*a + *b
});
assert_eq!(*result.evaluate(), 30);
}
}