Skip to content

Commit 4530434

Browse files
committed
Added initial implementation of deep_causality_haft
Signed-off-by: Marvin Hansen <[email protected]>
1 parent d204231 commit 4530434

File tree

16 files changed

+917
-0
lines changed

16 files changed

+917
-0
lines changed

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ The reason is, Bazel cannot access util files from within the test folder, but i
108108
can access the full src folder during testing. As a result, test utils have to be fully
109109
tested to count towards the code coverage score.
110110

111+
The usage of a prelude file is prohibited.
112+
111113
## Code export
112114

113115
* All public errors, traits, and errors are exported from src/lib.rs

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ members = [
88
"deep_causality_algorithms",
99
"deep_causality_data_structures",
1010
"deep_causality_discovery",
11+
"deep_causality_haft",
1112
"deep_causality_macros",
1213
"deep_causality_num",
1314
"deep_causality_rand",

deep_causality_haft/BUILD.bazel

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
load("@rules_rust//rust:defs.bzl", "rust_doc", "rust_doc_test", "rust_library", "rust_test")
2+
3+
rust_library(
4+
name = "deep_causality_haft",
5+
srcs = glob([
6+
"src/**",
7+
]),
8+
crate_root = "src/lib.rs",
9+
tags = [
10+
"utils",
11+
"HKT",
12+
"HAFT",
13+
],
14+
visibility = ["//visibility:public"],
15+
deps = [
16+
17+
],)
18+
19+
rust_doc(
20+
name = "doc",
21+
crate = ":deep_causality_haft",
22+
tags = ["doc"],
23+
visibility = ["//visibility:public"],
24+
)
25+
26+
rust_doc_test(
27+
name = "doc_test",
28+
crate = ":deep_causality_haft",
29+
tags = ["doc-test"],
30+
visibility = ["//visibility:public"],
31+
)
32+
33+
rust_test(
34+
name = "tests",
35+
crate = ":deep_causality_haft",
36+
srcs = glob(["tests/**"]),
37+
)

deep_causality_haft/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "deep_causality_haft"
3+
version = "0.1.0"
4+
edition = { workspace = true }
5+
rust-version = { workspace = true }
6+
license = { workspace = true }
7+
8+
repository = "https://github.com/deepcausality/deep_causality.rs"
9+
authors = ["Marvin Hansen <[email protected]>", ]
10+
description = "HKT traits for for the deep_causality crate."
11+
documentation = "https://docs.rs/deep_causality"
12+
categories = ["development-tools"]
13+
keywords = ["HKT", "traits"]
14+
# Exclude all bazel files as these conflict with Bazel workspace when vendored.
15+
exclude = ["*.bazel", "*/*.bazel", "*.bazel.*", "BUILD", "BUILD.bazel", "MODULE.bazel", ".bazelignore",".bazelrc", "tests/**/*"]
16+
17+
18+
[dependencies]
19+

deep_causality_haft/LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
SPDX-License-Identifier: MIT
2+
3+
Copyright (c) 2023 - The DeepCausality Authors. All Rights Reserved.
4+
5+
MIT License
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.

deep_causality_haft/README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
## Type-Encoded Effect System
84+
85+
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.
86+
87+
### How it Works
88+
89+
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).
90+
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.
91+
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.
92+
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.
93+
94+
In essence, this crate provides the tools to build a small, powerful, and compile-time-checked effects library tailored perfectly for your application's needs, forming the foundation for building powerful, abstract, and reusable causal models in the `deep_causality` ecosystem.
95+
```
96+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4+
*/
5+
6+
use deep_causality_haft::{Functor, Monad, OptionWitness, ResultWitness};
7+
8+
fn main() {
9+
println!("--- Option Example ---");
10+
11+
let opt_a = Some(5);
12+
let f_map = |x| x * 2;
13+
let opt_b = OptionWitness::fmap(opt_a, f_map);
14+
println!("fmap(Some(5), |x| x * 2) = {:?}", opt_b);
15+
assert_eq!(opt_b, Some(10));
16+
17+
let f_bind = |x| Some(x + 1);
18+
let opt_c = OptionWitness::bind(opt_b, f_bind);
19+
println!("bind(Some(10), |x| Some(x + 1)) = {:?}", opt_c);
20+
assert_eq!(opt_c, Some(11));
21+
22+
let pure_val_opt = OptionWitness::pure(100);
23+
println!("pure(100) = {:?}", pure_val_opt);
24+
assert_eq!(pure_val_opt, Some(100));
25+
26+
println!("\n--- Result Example ---");
27+
28+
type MyResult<T> = Result<T, String>;
29+
30+
let res_a: MyResult<i32> = Ok(10);
31+
let f_map_res = |x: i32| x.to_string();
32+
let res_b = ResultWitness::fmap(res_a, f_map_res);
33+
println!("fmap(Ok(10), |x| x.to_string()) = {:?}", res_b);
34+
assert_eq!(res_b, Ok("10".to_string()));
35+
36+
let f_bind_res = |s: String| Ok(s.len());
37+
let res_c = ResultWitness::bind(res_b, f_bind_res);
38+
println!("bind(Ok(\"10\"), |s| Ok(s.len())) = {:?}", res_c);
39+
assert_eq!(res_c, Ok(2));
40+
41+
let res_err: MyResult<i32> = Err("An error occurred".to_string());
42+
let res_err_mapped = ResultWitness::fmap(res_err.clone(), f_map_res);
43+
println!("fmap(Err(...), |x| x.to_string()) = {:?}", res_err_mapped);
44+
assert_eq!(res_err_mapped, Err("An error occurred".to_string()));
45+
46+
let res_err_bound = ResultWitness::bind(res_err, |x| Ok(x + 1));
47+
println!("bind(Err(...), |x| Ok(x + 1)) = {:?}", res_err_bound);
48+
assert_eq!(res_err_bound, Err("An error occurred".to_string()));
49+
50+
let pure_val_res: MyResult<i32> = ResultWitness::pure(200);
51+
println!("pure(200) = {:?}", pure_val_res);
52+
assert_eq!(pure_val_res, Ok(200));
53+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4+
*/
5+
6+
use crate::fp::{Functor, Monad};
7+
use crate::kind::{HKT, OptionWitness};
8+
use crate::kind::{HKT2, ResultWitness};
9+
10+
// Manual implementation of Functor for OptionWitness
11+
impl Functor<OptionWitness> for OptionWitness {
12+
fn fmap<A, B, Func>(
13+
m_a: <OptionWitness as HKT>::Type<A>,
14+
f: Func,
15+
) -> <OptionWitness as HKT>::Type<B>
16+
where
17+
Func: FnOnce(A) -> B,
18+
{
19+
m_a.map(f)
20+
}
21+
}
22+
23+
// Manual implementation of Monad for OptionWitness
24+
impl Monad<OptionWitness> for OptionWitness {
25+
fn pure<T>(value: T) -> <OptionWitness as HKT>::Type<T> {
26+
Some(value)
27+
}
28+
29+
fn bind<A, B, Func>(
30+
m_a: <OptionWitness as HKT>::Type<A>,
31+
f: Func,
32+
) -> <OptionWitness as HKT>::Type<B>
33+
where
34+
Func: FnOnce(A) -> <OptionWitness as HKT>::Type<B>,
35+
{
36+
m_a.and_then(f)
37+
}
38+
}
39+
40+
// Manual implementation of Functor for ResultWitness
41+
impl<E> Functor<ResultWitness<E>> for ResultWitness<E>
42+
where
43+
E: 'static,
44+
{
45+
fn fmap<A, B, Func>(
46+
m_a: <ResultWitness<E> as HKT2<E>>::Type<A>,
47+
f: Func,
48+
) -> <ResultWitness<E> as HKT2<E>>::Type<B>
49+
where
50+
Func: FnOnce(A) -> B,
51+
{
52+
m_a.map(f)
53+
}
54+
}
55+
56+
// Manual implementation of Monad for ResultWitness
57+
impl<E> Monad<ResultWitness<E>> for ResultWitness<E>
58+
where
59+
E: 'static,
60+
{
61+
fn pure<T>(value: T) -> <ResultWitness<E> as HKT2<E>>::Type<T> {
62+
Ok(value)
63+
}
64+
65+
fn bind<A, B, Func>(
66+
m_a: <ResultWitness<E> as HKT2<E>>::Type<A>,
67+
f: Func,
68+
) -> <ResultWitness<E> as HKT2<E>>::Type<B>
69+
where
70+
Func: FnOnce(A) -> <ResultWitness<E> as HKT2<E>>::Type<B>,
71+
{
72+
m_a.and_then(f)
73+
}
74+
}

0 commit comments

Comments
 (0)