Skip to content

Commit d5e0672

Browse files
Merge pull request #395 from marvin-hansen/main
feat(deep_causality_haft): Implemented Unbound HKTs, N-Arity traits, and more algebra traits
2 parents 13178e8 + 1744fdd commit d5e0672

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+3693
-546
lines changed

deep_causality_haft/Cargo.toml

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,73 @@ rust-version = { workspace = true }
66
license = { workspace = true }
77

88
repository = "https://github.com/deepcausality/deep_causality.rs"
9-
authors = ["Marvin Hansen <marvin.hansen@gmail.com>", ]
9+
authors = ["Marvin Hansen <marvin.hansen@gmail.com>"]
1010
description = "HKT traits for for the deep_causality crate."
1111
documentation = "https://docs.rs/deep_causality"
1212
categories = ["development-tools"]
1313
keywords = ["HKT", "traits"]
1414
# 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/**/*"]
15+
exclude = [
16+
"*.bazel",
17+
"*/*.bazel",
18+
"*.bazel.*",
19+
"BUILD",
20+
"BUILD.bazel",
21+
"MODULE.bazel",
22+
".bazelignore",
23+
".bazelrc",
24+
"tests/**/*",
25+
]
1626

1727

1828
[dependencies.deep_causality_num]
1929
path = "../deep_causality_num"
2030
version = "0.1"
2131

2232
[[example]]
23-
name = "haft_comonad_example"
24-
path = "examples/comonad_haft.rs"
33+
name = "haft_applicative"
34+
path = "examples/applicative.rs"
2535

2636
[[example]]
27-
name = "haft_effect_system_example"
28-
path = "examples/effect_system_example.rs"
37+
name = "haft_foldable"
38+
path = "examples/foldable.rs"
39+
40+
[[example]]
41+
name = "haft_monad"
42+
path = "examples/monad.rs"
43+
44+
[[example]]
45+
name = "haft_functor"
46+
path = "examples/functor.rs"
47+
48+
[[example]]
49+
name = "haft_comonad"
50+
path = "examples/comonad.rs"
51+
52+
[[example]]
53+
name = "haft_traversable"
54+
path = "examples/traversable.rs"
55+
56+
[[example]]
57+
name = "haft_adjunction"
58+
path = "examples/adjunction.rs"
59+
60+
[[example]]
61+
name = "haft_parametric_monad"
62+
path = "examples/parametric_monad.rs"
63+
64+
[[example]]
65+
name = "haft_bifunctor"
66+
path = "examples/bifunctor.rs"
2967

3068
[[example]]
31-
name = "haft_simple_example"
32-
path = "examples/simple_haft.rs"
69+
name = "haft_profunctor"
70+
path = "examples/profunctor.rs"
3371

3472
[[example]]
35-
name = "haft_functor_example"
36-
path = "examples/functor_haft.rs"
73+
name = "haft_effect_system_example"
74+
path = "examples/effect_system.rs"
3775

3876
[[example]]
39-
name = "haft_traversable_example"
40-
path = "examples/traversable_haft.rs"
77+
name = "haft_unbound_example"
78+
path = "examples/unbound_haft.rs"

deep_causality_haft/README.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fn main() {
8989
}
9090
```
9191

92-
When you run [the example ](/deep_causality_haft/examples/functor_haft.rs)via:
92+
When you run the example via:
9393

9494
`cargo run --example haft_functor_example`
9595

@@ -256,7 +256,7 @@ use deep_causality_haft::{Effect5, MonadEffect5, HKT5};
256256
println!("Sequenced outcome: {:?}", current_effect.value);
257257
```
258258

259-
When you run [the example ](/deep_causality_haft/examples/effect_system_example.rs)via:
259+
When you run the example via:
260260

261261
`cargo run --example haft_effect_system_example`
262262

@@ -321,6 +321,53 @@ Here's a breakdown of how it works:
321321
robust and predictable code.
322322

323323

324+
## Unbound HKTs & Functional Traits (Arity 2-5)
325+
326+
This crate also supports "Unbound" Higher-Kinded Types, where all generic parameters are free to vary. This enables advanced functional patterns from Category Theory that are crucial for complex systems modeling.
327+
328+
### Unbound HKT Traits
329+
330+
* **`HKT2Unbound` - `HKT5Unbound`**: Base traits for multi-arity type constructors (e.g., `Result<A, B>`, `(A, B, C)`).
331+
* **`Bifunctor`**: Maps over both types of a binary constructor simultaneously.
332+
* *Usage*: Evolving a coupled system (e.g., `(Metric, Plasma)`) where both components change type.
333+
* **`Profunctor`**: Contravariant input, Covariant output.
334+
* *Usage*: Adapters, Optics, and State Machines where you pre-process input and post-process output.
335+
* **`Adjunction`**: Defines a dual relationship between two functors ($L \dashv R$).
336+
* *Usage*: Conservation laws, optimization (Primal/Dual), and Galois connections.
337+
* **`ParametricMonad`**: A Monad where the state type changes (Indexed Monad).
338+
* *Usage*: Modeling state transitions (e.g., `Solid -> Liquid -> Gas`) or protocol state machines.
339+
* **`Promonad`**: Models interaction or fusion of contexts.
340+
* *Usage*: Tensor products, force calculations (merging fields), and quantum entanglement.
341+
* **`RiemannMap`**: Models curvature and scattering (Arity 4).
342+
* *Usage*: General Relativity (Curvature Tensor), Particle Physics (Scattering Matrices).
343+
* **`CyberneticLoop`**: Models a complete feedback control loop (Arity 5).
344+
* *Usage*: Autonomous agents (OODA Loop), Control Theory, and Error Correction.
345+
346+
### Example: Bifunctor
347+
348+
```rust
349+
use deep_causality_haft::{Bifunctor, HKT2Unbound};
350+
351+
struct ResultWitness;
352+
impl HKT2Unbound for ResultWitness {
353+
type Type<A, B> = Result<A, B>;
354+
}
355+
356+
impl Bifunctor<ResultWitness> for ResultWitness {
357+
fn bimap<A, B, C, D, F1, F2>(fab: Result<A, B>, mut f1: F1, mut f2: F2) -> Result<C, D>
358+
where F1: FnMut(A) -> C, F2: FnMut(B) -> D {
359+
match fab {
360+
Ok(a) => Ok(f1(a)),
361+
Err(b) => Err(f2(b)),
362+
}
363+
}
364+
}
365+
366+
// Usage
367+
let res: Result<i32, &str> = Ok(10);
368+
let new_res = ResultWitness::bimap(res, |x| x * 2.0, |e| e.len()); // Result<f64, usize>
369+
```
370+
324371
## 👨‍💻👩‍💻 Contribution
325372

326373
Contributions are welcomed especially related to documentation, example code, and fixes.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4+
*/
5+
6+
// ============================================================================
7+
// Adjunction: Global Configuration Access
8+
// ============================================================================
9+
10+
// ENGINEERING VALUE:
11+
// Adjunctions describe a relationship between two functors: Left (L) and Right (R).
12+
// A classic example is (Writer -| Reader) or (Product -| Exponential).
13+
//
14+
// Here we demonstrate the "Reader Adjunction" pattern (Product -| Reader).
15+
// It allows us to convert a function that takes a Context (Reader) into a
16+
// simple value pair (Product), and vice versa.
17+
//
18+
// Practical Use: "Currying" configuration.
19+
// Instead of passing `Config` to every function, we can "adjunct" the function
20+
// to lock in the config, producing a standalone value.
21+
22+
fn main() {
23+
println!("=== DeepCausality HKT: Adjunction Pattern ===\n");
24+
println!("--- Reader/Writer Duality ---");
25+
println!("(Demonstration of concept)");
26+
27+
// Scenario: We have a function that fetches data given a Config and an ID.
28+
// fetch_data: (Config, i32) -> String
29+
let fetch_data = |cfg: Config, id: i32| -> String {
30+
format!("Data for ID {} using Key {}", id, cfg.api_key)
31+
};
32+
33+
// We want to "bake in" the ID first, creating a reusable "Reader" that just needs Config.
34+
// We use the Right Adjunct logic manually here since Rust closures are tricky.
35+
36+
let id_to_fetch = 42;
37+
let reader = move |cfg: Config| fetch_data(cfg, id_to_fetch);
38+
39+
// Now 'reader' is a function Config -> String.
40+
// We can pass this 'reader' around to a component that holds the Config.
41+
42+
let my_config = Config {
43+
api_key: "SECRET_KEY".to_string(),
44+
};
45+
46+
let result = reader(my_config);
47+
println!("Adjunction Result: {}", result);
48+
}
49+
50+
struct Config {
51+
api_key: String,
52+
}
53+
54+
// Mock Adjunction Implementation for demonstration
55+
#[allow(dead_code)]
56+
struct ConfigAdjunction;
57+
58+
#[allow(dead_code)]
59+
impl ConfigAdjunction {
60+
// Left Adjunct: (Config, A) -> B ===> A -> (Config -> B)
61+
fn left_adjunct<A, B, F>(f: F) -> impl Fn(A) -> Box<dyn Fn(Config) -> B>
62+
where
63+
A: Clone + 'static,
64+
F: Fn(Config, A) -> B + Clone + 'static,
65+
{
66+
move |a: A| {
67+
let f = f.clone();
68+
let a = a.clone();
69+
Box::new(move |cfg: Config| f(cfg, a.clone()))
70+
}
71+
}
72+
73+
// Right Adjunct: A -> (Config -> B) ===> (Config, A) -> B
74+
fn right_adjunct<A, B, F>(f: F) -> impl Fn(Config, A) -> B
75+
where
76+
F: Fn(A) -> Box<dyn Fn(Config) -> B>,
77+
{
78+
move |cfg: Config, a: A| {
79+
let reader = f(a);
80+
reader(cfg)
81+
}
82+
}
83+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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, OptionWitness};
7+
8+
// ============================================================================
9+
// Domain: E-Commerce Order Processing
10+
// ============================================================================
11+
12+
fn main() {
13+
println!("=== DeepCausality HKT: Applicative Pattern ===\n");
14+
15+
// ------------------------------------------------------------------------
16+
// Applicative: Independent Validation
17+
//
18+
// ENGINEERING VALUE:
19+
// When validating a form, you often want to collect ALL errors, not just the first one.
20+
// Or you want to combine multiple independent results (e.g., parallel API calls).
21+
//
22+
// Applicative (`apply`) allows you to combine values inside a context (Result/Option)
23+
// independent of each other.
24+
// ------------------------------------------------------------------------
25+
println!("--- Independent Validation ---");
26+
27+
let validate_id = |id: &str| -> Option<String> {
28+
if id.len() > 3 {
29+
Some(id.to_string())
30+
} else {
31+
None
32+
}
33+
};
34+
35+
let validate_qty = |qty: u32| -> Option<u32> { if qty > 0 { Some(qty) } else { None } };
36+
37+
// We want to create an OrderItem ONLY if both ID and Qty are valid.
38+
// Applicative style: pure(constructor).apply(id).apply(qty)
39+
40+
let valid_id = validate_id("item_123");
41+
let _valid_qty = validate_qty(5);
42+
let price = 10.0;
43+
44+
// Note: Rust's type system makes currying a bit verbose, so we often use helper macros or
45+
// explicit closures. Here we show the raw `apply` mechanism.
46+
// We lift a closure that takes (String, u32) -> OrderItem
47+
let constructor = |id: String| {
48+
move |qty: u32| OrderItem {
49+
id,
50+
price,
51+
quantity: qty,
52+
}
53+
};
54+
55+
// Step 1: Lift constructor into Option
56+
// Option<Fn(String) -> Fn(u32) -> OrderItem>
57+
// Using OptionWitness::fmap for step 1
58+
let _partial_constructor = OptionWitness::fmap(valid_id, constructor);
59+
60+
// Now we have Option<Fn(u32) -> OrderItem>. We need to apply Option<u32>.
61+
// OptionWitness::apply expects Option<Fn(A) -> B> and Option<A>.
62+
63+
let config_host = Some("localhost".to_string());
64+
let config_port = 8080; // Directly use value to avoid unwrap warning
65+
66+
// We want to combine them into a string "host:port"
67+
let combine = |host: String| move |port: i32| format!("{}:{}", host, port);
68+
69+
let partial = OptionWitness::fmap(config_host, combine);
70+
// partial is Some(Fn(i32) -> String)
71+
72+
// We need to cast/coerce the closure type for `apply` to work generically,
73+
// which is hard in stable Rust without boxing.
74+
// So we'll simulate `apply` behavior for the example's clarity.
75+
76+
if let Some(f) = partial {
77+
let result = f(config_port); // Simulating apply
78+
println!("Applicative Result: {}", result);
79+
}
80+
}
81+
82+
#[derive(Debug, Clone, PartialEq)]
83+
struct OrderItem {
84+
id: String,
85+
price: f64,
86+
quantity: u32,
87+
}

0 commit comments

Comments
 (0)