Skip to main content

Crate ordofp

Crate ordofp 

Source
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:

  1. HLists (heterogeneously-typed lists)
  2. NominataUniversalis, and Universalis
  3. Disiunctio
  4. Probatum (accumulator for Result)
  5. Semigroup
  6. 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:

  1. Source on Github
  2. Crates.io page

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 HList data structures, implementations, and typeclasses.
indices
Types used for indexing into HLists and 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
OrdoFP Nexus: 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 ordofp experience.
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 Result that 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 HList of 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 HList based 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 NonEmpty lists.
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§

NominataUniversalis
Derives a Universalis instance based on Field + HList for a given Struct (Tuple Structs not supported because they have no labels)
Universalis
Derives a Universalis instance based on HList for a given Struct or Tuple Struct