Expand description
OrdoFP: a functional programming toolbelt for Rust
A collection of functional programming abstractions implemented in Rust in effective, useful, and idiomatic ways. Highlights include:
HLists(heterogeneously-typed lists)NominataUniversalis, and Universalis- Disiunctio
- Probatum (accumulator for Result)
- Semigroup
- Monoid
Here is a small taste of what OrdoFP has to offer:
use ordofp::prelude::*;
use ordofp::{self, hlist, coniunctio_pat, NominataUniversalis, monoid, Compositio, Universalis};
// Combining Monoids
let v = vec![Some(1), Some(3)];
assert_eq!(monoid::combine_all(&v), Some(4));
// HLists
let h = hlist![1, "hi"];
assert_eq!(h.len(), 2);
let coniunctio_pat!(a, b) = h;
assert_eq!(a, 1);
assert_eq!(b, "hi");
let h1 = hlist![Some(1), 3.3, 53i64, "hello".to_owned()];
let h2 = hlist![Some(2), 1.2, 1i64, " world".to_owned()];
let h3 = hlist![Some(3), 4.5, 54, "hello world".to_owned()];
assert_eq!(h1.combine(&h2), h3);
// Universalis and NominataUniversalis-based programming
// Allows Structs to play well easily with HLists
#[derive(Universalis, NominataUniversalis)]
struct ApiUser<'a> {
FirstName: &'a str,
LastName: &'a str,
Age: usize,
}
#[derive(Universalis, NominataUniversalis)]
struct NewUser<'a> {
first_name: &'a str,
last_name: &'a str,
age: usize,
}
#[derive(NominataUniversalis)]
struct SavedUser<'a> {
first_name: &'a str,
last_name: &'a str,
age: usize,
}
// Instantiate a struct from an HList. Note that you can go the other way too.
let a_user: ApiUser = ordofp::from_universalis(hlist!["Joe", "Blow", 30]);
// Convert using Universalis
let n_user: NewUser = Universalis::convert_from(a_user); // done
// Convert using NominataUniversalis
//
// This will fail if the fields of the types converted to and from do not
// have the same names or do not line up properly :)
//
// Also note that we're using a helper method to avoid having to use universal
// function call syntax
let s_user: SavedUser = ordofp::nominata_convert_from(n_user);
assert_eq!(s_user.first_name, "Joe");
assert_eq!(s_user.last_name, "Blow");
assert_eq!(s_user.age, 30);
// Uh-oh ! last_name and first_name have been flipped!
#[derive(NominataUniversalis)]
struct DeletedUser<'a> {
last_name: &'a str,
first_name: &'a str,
age: usize,
}
// let d_user = <DeletedUser as NominataUniversalis>::convert_from(s_user); <-- this would fail at compile time :)
// This will, however, work, because we make use of the Sculptor type-class
// to type-safely reshape the representations to align/match each other.
let d_user: DeletedUser = ordofp::transform_from(s_user);
assert_eq!(d_user.first_name, "Joe");§Transfiguring
Sometimes you need might have one data type that is “similar in shape” to another data type, but it
is similar recursively (e.g. it has fields that are structs that have fields that are a superset of
the fields in the target type, so they are transformable recursively). .transform_from can’t
help you there because it doesn’t deal with recursion, but the Transfigurator can help if both
are NominataUniversalis by transfigure()ing from one to the other.
What is “transfiguring”? In this context, it means to recursively transform some data of type A into data of type B, in a typesafe way, as long as A and B are “similarly-shaped”. In other words, as long as B’s fields and their subfields are subsets of A’s fields and their respective subfields, then A can be turned into B.
As usual, the goal with OrdoFP is to do this:
- Using stable (so no specialisation, which would have been helpful, methinks)
- Typesafe
- No usage of
unsafe
Here is an example:
use ordofp::NominataUniversalis;
use ordofp::labelled::Transfigurator;
#[derive(NominataUniversalis)]
struct InternalPhoneNumber {
emergency: Option<usize>,
main: usize,
secondary: Option<usize>,
}
#[derive(NominataUniversalis)]
struct InternalAddress<'a> {
is_whitelisted: bool,
name: &'a str,
phone: InternalPhoneNumber,
}
#[derive(NominataUniversalis)]
struct InternalUser<'a> {
name: &'a str,
age: usize,
address: InternalAddress<'a>,
is_banned: bool,
}
#[derive(NominataUniversalis, PartialEq, Debug)]
struct ExternalPhoneNumber {
main: usize,
}
#[derive(NominataUniversalis, PartialEq, Debug)]
struct ExternalAddress<'a> {
name: &'a str,
phone: ExternalPhoneNumber,
}
#[derive(NominataUniversalis, PartialEq, Debug)]
struct ExternalUser<'a> {
age: usize,
address: ExternalAddress<'a>,
name: &'a str,
}
let internal_user = InternalUser {
name: "John",
age: 10,
address: InternalAddress {
is_whitelisted: true,
name: "somewhere out there",
phone: InternalPhoneNumber {
main: 1234,
secondary: None,
emergency: Some(5678),
},
},
is_banned: true,
};
/// Boilerplate-free conversion of a top-level InternalUser into an
/// ExternalUser, taking care of subfield conversions as well.
let external_user: ExternalUser = internal_user.transfigure();
let expected_external_user = ExternalUser {
name: "John",
age: 10,
address: ExternalAddress {
name: "somewhere out there",
phone: ExternalPhoneNumber {
main: 1234,
},
}
};
assert_eq!(external_user, expected_external_user);Links:
Re-exports§
pub use crate::hlist::lift_from;pub use crate::hlist::Coniunctio;pub use crate::hlist::Nihil;pub use crate::traits::Func;pub use crate::traits::Poly;pub use crate::traits::ToMut;pub use crate::traits::ToRef;pub use crate::disiunctio::Absurdum;pub use crate::disiunctio::Disiunctio;pub use ordofp_core::universalis::Universalis;pub use ordofp_core::universalis::convert_from;pub use ordofp_core::universalis::from_universalis;pub use ordofp_core::universalis::into_universalis;pub use ordofp_core::universalis::map_inter;pub use ordofp_core::universalis::map_repr;pub use crate::labelled::NominataUniversalis;pub use crate::labelled::from_labelled_universalis;pub use crate::labelled::into_labelled_universalis;pub use crate::labelled::nominata_convert_from;pub use crate::labelled::transform_from;pub use crate::semigroup::Compositio;pub use crate::monoid::Unitas;pub use crate::validated::IntoProbatum;pub use crate::validated::Probatum;pub use crate::typeclasses::Applicatio;pub use crate::typeclasses::Apply;pub use crate::typeclasses::Functor;pub use crate::typeclasses::Monad;pub use crate::zipper::Zipper;
Modules§
- __alloc
- The Rust core allocation and collections library
- alternative
- Alternative type class for applicative functors with choice.
- arena
- Arena-Based Effect Handlers
- async_
core - Async Core Module - Asynchronous functional programming primitives.
- bifunctor
- Bifunctor type class for types with two type parameters.
- category
- Category Theory Foundations
- comonad
- Comonad type class - dual of Monad.
- datatypes
- Core Datatypes
- dependent
- Dependent Type Foundations - Typus Dependens
- diagnostics
- Diagnostics Module for
OrdoFP - disiunctio
- Module that holds Disiunctio (coproduct) data structures, traits, and implementations
- distributed
- Distributed Execution
- easy
- Easy API - Simplified Interface for
OrdoFP - effects
- Effect System Foundation - Lightweight algebraic effect infrastructure
- ffi_
bedrock - FFI Bedrock
- fix
- Fixed point of a functor.
- foldable
- Foldable type class for data structures that can be folded.
- foldl
- Composable Left Folds (Hadolint Pattern)
- free
- Free Monads and Tagless Final - DSL Interpreters
- gat
- GAT-based Type Classes for Higher Kinded Types simulation.
- hints
- Branch Prediction Hints
- hlist
- Module that holds
HListdata structures, implementations, and typeclasses. - indices
- Types used for indexing into
HListsand Disiunctiones. - labelled
- This module holds the machinery behind
NominataUniversalis. - linear
- Linear Types Module - Resource-safe programming with single-use guarantees
- metrics
- Effect Metrics - Performance Monitoring
- monoid
- Module for holding Unitas (Monoid) typeclass definitions and default implementations
- nexus
OrdoFPNexus: Efficient Type-Level Effect System- nonempty
NonEmpty- A non-empty list type.- optics
- Optics for
OrdoFP - par
ParFlumen— bulk, data-parallel collection semantics.- path
- Holds models, traits, and logic for Universalis traversal of models
- pfds
- Persistent Functional Data Structures (PFDS)
- prelude
- Traits that need to be imported for the complete
ordofpexperience. - quantitative
- Quantitative Types Module - Type-level multiplicity tracking
- recursion
- Recursion Schemes - Structured recursion combinators.
- refined
- Refinement Types
- rows
- Row Polymorphism - Extensible Records and Variants
- semigroup
- Module for holding the Compositio (Semigroup) typeclass definition and typeclass instances
- specialization
- Compile-Time Specialization Hints
- supervision
- Supervision Trees - Structured Fault Tolerance
- tailrec
- Tail recursion optimization for stack-safe recursive computations.
- tracing
- Effect Tracing - Causal Observability
- traits
- Traits that provide Universalis functionality for multiple types in ordofp
- transformers
- Monad Transformers for composing monadic effects.
- traversable
- Traversable type class for effectful iteration.
- typeclasses
- Type classes for algebraic structures and higher-kinded type simulation.
- universalis
- This module holds the machinery behind
Universalis. - validated
- Probatum — accumulating validation (a
Resultthat collects all errors instead of short-circuiting on the first). - vernacular
- Vernacular API - English Aliases for
OrdoFP - wrappers
- Wrapper types for alternative Compositio/Unitas behaviors.
- zipper
- Zipper: A cursor into a list with cheap focus operations
Macros§
- Disiunctio
- Returns a type signature for a Disiunctio of the provided types.
- HList
- Returns a type signature for an
HListof the provided types - Path
- Type-level path macro
- assert_
align - Assert that a type has a specific alignment at compile time.
- assert_
size - Assert that a type has a specific size at compile time.
- assert_
zst - Assert that a type is zero-sized at compile time.
- chain_
async - Chain async functions from left to right, returning a composed function.
- cold_
panic - A cold-path panic that ensures the panic logic is out-of-line.
- cold_
path - Mark a function as cold (rarely called).
- compose
- Compose functions from right to left.
- compose_
async - Compose async functions from right to left.
- coniunctio_
pat - Macro for pattern-matching on
HLists. - constant
- Create a constant function that ignores its argument.
- curry2
- Curry a function of 2 arguments.
- curry3
- Curry a function of 3 arguments.
- effect_
row - Macro for creating effect row types.
- field
- Used for creating a Field
- flip
- Flip the arguments of a binary function.
- functio_
poly - Returns a polymorphic function for use with mapping/folding heterogeneous types.
- hlist
- Returns an
HListbased on the values passed in. - mdo
- Monadic do-notation for composing monadic operations.
- mdo_
async - Async monadic do-notation for composing async monadic operations.
- nonempty
- Macro for creating
NonEmptylists. - path
- Build a Universalis path that can be used for traversals
- path_
type - Build the type of a Universalis path, for use in type position
(where
path!builds the value). - pipe
- Pipe a value through a series of functions from left to right.
- pipe_
async - Pipe a value through a series of async functions from left to right.
- unlikely_
panic - A panic that is marked as unlikely and cold.
- wrap_
pure - Foundation for all FFI wrappers. Defines the basic structure and pointer management.
- wrap_
ref - Defines a non-owning reference wrapper for a FFI type.
Derive Macros§
- Nominata
Universalis - Derives a Universalis instance based on Field +
HListfor a given Struct (Tuple Structs not supported because they have no labels) - Universalis
- Derives a Universalis instance based on
HListfor a given Struct or Tuple Struct