|
| 1 | +# Deep Causality HAFT |
| 2 | + |
| 3 | +**HAFT: Higher-Order Abstract Functional Traits** |
| 4 | + |
| 5 | +`deep_causality_haft` is a sub-crate of the `deep_causality` project, providing traits for Higher-Kinded Types (HKTs) in Rust. This enables writing generic, abstract code that can operate over different container types like `Option<T>` and `Result<T, E>`. |
| 6 | + |
| 7 | +## What are Higher-Kinded Types? |
| 8 | + |
| 9 | +In Rust, types like `Option<T>` and `Vec<T>` are generic over a type `T`. We can think of `Option` and `Vec` as "type constructors": they take a type and produce a new type. |
| 10 | + |
| 11 | +A Higher-Kinded Type is an abstraction over these type constructors. It allows us to write functions that are generic not just over a type, but over the *shape* or *kind* of a type constructor. For example, we can write a function that works with any type constructor that can be mapped over (a `Functor`), without caring if it's an `Option`, a `Result`, or something else. |
| 12 | + |
| 13 | +This crate provides the fundamental traits (`HKT`, `HKT2`, `HKT3`) and functional traits (`Functor`, `Monad`) to enable this pattern. |
| 14 | + |
| 15 | +## Usage |
| 16 | + |
| 17 | +This crate uses a "witness" pattern to represent HKTs. For each type constructor (like `Option`), we define a zero-sized "witness" type (like `OptionWitness`) that implements the `HKT` trait. |
| 18 | + |
| 19 | +### Example: Using `Functor` with `Option` |
| 20 | + |
| 21 | +Here's how you can use the `Functor` trait with `Option` via its witness type, `OptionWitness`. |
| 22 | + |
| 23 | +```rust |
| 24 | +use deep_causality_haft::{Functor, HKT, OptionWitness}; |
| 25 | + |
| 26 | +// Manual implementation of Functor for OptionWitness |
| 27 | +impl Functor<OptionWitness> for OptionWitness { |
| 28 | + fn fmap<A, B, Func>(m_a: <OptionWitness as HKT>::Type<A>, f: Func) -> <OptionWitness as HKT>::Type<B> |
| 29 | + where |
| 30 | + Func: FnOnce(A) -> B, |
| 31 | + { |
| 32 | + m_a.map(f) |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +fn main() { |
| 37 | + let opt_a = Some(5); |
| 38 | + let f = |x| x * 2; |
| 39 | + |
| 40 | + // Use the fmap function from our Functor implementation |
| 41 | + let opt_b = OptionWitness::fmap(opt_a, f); |
| 42 | + assert_eq!(opt_b, Some(10)); |
| 43 | + |
| 44 | + let opt_none: Option<i32> = None; |
| 45 | + let opt_none_mapped = OptionWitness::fmap(opt_none, f); |
| 46 | + assert_eq!(opt_none_mapped, None); |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +### Example: Using `Functor` with `Result` |
| 51 | + |
| 52 | +Here's how you can use the `Functor` trait with `Result<T, E>` via its witness type, `ResultWitness<E>`. `HKT2` is used here because `Result` has two generic parameters, and we are fixing the error type `E`. |
| 53 | + |
| 54 | +```rust |
| 55 | +use deep_causality_haft::{Functor, HKT2, ResultWitness}; |
| 56 | + |
| 57 | +// Manual implementation of Functor for ResultWitness |
| 58 | +impl<E> Functor<ResultWitness<E>> for ResultWitness<E> |
| 59 | +where |
| 60 | + E: 'static, |
| 61 | +{ |
| 62 | + fn fmap<A, B, Func>(m_a: <ResultWitness<E> as HKT2<E>>::Type<A>, f: Func) -> <ResultWitness<E> as HKT2<E>>::Type<B> |
| 63 | + where |
| 64 | + Func: FnOnce(A) -> B, |
| 65 | + { |
| 66 | + m_a.map(f) |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +fn main() { |
| 71 | + let res_a: Result<i32, String> = Ok(5); |
| 72 | + let f = |x| x * 2; |
| 73 | + |
| 74 | + // Use the fmap function from our Functor implementation |
| 75 | + let res_b = ResultWitness::fmap(res_a, f); |
| 76 | + assert_eq!(res_b, Ok(10)); |
| 77 | + |
| 78 | + let res_err: Result<i32, String> = Err("Error".to_string()); |
| 79 | + let res_err_mapped = ResultWitness::fmap(res_err, f); |
| 80 | + assert_eq!(res_err_mapped, Err("Error".to_string())); |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | + |
| 85 | +## Type-Encoded Effect System |
| 86 | + |
| 87 | +The `Effect3` and `MonadEffect3` traits provide a powerful mechanism for building a **type-encoded effect system**. This allows you to manage side-effects (like errors and logging) in a structured, safe, and composable way, which is particularly useful for building complex data processing pipelines. |
| 88 | + |
| 89 | +### How it Works |
| 90 | + |
| 91 | +1. **Effects as Types**: Side-effects are represented by generic type parameters on a container (e.g., `E` for Error, `W` for Warning on a custom `MyEffect<T, E, W>` type). |
| 92 | +2. **Rules as Traits**: The logic for how to handle and combine these effects is defined by implementing the `MonadEffect3` trait. For example, the `bind` function can specify that the pipeline should halt on an error while accumulating warnings. |
| 93 | +3. **Compiler-Enforced Safety**: Because the effects are part of the type signature, the Rust compiler can statically verify that all effects are handled correctly. This prevents bugs and ensures that your pipeline code remains pure and focused on its core logic. |
| 94 | +4. **Extensibility**: This pattern is extensible. If you need to manage more side-effects, you can introduce `HKT4` and `Effect4` traits to handle them, without having to rewrite your core pipeline logic. |
| 95 | + |
| 96 | + |
0 commit comments