chore: release fp-library v0.14.0#22
Merged
nothingnesses merged 112 commits intomainfrom Mar 29, 2026
Merged
Conversation
- Add `WithIndex` base trait to the Indexed type classes listing - Add `pipe` to the helper functions listing - Remove spurious `<I>` type parameters from `ParFunctorWithIndex` and `ParFoldableWithIndex` in the parallel trait tables
Distinguish the standard free monad from the freer monad encoding, correct attribution for the coroutine paper (Kawahara & Kameyama), clarify that bluefin uses capability passing rather than ReaderT IO, note that Effect (TypeScript) uses a fiber-based runtime not CPS, and add missing entries (fused-effects, ZIO) to the implementations table.
- Fix incorrect Send claim about Trampoline in README and lib.rs - Fix "run" -> "evaluate" and misleading print comment in trampoline.rs - Fix "to evaluable" -> "to evaluate" typo in evaluable.rs - Document From conversion costs involving clone - Add "Why there is no generic fix" section to deferrable.rs - Expand Evaluable trait doc with semantic contract
- Create pub(crate) utils module with panic_payload_to_string helper - Refactor TryThunk::catch_unwind and TryLazy::catch_unwind to use the shared helper instead of duplicated inline logic - Add TryTrampoline::catch_unwind for consistency with TryThunk and TryLazy, using the same helper
- QW-6: Deprecate TryThunk::pure in favor of ok - SC-3: Add inherent lift2 and then methods to TryThunk - MP-5: Add From<TryTrampoline> for TryThunk (without unnecessary Send) - MP-3: Add memoize() to TryThunk returning RcTryLazy - MP-6: Add memoize_arc() to TryThunk (evaluates eagerly since inner closure is not Send)
- MP-1: Remove unnecessary Send bound from From<Trampoline> for Thunk - MP-2: Remove unnecessary Send bounds from From<TryTrampoline> for RcTryLazy - QW-5: Add Foldable impl for LazyBrand<ArcLazyConfig> - SC-4: Add map and map_err methods to RcTryLazy and ArcTryLazy - MP-3: Add memoize() to TryTrampoline - MP-6: Add memoize_arc() to Thunk, Trampoline, and TryTrampoline (evaluates eagerly since inner closures are not Send)
- Add QuickCheck property tests for TryThunk (functor/monad laws, error short-circuit) - Add QuickCheck property tests for TryTrampoline (functor/monad laws, error short-circuit) - Add memoization and deferrable transparency tests for TryLazy - Add panic poisoning tests for RcLazy and RcTryLazy - Add ArcTryLazy thread safety test - Add ThunkBrand tail_rec_m stack safety test at depth 1M - Add TryTrampoline stack safety tests (tail_rec_m and bind chain)
Add #[inline] to trivial wrapper methods on Thunk, Lazy (both Rc and Arc variants), and TryThunk. Fix unnecessary clone in Lazy::ref_map for both RcLazy and ArcLazy by moving self directly into the closure.
Extend #[inline] coverage to TryThunk, TryTrampoline, and TryLazy wrapper methods. Add a Stack Safety section to Thunk struct docs explaining bind chain limitations and tail_rec_m/Trampoline alternatives.
Correct misleading claim that Thunk/Trampoline "re-run every time you call .evaluate()"; evaluate() takes self by value so each instance runs exactly once. Add comparison table rows for TryThunk, TryTrampoline, and TryLazy in both README.md and lib.rs.
Add catch_unwind for ArcTryLazy mirroring the RcTryLazy version. Fill test coverage for TryThunk lift2/then/memoize/memoize_arc and TryLazy map/map_err for both Rc and Arc variants. Add generalized catch_unwind_with accepting a custom panic payload converter on all Try* types.
Generalize catch_unwind on TryTrampoline, RcTryLazy, and ArcTryLazy to accept a custom Box<dyn Any + Send> -> E handler via catch_unwind_with. Existing catch_unwind methods now delegate using panic_payload_to_string.
Cover Thunk (map/bind chains), Trampoline (bind chains, tail_rec_m, vs iterative), Lazy (first-access, cached-access, ref_map chains for both Rc and Arc), and Free (left/right-associated binds, evaluate).
Add trybuild tests verifying that Thunk is !Send, Trampoline requires 'static, ArcLazy requires Send closures, and RcLazy is !Send.
Add resume() for step-by-step Free monad inspection, returning Result<A, F<Free<F, A>>>. Add fold_free() for interpreting Free into an arbitrary monad via NaturalTransformation. Add Map variant to FreeInner for efficient map chains without type-erasure roundtrip. Update Trampoline::map to use the new Map variant.
Delegate to lift2 for stack-safe combination of two Trampoline values using Semigroup::append. Add Trampoline::empty() wrapping Monoid::empty in Trampoline::pure.
Add associated type PointerBrand to LazyConfig trait, mapping RcLazyConfig to RcBrand and ArcLazyConfig to ArcBrand. Enables generic code to constrain pointer capabilities from a LazyConfig.
Add SendRefFunctor trait mirroring RefFunctor with Send bound on the mapping function, following the Deferrable/SendDeferrable pattern. Implement for LazyBrand<ArcLazyConfig> to give ArcLazy trait-level ref_map with thread-safe functions.
Remove unused ArcLazyConfig/RcLazyConfig imports from try_thunk, remove duplicate #[inline] attrs and simplify redundant closures in try_lazy, add #[document_parameters] to try_lazy impl blocks, fix NaturalTransformation doc attributes in free.
Add missing trait implementations, types, documentation, and tests across the lazy evaluation hierarchy based on a comprehensive audit of all 12 source files. New type: - SendThunk<'a, A>: thread-safe deferred computation (Send + 'a) Trait implementations: - RefFunctor, SendRefFunctor, Foldable, Semigroup, Monoid for TryLazy - Semigroup, Monoid, bimap for TryTrampoline - FunctorWithIndex, FoldableWithIndex for Thunk - Eq, Ord for Lazy - Deferrable for ArcLazy and ArcTryLazy Structural changes: - Split LazyConfig into LazyConfig + TryLazyConfig - Make SendDeferrable extend Deferrable as supertrait - Add brand type aliases (RcLazyBrand, ArcLazyBrand, etc.) - Add From conversions for ArcLazy/ArcTryLazy from Thunk/Trampoline Documentation: - Add stack safety warnings to fold_free, TryThunk, Thunk::tail_rec_m - Add panic docs to TryLazy::evaluate, rc_lazy_fix, arc_lazy_fix - Fix stale references (Runnable, Free::roll, eval test names) - Add "why no Functor" explanation to Lazy - Add algebraic properties, Traversable limitation notes to TryThunk - Add transparency law and examples to SendDeferrable - Add when-to-use guidance to all Try* types - Create docs/lazy-evaluation.md as user-facing reference - Update docs/architecture.md to link to new reference Tests: - HKT-level trait tests for Thunk (Foldable, lift2, apply, Evaluable) - Memoize/memoize_arc caching tests for Thunk and TryThunk - QuickCheck bifunctor, error-channel monad, Semigroup/Monoid laws - Thread safety test for TryThunk::memoize_arc - Trampoline stack safety stress tests (200k iterations) Project configuration: - Fix direnv usage: use direnv allow, never suppress errors - Make test output caching mandatory in CLAUDE.md
TrySendThunk<'a, A, E> wraps SendThunk<'a, Result<A, E>>, providing the Send counterpart to TryThunk with error-aware combinators. Inherent methods: new, ok, err, defer, pure, bind, map, map_err, bimap, catch, evaluate, lift2, then, memoize_arc, catch_unwind, catch_unwind_with. Trait impls: Deferrable, SendDeferrable, Semigroup, Monoid, Debug, From<TryThunk>, From<Result>, From<SendThunk>. Includes 36 unit tests covering all combinators, thread safety, memoize_arc with multiple threads, and From conversions.
ThunkBrand already has Extract, so it automatically becomes a Comonad. Extend wraps the function application in a new deferred computation. Includes property tests for comonad laws.
Each unique set of test arguments now gets its own independent cache keyed by md5 hash. Running just test -p fp-library foo no longer corrupts the full-suite cache. Remove the test-subset recipe since just test <args> handles both full and subset runs with caching.
Add ref_map as inherent methods on both TryLazy variants, mirroring the existing pattern on RcLazy/ArcLazy. Update RefFunctor and SendRefFunctor trait impls to delegate to the inherent methods.
Document monad laws, error short-circuiting behavior, and limitations
('static constraint, no HKT brand, !Send, not memoized). Model after
TryThunk and Trampoline documentation.
Split the single impl block into three sections documenting the actual minimal trait requirements of each method group: construction (needs only 'static), functor operations (needs Functor), and evaluation (needs Extract + Functor). Struct bounds remain Extract + Functor because the Drop impl requires Extract for stack-safe Suspend cleanup.
Factor the shared iterative logic from evaluate and resume into a to_view method that returns FreeStep::Done or FreeStep::Suspended. Rewrite evaluate and resume as thin wrappers over to_view. Add substitute_free which interprets Free<F, A> into Free<G, A> using a natural transformation F ~> Free<G>, without requiring MonadRec on the target (unlike fold_free).
Expose Free::resume on Trampoline and TryTrampoline as inherent methods, enabling step-by-step introspection of deferred computations without full evaluation.
Replace the recursive hoist_free implementation with one that uses lift_f and bind to defer each Suspend layer's transformation into the CatList. The actual work happens in evaluate's iterative loop, making the call stack O(1) regardless of Suspend depth. Add a 100k-depth stress test.
Remove the test-force recipe to prevent agents from bypassing the test cache. Update CLAUDE.md to instruct agents to check cached output before re-running tests. Fix stale cargo fmt reference.
…tors Remove the test_forever_vec_empty test which hung indefinitely (forever on Vec is genuinely non-terminating, not empty). Replace the doc example with OptionBrand which short-circuits via None. Fix while_some, until_some, while_m, and until_m doctests to use Cell for interior mutability instead of mutable captured variables (Fn closures cannot mutate captures).
Replace strong Rc/Arc clones captured in rc_lazy_fix and arc_lazy_fix closures with Rc::downgrade/Arc::downgrade. The closure captures a Weak reference; during evaluation, upgrade() succeeds because the outer reference is still alive. If dropped without evaluation, no cycle exists and memory is reclaimed. Remove memory leak caveats from documentation.
Add rc_try_lazy_fix and arc_try_lazy_fix for self-referential fallible lazy values, analogous to rc_lazy_fix/arc_lazy_fix. Use weak references from the start to prevent memory leaks on drop-without-evaluation. Include tests for basic functionality, memoization, and thread safety.
Add evaluate_owned(&self) -> A where A: Clone to RcLazy, ArcLazy, RcTryLazy, and ArcTryLazy. Eliminates the common .evaluate().clone() pattern.
Both types use an iterative loop that calls the step function by shared reference, so Clone is not needed. This reduces friction for callers with non-Clone closures.
Add QuickCheck tests verifying SendDeferrable transparency and nesting laws for SendThunk, ArcLazy, TrySendThunk, and ArcTryLazy. Add functor identity/composition and monad identity/associativity law tests for SendThunk's inherent map and bind methods.
- Add MonadRec equivalence law and nondeterministic termination caveat to trait documentation. - Remove stale Step reference from lib.rs crate docs. - Move prop_extract_map test from extract.rs to thunk.rs (it tests a Comonad law, not an Extract law). - Add duplicate as a default trait method on Extend alongside the existing free function.
Add comments explaining why Extract cannot be relaxed on Free (Drop requires matching struct bounds), why substitute_free is stack-safe (bind defers recursion to CatList), and why LazyBrand cannot implement Extend (requires Functor, which requires owned A).
Update README.md, lib.rs, lazy-evaluation.md, and benchmarking.md: - Replace Evaluable references with Extract. - Add Extend, Comonad, LazyConfig, TryLazyConfig, FreeStep to listings. - Remove stale Step references, add missing SendThunk/TrySendThunk. - Fix ResultWithErrBrand to ResultErrAppliedBrand in benchmarking.md. - Add missing benchmark docs for Pair, Lazy Evaluation, CatList. - Update benchmark commands to use just recipes. - Correct SendThunk/TrySendThunk stack safety in lazy-evaluation.md. - Alphabetize brands.rs entries. - Ensure README.md and lib.rs feature lists are consistent.
Integrate treefmt-nix and git-hooks.nix into the Nix flake to provide unified formatting and pre-commit/pre-push checks: - treefmt orchestrates rustfmt, nixfmt, prettier, and tombi in one pass - Pre-commit hook runs treefmt (formatting check) - Pre-push hook runs clippy and cargo doc (warnings as errors) - just fmt now calls treefmt instead of cargo fmt alone - just clippy and just doc now treat warnings as errors - CI fmt job uses treefmt (checks all file types, not just Rust) - Upgrade actions/checkout from v4 to v6 in CI and release workflows - Add .git-blame-ignore-revs for ignoring formatting commits - Remove plans/lazy/ (implementation complete) - Regenerate UI test .stderr files for shifted line numbers - Apply treefmt to all existing files (markdown, TOML, YAML, Nix)
Add suffix-based Extend implementations for Vec and CatList, matching PureScript's Array instance. extend(f, collection) applies f to each suffix of the collection. Add Clone bound to Extend trait (required for collection types to create suffix copies). Update existing Identity and Thunk impls to match. Add property tests for associativity law.
Add reciprocal cross-references: Semimonad mentions Extend as its dual, Monad mentions Comonad as its dual. Reword Deferrable/Extract from "dual" to "inverse" (they operate at different abstraction levels, not a categorical duality) and clarify that Deferrable is value-level while Extract is brand-level.
Add MonadPlus as a blanket impl marker trait combining Monad and Alternative. Documents the distributivity law (bind distributes over alt) and left-zero law (bind on empty produces empty). VecBrand and CatListBrand satisfy both laws; OptionBrand satisfies left-zero only (its alt picks the first Some, breaking distributivity). Add QuickCheck property tests for both laws.
Move extend_flipped, compose_co_kleisli, and compose_co_kleisli_flipped from free-function-only to default trait methods (like duplicate). Free functions now delegate to the trait methods. This allows implementors to override them for efficiency. Enable document_module validation on Extend and add doc examples to all trait methods and free functions. Fix RUSTDOCFLAGS ordering in justfile and disambiguate extend doc links.
Left-zero (bind(empty(), f) == empty()) follows from Monad + Alternative and was removed as a separate law in PureScript when MonadZero was deprecated. Keep it documented as a "this also holds" note rather than a required law.
Clippy, doc, bench, and deny CI jobs now use just recipes via nix develop, ensuring CI uses the same flags as local development (e.g., -D warnings on clippy, unicode check on doc). Test job remains direct cargo for feature matrix support.
Remove hardcoded --all-features from just test so it accepts arbitrary feature flags via args. just verify now passes --all-features explicitly. CI test matrix now delegates to just test with per-matrix feature flags, making the justfile the single source of truth for all jobs.
Check IN_NIX_SHELL to determine if the Nix environment is already active (set by nix develop and direnv). When set, skip the direnv prefix since the toolchain is already available. Fixes CI failures where direnv is not installed in the GitHub Actions runner.
4609329 to
14bdc8b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Release fp-library v0.14.0 with breaking changes and significant new functionality.
Highlights
Extend,Comonad(Extend + Extract),MonadPlus(Monad + Alternative),Extract(renamed fromEvaluable)Stepreplaced bycore::ops::ControlFlowwith swapped-parameter HKT brandsFreemonad refactored to CatList-paired representation (eliminates Bind-on-Pure nesting, uniformly O(1) bind)MonadReccombinators:forever,while_some,until_some,repeat_m,while_m,until_mExtendfor collections:VecBrandandCatListBrandimplement suffix-basedExtendBreaking changes
Evaluablerenamed toExtract(methodevaluate->extract)Stepenum removed (usecore::ops::ControlFlow)StepBrand/StepLoopAppliedBrand/StepDoneAppliedBrandrenamed toControlFlowBrand/ControlFlowBreakAppliedBrand/ControlFlowContinueAppliedBrandTrySendThunkBrandremovedExtendtrait requiresA: CloneSee
fp-library/CHANGELOG.mdfor the full list.Test plan