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.
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 - Categorical Composition: Category-theoretic function composition via Category and Arrow traits
- Pure Functional Style: Patterns for immutable data and explicit effect handling
- Error Handling: Functional error handling utilities that work across different types
Excellent for:
- Learning functional programming concepts
- Prototyping and research
- Educational purposes
- Small-scale applications
Avoid for:
- Performance-critical production code
- Real-time systems
- Game engines
- High-throughput web servers
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 for learning and experimentation.
Add Rustica to your Cargo.toml:
[dependencies]
rustica = "0.10.1"If you want to use async features, add the async feature:
[dependencies]
rustica = { version = "0.10.1", features = ["async"] }Persistent vector collections are now included by default in Rustica 0.10.0.
You can combine multiple features as needed:
[dependencies]
rustica = { version = "0.10.1", features = ["full"] }Then import the prelude to get started:
use rustica::prelude::*;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 contextAlternative- 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
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<T>- 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 effects- Bidirectional conversion between monads and their transformer versions
-
Optics
Lens- For focusing on parts of structuresPrism- For working with sum typesIsoLens- Lawful, composable lenses based on isomorphisms for deep focusingIsoPrism- Lawful, composable prisms based on isomorphisms for sum types
Rustica provides standardized error handling utilities that work across different functional types:
-
Core Functions
sequence- Combines a collection ofResultvalues into a singleResultcontaining a collectiontraverse- Applies a function that produces aResultto a collection, returning a singleResulttraverse_validated- Liketraversebut collects all errors instead of failing fast
-
Type Conversion
ResultExttrait - ExtendsResultwith methods liketo_validated()andto_either()WithErrortrait - 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()
Rustica provides an immutable persistent vector (RRB-Tree) for functional programming patterns. This is included by default in Rustica 0.10.0.
Example Usage
use rustica::pvec::PersistentVector;
use rustica::pvec::pvec;
let v1: PersistentVector<i32> = pvec![1, 2, 3, 4, 5];
let v2 = v1.push_back(6);
let v3 = v1.update(0, 10);
assert_eq!(v1.get(0), Some(&1));
assert_eq!(v2.get(5), Some(&6));
assert_eq!(v3.get(0), Some(&10));Rustica uses GitHub Actions for continuous integration, formatting, linting, and automated publishing to crates.io on tagged releases.
- Tests and formatting are run on every push and pull request.
- When a tag (e.g.
v0.10.1) is pushed, the version is checked and, if not already published, is automatically uploaded to crates.io.
See CHANGELOG.md for a complete list of recent changes and enhancements.
use rustica::prelude::*;
// Working with Maybe (like Option)
let maybe_value = Maybe::Just(42);
let doubled = maybe_value.fmap(|x| x * 2);
assert_eq!(doubled.unwrap(), 84);
// Working with Either for error handling
let result: Either<String, &str> = Either::Right("success");
let processed = result.fmap(|s| s.to_uppercase());
assert_eq!(processed.unwrap(), "SUCCESS");
// Using Choice for multiple alternatives
let choices = Choice::new(1, [2, 3]);
let results = choices.fmap(|x| x * 2);
assert_eq!(results.iter().collect::<Vec<_>>(), vec![&2, &4, &6]);use rustica::datatypes::state::State;
// A simple counter
let counter = State::new(|count: i32| (count + 1, count));
// Run the state computation
let (new_count, result) = counter.run_state(0);
assert_eq!(new_count, 1);
assert_eq!(result, 0);use rustica::datatypes::io::IO;
// Pure IO description
let read_line = IO::new(|| "Hello from IO!".to_string());
// Execute the IO operation
let result = read_line.run();
assert_eq!(result, "Hello from IO!");Rustica is inspired by functional programming libraries in other languages:
- Haskell's standard library
- Scala's Cats
- Kotlin's Arrow
- TypeScript's fp-ts
Rustica is licensed under the Apache License, version 2.0. See the LICENSE file for details.
For detailed documentation, please visit docs.rs/rustica