Skip to main content

karpal_core/
hkt.rs

1use core::marker::PhantomData;
2
3/// Higher-Kinded Type encoding via GATs.
4///
5/// A type implementing `HKT` acts as a type-level function:
6/// given a type `T`, it produces `Self::Of<T>`.
7pub trait HKT {
8    type Of<T>;
9}
10
11/// Type constructor for `Option<T>`.
12pub struct OptionF;
13
14impl HKT for OptionF {
15    type Of<T> = Option<T>;
16}
17
18/// Type constructor for `Result<T, E>` (fixed error type `E`).
19pub struct ResultF<E> {
20    _marker: PhantomData<E>,
21}
22
23impl<E> HKT for ResultF<E> {
24    type Of<T> = Result<T, E>;
25}
26
27/// Type constructor for `Vec<T>`.
28#[cfg(any(feature = "std", feature = "alloc"))]
29pub struct VecF;
30
31#[cfg(any(feature = "std", feature = "alloc"))]
32impl HKT for VecF {
33    #[cfg(feature = "std")]
34    type Of<T> = Vec<T>;
35
36    #[cfg(all(not(feature = "std"), feature = "alloc"))]
37    type Of<T> = alloc::vec::Vec<T>;
38}
39
40/// Two-parameter type constructor (HKT for bifunctors / profunctors).
41pub trait HKT2 {
42    type P<A, B>;
43}
44
45/// Type constructor for `Result<A, B>` as a bifunctor (both parameters vary).
46pub struct ResultBF;
47
48impl HKT2 for ResultBF {
49    type P<A, B> = Result<B, A>;
50}
51
52/// Type constructor for `(A, B)` as a bifunctor.
53pub struct TupleF;
54
55impl HKT2 for TupleF {
56    type P<A, B> = (A, B);
57}
58
59/// Type constructor for the identity functor: `Of<T> = T`.
60pub struct IdentityF;
61
62impl HKT for IdentityF {
63    type Of<T> = T;
64}
65
66/// A non-empty vector: guaranteed to have at least one element.
67#[cfg(any(feature = "std", feature = "alloc"))]
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct NonEmptyVec<T> {
70    pub head: T,
71    pub tail: Vec<T>,
72}
73
74#[cfg(any(feature = "std", feature = "alloc"))]
75impl<T> NonEmptyVec<T> {
76    pub fn new(head: T, tail: Vec<T>) -> Self {
77        Self { head, tail }
78    }
79
80    pub fn singleton(value: T) -> Self {
81        Self {
82            head: value,
83            tail: Vec::new(),
84        }
85    }
86
87    pub fn len(&self) -> usize {
88        1 + self.tail.len()
89    }
90
91    pub fn is_empty(&self) -> bool {
92        false // always at least one element
93    }
94
95    pub fn iter(&self) -> impl Iterator<Item = &T> {
96        core::iter::once(&self.head).chain(self.tail.iter())
97    }
98
99    /// Collect all suffixes as a NonEmptyVec of NonEmptyVecs.
100    pub fn tails(&self) -> NonEmptyVec<NonEmptyVec<T>>
101    where
102        T: Clone,
103    {
104        let mut result_tail = Vec::new();
105        for i in 1..self.len() {
106            result_tail.push(NonEmptyVec::new(
107                self.tail[i - 1].clone(),
108                self.tail[i..].to_vec(),
109            ));
110        }
111        NonEmptyVec::new(self.clone(), result_tail)
112    }
113}
114
115/// Type constructor for `NonEmptyVec<T>`.
116#[cfg(any(feature = "std", feature = "alloc"))]
117pub struct NonEmptyVecF;
118
119#[cfg(any(feature = "std", feature = "alloc"))]
120impl HKT for NonEmptyVecF {
121    type Of<T> = NonEmptyVec<T>;
122}
123
124/// Type constructor for the Env comonad: `Of<T> = (E, T)`.
125pub struct EnvF<E>(PhantomData<E>);
126
127impl<E> HKT for EnvF<E> {
128    type Of<T> = (E, T);
129}
130
131/// Type constructor for the Store comonad: `Of<T> = (Box<dyn Fn(S) -> T>, S)`.
132#[cfg(any(feature = "std", feature = "alloc"))]
133pub struct StoreF<S>(PhantomData<S>);
134
135#[cfg(any(feature = "std", feature = "alloc"))]
136impl<S: 'static> HKT for StoreF<S> {
137    type Of<T> = (Box<dyn Fn(S) -> T>, S);
138}
139
140/// Type constructor for the Traced comonad: `Of<T> = Box<dyn Fn(M) -> T>`.
141#[cfg(any(feature = "std", feature = "alloc"))]
142pub struct TracedF<M>(PhantomData<M>);
143
144#[cfg(any(feature = "std", feature = "alloc"))]
145impl<M: 'static> HKT for TracedF<M> {
146    type Of<T> = Box<dyn Fn(M) -> T>;
147}