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

#![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.

/// The empty `HList`.
pub struct Nil;

/// An `HList` with `H` at position 0, and `T` as the rest of the list.
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()
    }
}

#[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);
}