fp_library/types/
lazy.rs

1//! Lazy value wrapper.
2//!
3//! This module defines the [`Lazy`] struct, which represents a lazily-computed, memoized value.
4//! It implements [`Semigroup`], [`Monoid`], and [`Defer`].
5//!
6//! ## Configurations
7//!
8//! - [`RcLazyConfig`] / [`RcLazy`]: Single-threaded lazy values using [`Rc`](std::rc::Rc). Not thread-safe.
9//! - [`ArcLazyConfig`] / [`ArcLazy`]: Thread-safe lazy values using [`Arc`]. Requires `A: Send + Sync`.
10
11use crate::{
12	brands::{ArcBrand, ArcFnBrand, LazyBrand, OnceCellBrand, OnceLockBrand, RcBrand, RcFnBrand},
13	classes::{
14		cloneable_fn::CloneableFn, defer::Defer, monoid::Monoid, once::Once,
15		ref_counted_pointer::RefCountedPointer, semigroup::Semigroup,
16		send_cloneable_fn::SendCloneableFn, thunk_wrapper::ThunkWrapper,
17	},
18	impl_kind,
19	kinds::*,
20};
21use std::{
22	fmt::{self, Debug, Formatter},
23	ops::Deref,
24	sync::Arc,
25};
26use thiserror::Error;
27
28/// Configuration trait for `Lazy` types.
29///
30/// This trait defines the types used for pointer storage, memoization, and thunk execution.
31/// It ensures that compatible types are used together (e.g., `Rc` with `OnceCell`, `Arc` with `OnceLock`).
32pub trait LazyConfig: Sized + 'static {
33	/// The pointer brand for shared ownership (e.g., `RcBrand`, `ArcBrand`).
34	type PtrBrand: RefCountedPointer + ThunkWrapper;
35	/// The once-cell brand for memoization (e.g., `OnceCellBrand`, `OnceLockBrand`).
36	type OnceBrand: Once;
37	/// The function brand for thunk storage (e.g., `RcFnBrand`, `ArcFnBrand`).
38	type FnBrand: CloneableFn;
39	/// The thunk type to use for this configuration.
40	/// Thunks deref to `Fn(()) -> A` to match the cloneable function wrapper.
41	type ThunkOf<'a, A>: Clone + Deref<Target: Fn(()) -> A>
42	where
43		A: 'a;
44}
45
46/// Trait for `Lazy` configurations that support semigroup operations.
47pub trait LazySemigroup<A>: LazyConfig {
48	/// Combines two lazy values.
49	///
50	/// This method combines two lazy values into a new lazy value.
51	///
52	/// ### Type Signature
53	///
54	/// `forall config a. Semigroup a => (Lazy config a, Lazy config a) -> Lazy config a`
55	///
56	/// ### Parameters
57	///
58	/// * `x`: The first lazy value.
59	/// * `y`: The second lazy value.
60	///
61	/// ### Returns
62	///
63	/// A new lazy value that combines the results.
64	///
65	/// ### Examples
66	///
67	/// ```
68	/// use fp_library::types::lazy::*;
69	///
70	/// let x = RcLazy::new(RcLazyConfig::new_thunk(|_| "Hello, ".to_string()));
71	/// let y = RcLazy::new(RcLazyConfig::new_thunk(|_| "World!".to_string()));
72	/// // Note: LazySemigroup::append is usually called via Semigroup::append on the Lazy type
73	/// let z = <RcLazyConfig as LazySemigroup<String>>::append(x, y);
74	/// assert_eq!(Lazy::force_or_panic(&z), "Hello, World!".to_string());
75	/// ```
76	fn append<'a>(
77		x: Lazy<'a, Self, A>,
78		y: Lazy<'a, Self, A>,
79	) -> Lazy<'a, Self, A>
80	where
81		A: Semigroup + Clone + 'a;
82}
83
84/// Trait for `Lazy` configurations that support monoid operations.
85pub trait LazyMonoid<A>: LazySemigroup<A> {
86	/// Returns the identity element.
87	///
88	/// This method returns a lazy value that evaluates to the identity element.
89	///
90	/// ### Type Signature
91	///
92	/// `forall config a. Monoid a => () -> Lazy config a`
93	///
94	/// ### Returns
95	///
96	/// A lazy value containing the identity element.
97	///
98	/// ### Examples
99	///
100	/// ```
101	/// use fp_library::types::lazy::*;
102	///
103	/// let x = <RcLazyConfig as LazyMonoid<String>>::empty();
104	/// assert_eq!(Lazy::force_or_panic(&x), "".to_string());
105	/// ```
106	fn empty<'a>() -> Lazy<'a, Self, A>
107	where
108		A: Monoid + Clone + 'a;
109}
110
111/// Trait for `Lazy` configurations that support defer operations.
112pub trait LazyDefer<'a, A>: LazyConfig {
113	/// Creates a value from a computation that produces the value.
114	///
115	/// This method defers the construction of a `Lazy` value.
116	///
117	/// ### Type Signature
118	///
119	/// `forall config a. (() -> Lazy config a) -> Lazy config a`
120	///
121	/// ### Type Parameters
122	///
123	/// * `FnBrand`: The brand of the cloneable function wrapper.
124	///
125	/// ### Parameters
126	///
127	/// * `f`: A thunk (wrapped in a cloneable function) that produces the value.
128	///
129	/// ### Returns
130	///
131	/// A new lazy value.
132	///
133	/// ### Examples
134	///
135	/// ```
136	/// use fp_library::{brands::*, functions::*, types::lazy::*};
137	///
138	/// let lazy = <RcLazyConfig as LazyDefer<i32>>::defer::<RcFnBrand>(
139	///     cloneable_fn_new::<RcFnBrand, _, _>(|_| RcLazy::new(RcLazyConfig::new_thunk(|_| 42)))
140	/// );
141	/// assert_eq!(Lazy::force_or_panic(&lazy), 42);
142	/// ```
143	fn defer<FnBrand>(
144		f: <FnBrand as CloneableFn>::Of<'a, (), Lazy<'a, Self, A>>
145	) -> Lazy<'a, Self, A>
146	where
147		FnBrand: CloneableFn + 'a,
148		A: Clone + 'a;
149}
150
151// =============================================================================
152// RcLazyConfig - Single-threaded lazy values
153// =============================================================================
154
155/// Configuration for `Rc`-based `Lazy` values.
156///
157/// Uses `Rc` for shared ownership, `OnceCell` for memoization, and `RcFn` for thunks.
158/// This configuration is **not thread-safe**.
159///
160/// ### Examples
161///
162/// ```
163/// use fp_library::types::lazy::*;
164///
165/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
166/// assert_eq!(Lazy::force_or_panic(&lazy), 42);
167/// ```
168#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
169pub struct RcLazyConfig;
170
171impl RcLazyConfig {
172	/// Creates a new thunk from a closure.
173	///
174	/// ### Type Signature
175	///
176	/// `forall a. (Fn(()) -> a) -> ThunkOf a`
177	///
178	/// ### Type Parameters
179	///
180	/// * `A`: The return type of the closure.
181	/// * `F`: The closure type.
182	///
183	/// ### Parameters
184	///
185	/// * `f`: The closure to wrap.
186	///
187	/// ### Returns
188	///
189	/// A new thunk.
190	///
191	/// ### Examples
192	///
193	/// ```
194	/// use fp_library::types::lazy::*;
195	///
196	/// let thunk = RcLazyConfig::new_thunk(|_| 42);
197	/// assert_eq!(thunk(()), 42);
198	/// ```
199	pub fn new_thunk<'a, A, F>(f: F) -> <Self as LazyConfig>::ThunkOf<'a, A>
200	where
201		A: 'a,
202		F: Fn(()) -> A + Clone + 'a,
203	{
204		<RcFnBrand as CloneableFn>::new(f)
205	}
206}
207
208impl LazyConfig for RcLazyConfig {
209	type PtrBrand = RcBrand;
210	type OnceBrand = OnceCellBrand;
211	type FnBrand = RcFnBrand;
212	type ThunkOf<'a, A>
213		= <RcFnBrand as CloneableFn>::Of<'a, (), A>
214	where
215		A: 'a;
216}
217
218impl<A> LazySemigroup<A> for RcLazyConfig {
219	/// Combines two lazy values.
220	///
221	/// The combination is itself lazy: the result is a new thunk that, when forced,
222	/// forces both input values and combines them.
223	///
224	/// ### Type Signature
225	///
226	/// `forall a. Semigroup a => (RcLazy a, RcLazy a) -> RcLazy a`
227	///
228	/// ### Parameters
229	///
230	/// * `x`: The first lazy value.
231	/// * `y`: The second lazy value.
232	///
233	/// ### Returns
234	///
235	/// A new lazy value that combines the results.
236	///
237	/// ### Examples
238	///
239	/// ```
240	/// use fp_library::types::lazy::*;
241	///
242	/// let x = ArcLazy::new(ArcLazyConfig::new_thunk(|_| "Hello, ".to_string()));
243	/// let y = ArcLazy::new(ArcLazyConfig::new_thunk(|_| "World!".to_string()));
244	/// let z = ArcLazyConfig::append(x, y);
245	/// assert_eq!(Lazy::force_or_panic(&z), "Hello, World!".to_string());
246	/// ```
247	fn append<'a>(
248		x: Lazy<'a, Self, A>,
249		y: Lazy<'a, Self, A>,
250	) -> Lazy<'a, Self, A>
251	where
252		A: Semigroup + Clone + 'a,
253	{
254		let thunk = Self::new_thunk(move |_| {
255			let x_val = match Lazy::force(&x) {
256				Ok(v) => v.clone(),
257				Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
258			};
259			let y_val = match Lazy::force(&y) {
260				Ok(v) => v.clone(),
261				Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
262			};
263			Semigroup::append(x_val, y_val)
264		});
265		Lazy::new(thunk)
266	}
267}
268
269impl<A> LazyMonoid<A> for RcLazyConfig {
270	/// Returns the identity element.
271	///
272	/// This method returns a lazy value that evaluates to the underlying type's identity element.
273	///
274	/// ### Type Signature
275	///
276	/// `forall a. Monoid a => () -> RcLazy a`
277	///
278	/// ### Returns
279	///
280	/// A lazy value containing the identity element.
281	///
282	/// ### Examples
283	///
284	/// ```
285	/// use fp_library::types::lazy::*;
286	///
287	/// let x: ArcLazy<String> = ArcLazyConfig::empty();
288	/// assert_eq!(Lazy::force_or_panic(&x), "".to_string());
289	/// ```
290	fn empty<'a>() -> Lazy<'a, Self, A>
291	where
292		A: Monoid + Clone + 'a,
293	{
294		let thunk = Self::new_thunk(move |_| Monoid::empty());
295		Lazy::new(thunk)
296	}
297}
298
299impl<'a, A> LazyDefer<'a, A> for RcLazyConfig {
300	/// Creates a value from a computation that produces the value.
301	///
302	/// This method defers the construction of a `Lazy` value.
303	///
304	/// ### Type Signature
305	///
306	/// `forall a. (() -> RcLazy a) -> RcLazy a`
307	///
308	/// ### Type Parameters
309	///
310	/// * `FnBrand`: The brand of the cloneable function wrapper.
311	///
312	/// ### Parameters
313	///
314	/// * `f`: A thunk (wrapped in a cloneable function) that produces the value.
315	///
316	/// ### Returns
317	///
318	/// A new lazy value.
319	///
320	/// ### Examples
321	///
322	/// ```
323	/// use fp_library::{brands::*, functions::*, types::lazy::*};
324	///
325	/// let lazy = RcLazyConfig::defer::<RcFnBrand>(
326	///     cloneable_fn_new::<RcFnBrand, _, _>(|_| RcLazy::new(RcLazyConfig::new_thunk(|_| 42)))
327	/// );
328	/// assert_eq!(Lazy::force_or_panic(&lazy), 42);
329	/// ```
330	fn defer<FnBrand>(
331		f: <FnBrand as CloneableFn>::Of<'a, (), Lazy<'a, Self, A>>
332	) -> Lazy<'a, Self, A>
333	where
334		FnBrand: CloneableFn + 'a,
335		A: Clone + 'a,
336	{
337		let thunk = Self::new_thunk(move |_| {
338			let inner_lazy = f(());
339			match Lazy::force(&inner_lazy) {
340				Ok(v) => v.clone(),
341				Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
342			}
343		});
344		Lazy::new(thunk)
345	}
346}
347
348// =============================================================================
349// ArcLazyConfig - Thread-safe lazy values
350// =============================================================================
351
352/// Configuration for `Arc`-based `Lazy` values.
353///
354/// Uses `Arc` for shared ownership, `OnceLock` for memoization, and thread-safe `ArcFn` for thunks.
355/// This configuration is **thread-safe** and requires `A: Send + Sync` for full functionality.
356///
357/// ### Thread Safety
358///
359/// `ArcLazy<A>` is `Send + Sync` when `A: Send + Sync`. This allows lazy values
360/// to be shared across threads safely.
361///
362/// ### Examples
363///
364/// ```
365/// use fp_library::types::lazy::*;
366/// use std::thread;
367///
368/// let lazy = ArcLazy::new(ArcLazyConfig::new_thunk(|_| 42));
369/// let lazy_clone = lazy.clone();
370///
371/// let handle = thread::spawn(move || {
372///     Lazy::force_or_panic(&lazy_clone)
373/// });
374///
375/// assert_eq!(handle.join().unwrap(), 42);
376/// ```
377#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
378pub struct ArcLazyConfig;
379
380impl ArcLazyConfig {
381	/// Creates a new thread-safe thunk from a closure.
382	///
383	/// The closure must be `Send + Sync` to ensure thread safety.
384	///
385	/// ### Type Signature
386	///
387	/// `forall a. (Fn(()) -> a + Send + Sync) -> ThunkOf a`
388	///
389	/// ### Type Parameters
390	///
391	/// * `A`: The return type of the closure.
392	/// * `F`: The closure type (must be `Send + Sync`).
393	///
394	/// ### Parameters
395	///
396	/// * `f`: The closure to wrap.
397	///
398	/// ### Returns
399	///
400	/// A new thread-safe thunk.
401	///
402	/// ### Examples
403	///
404	/// ```
405	/// use fp_library::types::lazy::*;
406	///
407	/// let thunk = ArcLazyConfig::new_thunk(|_| 42);
408	/// assert_eq!(thunk(()), 42);
409	/// ```
410	pub fn new_thunk<'a, A, F>(f: F) -> <Self as LazyConfig>::ThunkOf<'a, A>
411	where
412		A: 'a,
413		F: Fn(()) -> A + Send + Sync + 'a,
414	{
415		<ArcFnBrand as SendCloneableFn>::send_cloneable_fn_new(f)
416	}
417}
418
419impl LazyConfig for ArcLazyConfig {
420	type PtrBrand = ArcBrand;
421	type OnceBrand = OnceLockBrand;
422	type FnBrand = ArcFnBrand;
423	// Use SendOf for thread-safe thunks
424	type ThunkOf<'a, A>
425		= <ArcFnBrand as SendCloneableFn>::SendOf<'a, (), A>
426	where
427		A: 'a;
428}
429
430// LazySemigroup for ArcLazyConfig requires A: Send + Sync because:
431// 1. The closure captures Lazy values which must be Send + Sync to be in a Send + Sync closure
432// 2. The result A is stored in OnceLock which requires Send + Sync for thread-safe access
433impl<A: Send + Sync> LazySemigroup<A> for ArcLazyConfig {
434	/// Combines two lazy values.
435	///
436	/// The combination is itself lazy: the result is a new thunk that, when forced,
437	/// forces both input values and combines them.
438	///
439	/// ### Type Signature
440	///
441	/// `forall a. (Semigroup a, Send a, Sync a) => (ArcLazy a, ArcLazy a) -> ArcLazy a`
442	///
443	/// ### Parameters
444	///
445	/// * `x`: The first lazy value.
446	/// * `y`: The second lazy value.
447	///
448	/// ### Returns
449	///
450	/// A new lazy value that combines the results.
451	///
452	/// ### Examples
453	///
454	/// ```
455	/// use fp_library::types::lazy::*;
456	///
457	/// let x = RcLazy::new(RcLazyConfig::new_thunk(|_| "Hello, ".to_string()));
458	/// let y = RcLazy::new(RcLazyConfig::new_thunk(|_| "World!".to_string()));
459	/// let z = RcLazyConfig::append(x, y);
460	/// assert_eq!(Lazy::force_or_panic(&z), "Hello, World!".to_string());
461	/// ```
462	fn append<'a>(
463		x: Lazy<'a, Self, A>,
464		y: Lazy<'a, Self, A>,
465	) -> Lazy<'a, Self, A>
466	where
467		A: Semigroup + Clone + 'a,
468	{
469		let thunk = Self::new_thunk(move |_| {
470			let x_val = match Lazy::force(&x) {
471				Ok(v) => v.clone(),
472				Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
473			};
474			let y_val = match Lazy::force(&y) {
475				Ok(v) => v.clone(),
476				Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
477			};
478			Semigroup::append(x_val, y_val)
479		});
480		Lazy::new(thunk)
481	}
482}
483
484impl<A: Send + Sync> LazyMonoid<A> for ArcLazyConfig {
485	/// Returns the identity element.
486	///
487	/// This method returns a lazy value that evaluates to the underlying type's identity element.
488	///
489	/// ### Type Signature
490	///
491	/// `forall a. (Monoid a, Send a, Sync a) => () -> ArcLazy a`
492	///
493	/// ### Returns
494	///
495	/// A lazy value containing the identity element.
496	///
497	/// ### Examples
498	///
499	/// ```
500	/// use fp_library::types::lazy::*;
501	///
502	/// let x: RcLazy<String> = RcLazyConfig::empty();
503	/// assert_eq!(Lazy::force_or_panic(&x), "".to_string());
504	/// ```
505	fn empty<'a>() -> Lazy<'a, Self, A>
506	where
507		A: Monoid + Clone + 'a,
508	{
509		let thunk = Self::new_thunk(move |_| Monoid::empty());
510		Lazy::new(thunk)
511	}
512}
513
514// Note: LazyDefer is NOT implemented for ArcLazyConfig because the Defer trait
515// allows any FnBrand, but ArcLazy requires Send + Sync closures. Users should
516// use SendDefer instead for thread-safe deferred lazy evaluation.
517
518// =============================================================================
519// Type Aliases
520// =============================================================================
521
522/// Type alias for `Rc`-based `Lazy` values.
523///
524/// Use this for single-threaded lazy evaluation. Not thread-safe.
525pub type RcLazy<'a, A> = Lazy<'a, RcLazyConfig, A>;
526
527/// Type alias for `Arc`-based `Lazy` values.
528///
529/// Use this for thread-safe lazy evaluation. Requires `A: Send + Sync`.
530pub type ArcLazy<'a, A> = Lazy<'a, ArcLazyConfig, A>;
531
532// =============================================================================
533// LazyError
534// =============================================================================
535
536/// Error type for `Lazy` evaluation failures.
537///
538/// This error is returned when a thunk panics during evaluation.
539#[derive(Clone, Debug, Default, Error, PartialEq, Eq, PartialOrd, Ord, Hash)]
540#[error("thunk panicked during evaluation{}", .0.as_ref().map(|m| format!(": {m}")).unwrap_or_default())]
541pub struct LazyError(Option<Arc<str>>);
542
543impl LazyError {
544	/// Creates a `LazyError` from a panic payload.
545	///
546	/// ### Type Signature
547	///
548	/// `Box (dyn Any + Send) -> LazyError`
549	///
550	/// ### Parameters
551	///
552	/// * `payload`: The panic payload.
553	///
554	/// ### Returns
555	///
556	/// A new `LazyError`.
557	///
558	/// ### Examples
559	///
560	/// ```
561	/// use fp_library::types::*;
562	///
563	/// let payload = Box::new("oops");
564	/// let error = LazyError::from_panic(payload);
565	/// assert_eq!(error.to_string(), "thunk panicked during evaluation: oops");
566	/// ```
567	pub fn from_panic(payload: Box<dyn std::any::Any + Send + 'static>) -> Self {
568		let msg = if let Some(s) = payload.downcast_ref::<&str>() {
569			Some(Arc::from(*s))
570		} else {
571			payload.downcast_ref::<String>().map(|s| Arc::from(s.as_str()))
572		};
573		Self(msg)
574	}
575
576	/// Returns the panic message, if available.
577	///
578	/// ### Type Signature
579	///
580	/// `LazyError -> Option &str`
581	///
582	/// ### Returns
583	///
584	/// The panic message as a string slice, or `None` if no message was captured.
585	///
586	/// ### Examples
587	///
588	/// ```
589	/// use fp_library::types::*;
590	///
591	/// let payload = Box::new("oops");
592	/// let error = LazyError::from_panic(payload);
593	/// assert_eq!(error.panic_message(), Some("oops"));
594	/// ```
595	pub fn panic_message(&self) -> Option<&str> {
596		self.0.as_deref()
597	}
598}
599
600// =============================================================================
601// LazyInner and Lazy
602// =============================================================================
603
604struct LazyInner<'a, Config: LazyConfig, A: 'a> {
605	/// The memoized result (computed at most once).
606	/// Stores Result<A, Arc<LazyError>> to capture both successful values and errors.
607	once: <Config::OnceBrand as Once>::Of<Result<A, Arc<LazyError>>>,
608	/// The thunk, wrapped in ThunkWrapper::Cell for interior mutability.
609	thunk: <Config::PtrBrand as ThunkWrapper>::Cell<Config::ThunkOf<'a, A>>,
610}
611
612/// Represents a lazily-computed, memoized value with shared semantics.
613///
614/// `Lazy` stores a computation (a thunk) that is executed only when the value is needed.
615/// The result is then cached (memoized) so that subsequent accesses return the same value
616/// without re-executing the computation.
617///
618/// This `Lazy` type uses shared semantics: cloning the `Lazy` value shares the
619/// underlying memoization state. If one clone forces the value, all other
620/// clones see the result.
621///
622/// ### Type Parameters
623///
624/// * `Config`: The configuration for the `Lazy` value (e.g., `RcLazyConfig`, `ArcLazyConfig`).
625/// * `A`: The type of the value.
626///
627/// ### Configuration Choice
628///
629/// - Use [`RcLazy`] for single-threaded contexts
630/// - Use [`ArcLazy`] for thread-safe contexts (requires `A: Send + Sync`)
631pub struct Lazy<'a, Config: LazyConfig, A>(
632	// CloneableOf wraps LazyInner for shared ownership
633	<Config::PtrBrand as RefCountedPointer>::CloneableOf<LazyInner<'a, Config, A>>,
634);
635
636impl<'a, Config: LazyConfig, A> Lazy<'a, Config, A> {
637	/// Creates a new `Lazy` value from a thunk.
638	///
639	/// This method creates a new `Lazy` value that will evaluate the given thunk when forced.
640	///
641	/// ### Type Signature
642	///
643	/// `forall config a. config::ThunkOf a -> Lazy config a`
644	///
645	/// ### Parameters
646	///
647	/// * `thunk`: The thunk that produces the value.
648	///
649	/// ### Returns
650	///
651	/// A new `Lazy` value.
652	///
653	/// ### Examples
654	///
655	/// ```
656	/// use fp_library::types::lazy::*;
657	///
658	/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
659	/// assert_eq!(Lazy::force(&lazy).unwrap(), &42);
660	/// ```
661	pub fn new(thunk: Config::ThunkOf<'a, A>) -> Self {
662		let inner =
663			LazyInner { once: Config::OnceBrand::new(), thunk: Config::PtrBrand::new(Some(thunk)) };
664		Self(Config::PtrBrand::cloneable_new(inner))
665	}
666
667	/// Forces the evaluation of the thunk and returns the value.
668	///
669	/// If the value has already been computed, the cached value is returned.
670	/// If the computation panics, the panic is caught and returned as a `LazyError`.
671	/// Subsequent calls will return the same error.
672	///
673	/// ### Type Signature
674	///
675	/// `forall config a. Lazy config a -> Result (&a) LazyError`
676	///
677	/// ### Parameters
678	///
679	/// * `this`: The lazy value to force.
680	///
681	/// ### Returns
682	///
683	/// The computed value or an error.
684	///
685	/// ### Examples
686	///
687	/// ```
688	/// use fp_library::types::lazy::*;
689	///
690	/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
691	/// assert_eq!(Lazy::force(&lazy).unwrap(), &42);
692	/// ```
693	pub fn force(this: &Self) -> Result<&A, LazyError> {
694		let inner = &*this.0;
695		let result: &Result<A, Arc<LazyError>> =
696			<Config::OnceBrand as Once>::get_or_init(&inner.once, || {
697				let thunk = Config::PtrBrand::take(&inner.thunk)
698					.expect("unreachable: get_or_init guarantees single execution");
699				// Call thunk with () since it's Fn(()) -> A
700				std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| thunk(())))
701					.map_err(|payload| Arc::new(LazyError::from_panic(payload)))
702			});
703		result.as_ref().map_err(|e| (**e).clone())
704	}
705
706	/// Forces the evaluation of the thunk and returns the value, cloning it.
707	///
708	/// This is a convenience method that clones the value after forcing it.
709	///
710	/// ### Type Signature
711	///
712	/// `forall config a. Clone a => Lazy config a -> Result a LazyError`
713	///
714	/// ### Parameters
715	///
716	/// * `this`: The lazy value to force.
717	///
718	/// ### Returns
719	///
720	/// The computed value (cloned) or an error.
721	///
722	/// ### Examples
723	///
724	/// ```
725	/// use fp_library::types::lazy::*;
726	///
727	/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
728	/// assert_eq!(Lazy::force_cloned(&lazy).unwrap(), 42);
729	/// ```
730	pub fn force_cloned(this: &Self) -> Result<A, LazyError>
731	where
732		A: Clone,
733	{
734		Self::force(this).cloned()
735	}
736
737	/// Forces the evaluation of the thunk and returns the value, panicking if the thunk panics.
738	///
739	/// This method unwraps the result of `force`, propagating any panic that occurred during evaluation.
740	///
741	/// ### Type Signature
742	///
743	/// `forall config a. Clone a => Lazy config a -> a`
744	///
745	/// ### Parameters
746	///
747	/// * `this`: The lazy value to force.
748	///
749	/// ### Returns
750	///
751	/// The computed value.
752	///
753	/// ### Examples
754	///
755	/// ```
756	/// use fp_library::types::lazy::*;
757	///
758	/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
759	/// assert_eq!(Lazy::force_or_panic(&lazy), 42);
760	/// ```
761	pub fn force_or_panic(this: &Self) -> A
762	where
763		A: Clone,
764	{
765		match Self::force(this) {
766			Ok(v) => v.clone(),
767			Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
768		}
769	}
770
771	/// Forces the evaluation of the thunk and returns a reference to the value, panicking if the thunk panics.
772	///
773	/// This method unwraps the result of `force`, propagating any panic that occurred during evaluation.
774	///
775	/// ### Type Signature
776	///
777	/// `forall config a. Lazy config a -> &a`
778	///
779	/// ### Parameters
780	///
781	/// * `this`: The lazy value to force.
782	///
783	/// ### Returns
784	///
785	/// A reference to the computed value.
786	///
787	/// ### Examples
788	///
789	/// ```
790	/// use fp_library::types::lazy::*;
791	///
792	/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
793	/// assert_eq!(Lazy::force_ref_or_panic(&lazy), &42);
794	/// ```
795	pub fn force_ref_or_panic(this: &Self) -> &A {
796		match Self::force(this) {
797			Ok(v) => v,
798			Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
799		}
800	}
801
802	/// Returns true if the lazy value has been forced and panicked.
803	///
804	/// ### Type Signature
805	///
806	/// `forall config a. Lazy config a -> bool`
807	///
808	/// ### Parameters
809	///
810	/// * `this`: The lazy value to check.
811	///
812	/// ### Returns
813	///
814	/// `true` if the value has been forced and panicked, `false` otherwise.
815	///
816	/// ### Examples
817	///
818	/// ```
819	/// use fp_library::types::lazy::*;
820	///
821	/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| panic!("oops")));
822	/// let _ = Lazy::force(&lazy);
823	/// assert!(Lazy::is_poisoned(&lazy));
824	/// ```
825	pub fn is_poisoned(this: &Self) -> bool {
826		let inner = &*this.0;
827		if let Some(result) = <Config::OnceBrand as Once>::get(&inner.once) {
828			result.is_err()
829		} else {
830			false
831		}
832	}
833
834	/// Returns the error if the lazy value has been forced and panicked.
835	///
836	/// ### Type Signature
837	///
838	/// `forall config a. Lazy config a -> Option LazyError`
839	///
840	/// ### Parameters
841	///
842	/// * `this`: The lazy value to check.
843	///
844	/// ### Returns
845	///
846	/// The error if the value has been forced and panicked, `None` otherwise.
847	///
848	/// ### Examples
849	///
850	/// ```
851	/// use fp_library::types::lazy::*;
852	///
853	/// let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| panic!("oops")));
854	/// let _ = Lazy::force(&lazy);
855	/// assert!(Lazy::get_error(&lazy).is_some());
856	/// ```
857	pub fn get_error(this: &Self) -> Option<LazyError> {
858		let inner = &*this.0;
859		if let Some(Err(e)) = <Config::OnceBrand as Once>::get(&inner.once) {
860			Some(LazyError(e.0.clone()))
861		} else {
862			None
863		}
864	}
865}
866
867impl<'a, Config: LazyConfig, A: Debug> Debug for Lazy<'a, Config, A> {
868	fn fmt(
869		&self,
870		f: &mut Formatter<'_>,
871	) -> fmt::Result {
872		let inner = &*self.0;
873		f.debug_struct("Lazy")
874			.field("value", &<Config::OnceBrand as Once>::get(&inner.once))
875			.finish()
876	}
877}
878
879impl<'a, Config: LazyConfig, A: Clone> Clone for Lazy<'a, Config, A> {
880	fn clone(&self) -> Self {
881		Self(self.0.clone())
882	}
883}
884
885impl_kind! {
886	impl<Config: LazyConfig> for LazyBrand<Config> {
887		type Of<'a, A: 'a>: 'a = Lazy<'a, Config, A>;
888	}
889}
890
891// =============================================================================
892// Type Class Implementations
893// =============================================================================
894
895// Note: We do NOT implement TrySemigroup/TryMonoid explicitly for Lazy.
896// Since Lazy implements Semigroup/Monoid, it inherits the blanket impls from
897// TrySemigroup/TryMonoid which use Error = Infallible. Users should handle
898// errors via force() returning Result<&A, LazyError>.
899
900impl<'a, Config: LazySemigroup<A>, A: Semigroup + Clone + 'a> Semigroup for Lazy<'a, Config, A> {
901	/// Combines two lazy values.
902	///
903	/// The combination is itself lazy: the result is a new thunk that, when forced,
904	/// forces both input values and combines them.
905	///
906	/// ### Type Signature
907	///
908	/// `forall config a. Semigroup a => (Lazy config a, Lazy config a) -> Lazy config a`
909	///
910	/// ### Parameters
911	///
912	/// * `x`: The first lazy value.
913	/// * `y`: The second lazy value.
914	///
915	/// ### Returns
916	///
917	/// A new lazy value that combines the results.
918	///
919	/// ### Examples
920	///
921	/// ```
922	/// use fp_library::{functions::*, types::*};
923	///
924	/// let x = RcLazy::new(RcLazyConfig::new_thunk(|_| "Hello, ".to_string()));
925	/// let y = RcLazy::new(RcLazyConfig::new_thunk(|_| "World!".to_string()));
926	/// let z = append::<RcLazy<_>>(x, y);
927	/// assert_eq!(Lazy::force_or_panic(&z), "Hello, World!".to_string());
928	/// ```
929	fn append(
930		x: Self,
931		y: Self,
932	) -> Self {
933		Config::append(x, y)
934	}
935}
936
937impl<'a, Config: LazyMonoid<A>, A: Monoid + Clone + 'a> Monoid for Lazy<'a, Config, A> {
938	/// Returns the identity element.
939	///
940	/// This method returns a lazy value that evaluates to the underlying type's identity element.
941	///
942	/// ### Type Signature
943	///
944	/// `forall config a. Monoid a => () -> Lazy config a`
945	///
946	/// ### Returns
947	///
948	/// A lazy value containing the identity element.
949	///
950	/// ### Examples
951	///
952	/// ```
953	/// use fp_library::{functions::*, types::*};
954	///
955	/// let x = empty::<RcLazy<String>>();
956	/// assert_eq!(Lazy::force_or_panic(&x), "".to_string());
957	/// ```
958	fn empty() -> Self {
959		Config::empty()
960	}
961}
962
963impl<'a, Config: LazyDefer<'a, A>, A: Clone + 'a> Defer<'a> for Lazy<'a, Config, A> {
964	/// Creates a value from a computation that produces the value.
965	///
966	/// This method defers the construction of a `Lazy` value.
967	///
968	/// ### Type Signature
969	///
970	/// `forall config a. (() -> Lazy config a) -> Lazy config a`
971	///
972	/// ### Type Parameters
973	///
974	/// * `FnBrand`: The brand of the cloneable function wrapper.
975	///
976	/// ### Parameters
977	///
978	/// * `f`: A thunk (wrapped in a cloneable function) that produces the value.
979	///
980	/// ### Returns
981	///
982	/// A new lazy value.
983	///
984	/// ### Examples
985	///
986	/// ```
987	/// use fp_library::{brands::*, functions::*, types::lazy::*};
988	///
989	/// let lazy = defer::<RcLazy<i32>, RcFnBrand>(
990	///     cloneable_fn_new::<RcFnBrand, _, _>(|_| RcLazy::new(RcLazyConfig::new_thunk(|_| 42)))
991	/// );
992	/// assert_eq!(Lazy::force_or_panic(&lazy), 42);
993	/// ```
994	fn defer<FnBrand>(f: <FnBrand as CloneableFn>::Of<'a, (), Self>) -> Self
995	where
996		Self: Sized,
997		FnBrand: CloneableFn + 'a,
998	{
999		Config::defer::<FnBrand>(f)
1000	}
1001}
1002
1003use crate::classes::send_defer::SendDefer;
1004
1005impl SendDefer for LazyBrand<ArcLazyConfig> {
1006	/// Creates a deferred value from a thread-safe thunk.
1007	///
1008	/// ### Type Signature
1009	///
1010	/// `forall a. (Send a, Sync a) => (() -> ArcLazy a) -> ArcLazy a`
1011	///
1012	/// ### Type Parameters
1013	///
1014	/// * `A`: The type of the value.
1015	///
1016	/// ### Parameters
1017	///
1018	/// * `thunk`: The function that produces the value.
1019	///
1020	/// ### Returns
1021	///
1022	/// A deferred value.
1023	///
1024	/// ### Examples
1025	///
1026	/// ```
1027	/// use fp_library::{brands::*, functions::*, types::lazy::*};
1028	///
1029	/// let lazy = send_defer::<LazyBrand<ArcLazyConfig>, _, _>(|| ArcLazy::new(ArcLazyConfig::new_thunk(|_| 42)));
1030	/// assert_eq!(Lazy::force_or_panic(&lazy), 42);
1031	/// ```
1032	fn send_defer<'a, A>(thunk: impl 'a + Fn() -> Self::Of<'a, A> + Send + Sync) -> Self::Of<'a, A>
1033	where
1034		A: Clone + Send + Sync + 'a,
1035	{
1036		let thunk = ArcLazyConfig::new_thunk(move |_| {
1037			let inner_lazy = thunk();
1038			match Lazy::force(&inner_lazy) {
1039				Ok(v) => v.clone(),
1040				Err(e) => std::panic::resume_unwind(Box::new(e.to_string())),
1041			}
1042		});
1043		Lazy::new(thunk)
1044	}
1045}
1046
1047// =============================================================================
1048// Tests
1049// =============================================================================
1050
1051#[cfg(test)]
1052mod tests {
1053	use super::*;
1054	use crate::{
1055		brands::RcFnBrand,
1056		classes::{cloneable_fn::CloneableFn, defer::Defer},
1057	};
1058	use std::{cell::RefCell, rc::Rc};
1059
1060	/// Tests that `Lazy::force` memoizes the result.
1061	#[test]
1062	fn force_memoization() {
1063		let counter = Rc::new(RefCell::new(0));
1064		let counter_clone = counter.clone();
1065
1066		let lazy = RcLazy::new(<RcFnBrand as CloneableFn>::new(move |_| {
1067			*counter_clone.borrow_mut() += 1;
1068			42
1069		}));
1070
1071		assert_eq!(*counter.borrow(), 0);
1072		assert_eq!(Lazy::force(&lazy).unwrap(), &42);
1073		assert_eq!(*counter.borrow(), 1);
1074		assert_eq!(Lazy::force(&lazy).unwrap(), &42);
1075		// The new implementation uses shared semantics!
1076		// So cloning the Lazy should SHARE the OnceCell.
1077		let lazy_clone = lazy.clone();
1078		assert_eq!(Lazy::force(&lazy_clone).unwrap(), &42);
1079		assert_eq!(*counter.borrow(), 1); // Should still be 1
1080	}
1081
1082	/// Tests that `Lazy::defer` delays execution until forced.
1083	#[test]
1084	fn defer_execution_order() {
1085		let counter = Rc::new(RefCell::new(0));
1086		let counter_clone = counter.clone();
1087
1088		let lazy = RcLazy::defer::<RcFnBrand>(<RcFnBrand as CloneableFn>::new(move |_| {
1089			*counter_clone.borrow_mut() += 1;
1090			RcLazy::new(<RcFnBrand as CloneableFn>::new(|_| 42))
1091		}));
1092
1093		assert_eq!(*counter.borrow(), 0);
1094		assert_eq!(Lazy::force(&lazy).unwrap(), &42);
1095		assert_eq!(*counter.borrow(), 1);
1096	}
1097
1098	/// Tests that panics are caught and cached.
1099	#[test]
1100	fn panic_caching() {
1101		let counter = Rc::new(RefCell::new(0));
1102		let counter_clone = counter.clone();
1103
1104		let lazy = RcLazy::new(<RcFnBrand as CloneableFn>::new(move |_| {
1105			*counter_clone.borrow_mut() += 1;
1106			if *counter_clone.borrow() == 1 {
1107				panic!("oops");
1108			}
1109			42
1110		}));
1111
1112		assert!(Lazy::force(&lazy).is_err());
1113		assert_eq!(*counter.borrow(), 1);
1114		assert!(Lazy::force(&lazy).is_err());
1115		assert_eq!(*counter.borrow(), 1); // Should not re-execute
1116	}
1117
1118	/// Tests that `force_or_panic` returns the value on success.
1119	#[test]
1120	fn force_or_panic_success() {
1121		let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
1122		assert_eq!(Lazy::force_or_panic(&lazy), 42);
1123	}
1124
1125	/// Tests that `force_or_panic` propagates the panic on failure.
1126	#[test]
1127	#[should_panic(expected = "thunk panicked during evaluation: oops")]
1128	fn force_or_panic_failure() {
1129		let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| panic!("oops")));
1130		Lazy::force_or_panic(&lazy);
1131	}
1132
1133	/// Tests that `force_ref_or_panic` returns a reference to the value on success.
1134	#[test]
1135	fn force_ref_or_panic_success() {
1136		let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
1137		assert_eq!(Lazy::force_ref_or_panic(&lazy), &42);
1138	}
1139
1140	/// Tests that `force_ref_or_panic` propagates the panic on failure.
1141	#[test]
1142	#[should_panic(expected = "thunk panicked during evaluation: oops")]
1143	fn force_ref_or_panic_failure() {
1144		let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| panic!("oops")));
1145		Lazy::force_ref_or_panic(&lazy);
1146	}
1147
1148	/// Tests `is_poisoned` and `get_error` state transitions.
1149	#[test]
1150	fn is_poisoned_and_get_error() {
1151		let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| panic!("oops")));
1152		assert!(!Lazy::is_poisoned(&lazy));
1153		assert!(Lazy::get_error(&lazy).is_none());
1154
1155		let _ = Lazy::force(&lazy);
1156
1157		assert!(Lazy::is_poisoned(&lazy));
1158		let err = Lazy::get_error(&lazy).unwrap();
1159		assert_eq!(err.to_string(), "thunk panicked during evaluation: oops");
1160	}
1161
1162	/// Tests that `force_cloned` returns a cloned value.
1163	#[test]
1164	fn force_cloned() {
1165		let lazy = RcLazy::new(RcLazyConfig::new_thunk(|_| 42));
1166		assert_eq!(Lazy::force_cloned(&lazy).unwrap(), 42);
1167	}
1168
1169	/// Tests `Semigroup::append` for `Lazy`.
1170	#[test]
1171	fn semigroup_append() {
1172		let x = RcLazy::new(RcLazyConfig::new_thunk(|_| "Hello, ".to_string()));
1173		let y = RcLazy::new(RcLazyConfig::new_thunk(|_| "World!".to_string()));
1174		let z = Semigroup::append(x, y);
1175		assert_eq!(Lazy::force_or_panic(&z), "Hello, World!".to_string());
1176	}
1177
1178	/// Tests `Monoid::empty` for `Lazy`.
1179	#[test]
1180	fn monoid_empty() {
1181		let x = <RcLazy<String> as Monoid>::empty();
1182		assert_eq!(Lazy::force_or_panic(&x), "".to_string());
1183	}
1184
1185	/// Tests that `ArcLazy` is thread-safe.
1186	#[test]
1187	fn arc_lazy_thread_safety() {
1188		use std::sync::{Arc, Mutex};
1189		use std::thread;
1190
1191		let counter = Arc::new(Mutex::new(0));
1192		let counter_clone = counter.clone();
1193
1194		let lazy = ArcLazy::new(ArcLazyConfig::new_thunk(move |_| {
1195			let mut guard = counter_clone.lock().unwrap();
1196			*guard += 1;
1197			42
1198		}));
1199
1200		let lazy_clone = lazy.clone();
1201
1202		let handle = thread::spawn(move || Lazy::force_or_panic(&lazy_clone));
1203
1204		assert_eq!(handle.join().unwrap(), 42);
1205		assert_eq!(Lazy::force_or_panic(&lazy), 42);
1206		// Should only be computed once due to shared memoization
1207		assert_eq!(*counter.lock().unwrap(), 1);
1208	}
1209
1210	/// Tests `Semigroup::append` for `ArcLazy`.
1211	#[test]
1212	fn arc_lazy_semigroup_append() {
1213		let x = ArcLazy::new(ArcLazyConfig::new_thunk(|_| "Hello, ".to_string()));
1214		let y = ArcLazy::new(ArcLazyConfig::new_thunk(|_| "World!".to_string()));
1215		let z = Semigroup::append(x, y);
1216		assert_eq!(Lazy::force_or_panic(&z), "Hello, World!".to_string());
1217	}
1218
1219	/// Tests `Monoid::empty` for `ArcLazy`.
1220	#[test]
1221	fn arc_lazy_monoid_empty() {
1222		let x = <ArcLazy<String> as Monoid>::empty();
1223		assert_eq!(Lazy::force_or_panic(&x), "".to_string());
1224	}
1225}