1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#![warn(missing_docs)]

//! This crate provides types `Nil` and `Cons<H, T>`, which together allow for creating lists consisting of multiple types.
//! The types in the list are present in the type of the list, so that `Cons<i32, Cons<i64, Nil>>` contains an `i32` and an `i64`.
//! If type `T` is present exactly once in an `HList`, it is usually possible to allow the compiler to find the `T` in the `HList` by using the `Find` trait.

#[cfg(feature = "with_serde")]
#[macro_use]
extern crate serde_derive;

use std::ops::Add;

/// The empty `HList`.
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))]
pub struct Nil;

/// An `HList` with `H` at position 0, and `T` as the rest of the list.
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))]
pub struct Cons<H, T>(pub H, pub T);

/// A marker trait that `Nil` and `Cons<H, T>` satisfies.
/// Not currently used to enforce proper hlists, although this may change.
/// Provides the `push()` method
pub trait HList: Sized {
    /// Consumes the `HList`, and returns a new HList with `item` at the beginning.
    fn push<N>(self, item: N) -> Cons<N, Self> {
        Cons(item, self)
    }
}

impl HList for Nil {}
impl<H, T> HList for Cons<H, T> {}


/// Used as an index into an `HList`.
///
/// `Here` is 0, pointing to the head of the HList.
///
/// Users should normally allow type inference to create this type
#[allow(dead_code)]
pub enum Here {}


/// Used as an index into an `HList`.
///
/// `There<T>` is 1 + `T`.
///
/// Users should normally allow type inference to create this type.
#[allow(dead_code)]
pub struct There<T>(std::marker::PhantomData<T>);


/// `Find<T, I>` is implemented for an `HList` if index `I` of the `HList` is a `T`
///
/// Rust's type inferencer can often produce a correct `I`
/// if there is exactly one `T` in the `HList`.
///
/// ```rust
/// use hlist::{HList, Nil, Find};
///
/// // The type of list is Cons<i64, Cons<i32, Nil>>
/// let list = Nil.push(0i32).push(1i64);
///
/// // Here list satisfies the trait Find<i64, Here>.
/// // The compiler infers the second type parameter.
/// let a: i64 = *list.get();
/// assert!(a == 1);
///
/// // Here list satisfies the trait Find<i32, There<Here>>.
/// let b: i32 = *list.get(); 
/// assert!(b == 0);
/// ```
///
/// Functions that need to look up values of a type in an HList given to them should get the index from the call site:
///
/// ```rust
/// use hlist::{HList, Nil, Find};
///
/// fn foo<I, L: Find<i32, I>>(list: &L) -> i32 {
///     *list.get()
/// }
/// let list = Nil.push("foo").push(5i32).push("bar");
/// assert!(foo(&list) == 5);
/// ```
/// 
/// When `foo()` is called, the compiler figures out the appropriate value for `I`.
pub trait Find<T, I> {
    /// Retrieves a `&T`.
    ///
    /// Allows for type inferencing to act like type-directed search.
    ///
    /// ```rust
    /// use hlist::{HList, Nil, Find};
    ///
    /// let list = Nil.push(0i32).push(1i64);
    /// let a: i64 = *list.get();
    /// assert!(a == 1);
    fn get(&self) -> &T;
    
    /// Retrieves a `&mut T`.
    ///
    /// Allows for type inferencing to act like type-directed search.
    ///
    /// ```rust
    /// use hlist::{HList, Nil, Find};
    ///
    /// let mut list = Nil.push(0i32).push(1i64);
    /// *list.get_mut() = 5i32;
    /// let a: i32 = *list.get();
    /// assert!(a == 5);
    fn get_mut(&mut self) -> &mut T;
}

impl<T, Tail> Find<T, Here> for Cons<T, Tail> {
    fn get(&self) -> &T {
        &self.0
    }
    fn get_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<Head, T, Tail, TailIndex> Find<T, There<TailIndex>> for Cons<Head, Tail>
    where Tail: Find<T, TailIndex> {
    fn get(&self) -> &T {
        self.1.get()
    }
    fn get_mut(&mut self) -> &mut T {
        self.1.get_mut()
    }
}

impl<RHS> Add<RHS> for Nil {
    type Output = RHS;
    
    fn add(self, rhs: RHS) -> RHS {
        rhs
    }
}

impl<H, T, RHS> Add<RHS> for Cons<H, T>
    where T: Add<RHS> {
    type Output = Cons<H, <T as Add<RHS>>::Output>;
    
    fn add(self, rhs: RHS) -> Self::Output {
        Cons(self.0, self.1 + rhs)
    }
}

#[test]
fn test_get() {
    let list = Nil.push(5i32).push("Foo");
    let a: i32 = *list.get();
    let b: &str = *list.get();
    assert!(a == 5i32);
    assert!(b == "Foo");
}

#[test]
fn test_get_mut() {
    let mut list = Nil.push(5i32).push("Foo");
    *list.get_mut() = 6i32;
    *list.get_mut() = "Bar";
    let a: i32 = *list.get();
    let b: &str = *list.get();
    assert!(a == 6i32);
    assert!(b == "Bar");
}

#[test]
fn test_index_as_type_parameter() {
    fn foo<I, L: Find<i32, I>>(list: &L) -> i32 {
        *list.get()
    }
    let list = Nil.push("foo").push(5i32).push("bar");
    assert!(foo(&list) == 5);
}

#[test]
fn test_hlist_addition() {
    let list_1 = Nil.push(0i32);
    let list_2 = Nil.push("Foo");
    let list_3 = list_1 + list_2;
    let a: i32 = *list_3.get();
    let b: &str = *list_3.get();
    assert!(a == 0i32);
    assert!(b == "Foo");
}