17 releases (5 breaking)
new 0.6.4 | Apr 18, 2025 |
---|---|
0.6.3 | Apr 17, 2025 |
0.5.4 | Mar 24, 2025 |
0.4.0 | Feb 26, 2025 |
0.1.0 | Feb 4, 2025 |
#152 in Data structures
523 downloads per month
1MB
11K
SLoC
Rustica
Rustica is a comprehensive functional programming library for Rust, bringing powerful abstractions from category theory and functional programming to the Rust ecosystem. It provides a rich set of type classes, data types, and utilities commonly found in functional programming languages.
Overview
Rustica enables idiomatic functional programming in Rust by providing:
- Type Classes: Core abstractions like
Functor
,Applicative
, andMonad
- Data Types: Common functional data structures like
Maybe
,Either
,Choice
, andIO
- Monad Transformers: Powerful composition with
StateT
,ReaderT
, and more - Composable APIs: Tools for function composition and transformation
- Pure Functional Style: Patterns for immutable data and explicit effect handling
- Error Handling: Functional error handling utilities that work across different types
Whether you're coming from Haskell, Scala, or other functional languages, or just want to explore functional programming in Rust, Rustica provides the tools you need.
Getting Started
Add Rustica to your Cargo.toml
:
[dependencies]
rustica = "0.6.4"
If you want to use async features, add the async
feature:
[dependencies]
rustica = { version = "0.6.4", features = ["async"] }
If you want to use persistent vector collections, add the pvec
feature:
[dependencies]
rustica = { version = "0.6.4", features = ["pvec"] }
You can combine multiple features as needed:
[dependencies]
rustica = { version = "0.6.4", features = ["full"] }
Then import the prelude to get started:
use rustica::prelude::*;
Features
Type Classes
Rustica implements a wide range of type classes from category theory:
-
Basic Abstractions
Functor
- For mapping over contained valuesApplicative
- For applying functions in a contextMonad
- For sequential computationsPure
- For lifting values into a contextIdentity
- For accessing values inside contextsAlternative
- For choice between computations
-
Algebraic Structures
Semigroup
- Types with an associative binary operationMonoid
- Semigroups with an identity elementFoldable
- For reducing structuresTraversable
- For structure-preserving transformations
-
Advanced Concepts
Bifunctor
- For mapping over two type parametersContravariant
- For reversing function applicationCategory
- For abstract compositionArrow
- For generalized computationComonad
- For context-aware computationsMonadError
- For error handling in monadic contexts
Data Types
Rustica provides a rich collection of functional data types:
-
Core Types
Maybe<T>
- For optional values (likeOption<T>
)Either<L, R>
- For values with two possibilitiesId<T>
- The identity monadValidated<E, T>
- For accumulating validation errorsChoice<T>
- For representing non-deterministic computations with alternatives
-
Effect Types
IO<A>
- For pure I/O operationsState<S, A>
- For stateful computations with thread-safe implementationsReader<E, A>
- For environment-based computationsWriter<W, A>
- For logging operationsCont<R, A>
- For continuation-based programmingAsyncMonad<A>
- For asynchronous operations
-
Special Purpose
- Various wrapper types (
First
,Last
,Min
,Max
, etc.)
- Various wrapper types (
-
Persistent Collections
- PersistentVector: An efficient immutable vector with structural sharing and small vector optimization
-
Transformers
StateT<S, M, A>
- State monad transformer for combining state with other effectsReaderT<E, M, A>
- Reader monad transformer for combining environment with other effectsWriterT<W, M, A>
- Writer monad transformer for combining logging with other effects- Bidirectional conversion between monads and their transformer versions
-
Optics
Lens
- For focusing on parts of structuresPrism
- For working with sum types
Error Handling Utilities
Rustica provides standardized error handling utilities that work across different functional types:
-
Core Functions
sequence
- Combines a collection ofResult
values into a singleResult
containing a collectiontraverse
- Applies a function that produces aResult
to a collection, returning a singleResult
traverse_validated
- Liketraverse
but collects all errors instead of failing fast
-
Type Conversion
ResultExt
trait - ExtendsResult
with methods liketo_validated()
andto_either()
WithError
trait - Generic trait for any type that can represent error states- Conversion functions between
Result
,Either
, andValidated
-
Error Types
AppError<M, C>
- A structured error type that provides both a message and optional context- Helper functions like
error()
anderror_with_context()
Persistent Collections
Rustica includes a high-performance, immutable persistent vector:
- PersistentVector: An efficient immutable vector with structural sharing, implemented as a Relaxed Radix Balanced (RRB) tree. Provides:
- Fast random access and updates (O(log n))
- Small vector optimization for memory efficiency
- Structural sharing for efficient cloning and branching
- Customizable cache policies for advanced use cases
pvec![]
macro for convenient construction
Writer Monad with Persistent Logs
The Writer
monad now uses PersistentVector
for log accumulation, ensuring efficient, immutable logs with structural sharing:
- Writer<W, A>: Accumulates logs of type
W
alongside computations - Efficient log accumulation and sharing
Improved Functor Trait
- Blanket implementation for all Map trait implementers
- Ownership-aware API:
fmap_owned
,replace_owned
,void_owned
- Performance:
#[inline]
on all methods - Improved documentation and law examples
Benchmarks and Documentation
- Comprehensive benchmarks for all data types (see benchmarks page)
- GitHub Pages documentation site: https://but212.github.io/rustica/
Changelog
See CHANGELOG.md for a complete list of recent changes and enhancements.
Examples
Basic Usage
use rustica::datatypes::validated::Validated;
let valid: Validated<&str, i32> = Validated::valid(42);
assert!(valid.is_valid());
let invalid: Validated<&str, i32> = Validated::invalid("error");
assert!(invalid.is_invalid());
Conversion from Result
use rustica::datatypes::validated::Validated;
let result: Result<i32, &str> = Ok(42);
let validated = Validated::from_result(&result);
assert_eq!(validated, Validated::valid(42));
let error_result: Result<i32, &str> = Err("error");
let validated = Validated::from_result(&error_result);
assert_eq!(validated, Validated::invalid("error"));
Conversion from Option
use rustica::datatypes::validated::Validated;
let some_value: Option<i32> = Some(42);
let validated: Validated<&str, i32> = Validated::from_option(&some_value, &"missing value");
assert_eq!(validated, Validated::valid(42));
let none_value: Option<i32> = None;
let validated: Validated<&str, i32> = Validated::from_option(&none_value, &"missing value");
assert_eq!(validated, Validated::invalid("missing value"));
Accumulating Errors from a Collection
use rustica::utils::error_utils::traverse_validated;
use rustica::datatypes::validated::Validated;
// Define a fallible parsing function
let parse_int = |s: &str| -> Result<i32, String> {
s.parse::<i32>().map_err(|_| format!("'{}' is not a valid number", s))
};
// Process a collection with multiple errors
let inputs: Vec<&str> = vec!["1", "not_a_number", "3", "also_not_a_number"];
let result: Validated<String, Vec<i32>> = traverse_validated(inputs, parse_int);
// Verify that the result is invalid and contains all errors
assert!(result.is_invalid());
assert_eq!(result.errors().len(), 2);
assert!(result.errors()[0].contains("not_a_number"));
assert!(result.errors()[1].contains("also_not_a_number"));
// Process a collection with no errors
let valid_inputs: Vec<&str> = vec!["1", "2", "3"];
let valid_result: Validated<String, Vec<i32>> = traverse_validated(valid_inputs, parse_int);
assert!(valid_result.is_valid());
assert_eq!(valid_result.unwrap(), vec![1, 2, 3]);
Mapping Over Errors
use rustica::datatypes::validated::Validated;
let invalid: Validated<&str, i32> = Validated::invalid("error");
let mapped = invalid.fmap_invalid(|e| format!("Error: {}", e));
assert_eq!(mapped, Validated::invalid("Error: error".to_string()));
Inspiration
Rustica is inspired by functional programming libraries in other languages:
- Haskell's standard library
- Scala's Cats
- Kotlin's Arrow
- TypeScript's fp-ts
Contributing
Contributions are welcome! Check the TODO list for areas that need work.
License
Rustica is licensed under the Apache License, version 2.0. See the LICENSE file for details.
Documentation
For detailed documentation, please visit docs.rs/rustica
Dependencies
~1.5–10MB
~94K SLoC