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
/// A comonoid in a monoidal category is a monoid
/// in the dual category, what is the problem?
///
/// Being the dual to a monoid, it consists of:
///
/// - The dual to unit, a function from `T` to `()` called `counit`.
/// - The dual to multiplication, a function from `T` to `(T, T)` called `comult`.
///
/// It is useful within Rust's ownership type system,
/// to represent a type that can be both cloned and destroyed.
///
/// There is a trivial implementation of this trait for every
/// type that implements `Clone`, as reflected by the `Comonoidal`
/// newtype wrapper, with `discard` as `counit`, and `duplicate` as `comult`.
/// The behaviour of `counit` and `comult` can be altered by the
/// implementation of [`Drop`](http://doc.rust-lang.org/std/ops/trait.Drop.html)
/// and [`Clone`](http://doc.rust-lang.org/std/clone/trait.Clone.html),
/// respectively.
pub trait Comonoid {
    /// The dual to the monoidal unit.
    fn counit(self) -> ();
    /// The dual to the monoidal multiplication.
    fn comult(self) -> (Self, Self);
}

/// Takes ownership of a value and returns `()`,
/// rendering it unusable.
pub fn discard<T>(_: T) -> () {}
/// Takes ownership of a value and returns a tuple of
/// clones.
pub fn duplicate<T>(a: T) -> (T, T) where T: Clone {
(a.clone(), a)
}

/// A newtype wrapper to make a comonoid out of any
/// cloneable type.
pub struct Comonoidal<T>(T);

impl<T> From<T> for Comonoidal<T> {
    fn from(a: T) -> Comonoidal<T> { Comonoidal(a) }
}

impl<T> AsRef<T> for Comonoidal<T> {
    fn as_ref(&self) -> &T { &self.0 }
}

impl<T> AsMut<T> for Comonoidal<T> {
    fn as_mut(&mut self) -> &mut T { &mut self.0 }
}

impl<T> Clone for Comonoidal<T> where T: Clone {
    fn clone(&self) -> Self {
        let a = self.0.clone();
        Comonoidal(a)
    }
}

impl<T> Comonoid for Comonoidal<T> where T: Clone {
    fn counit(self) -> () { discard(self) }
    fn comult(self) -> (Self, Self) { duplicate(self) }
}

#[test]
fn test_counit() {
    let x = from(42);
    assert_eq!(Comonoid::counit(x), ());
}

#[test]
fn test_comult() {
    let x = from(42);
    let (y, z) = Comonoid::comult(x);
    assert_eq!(y.0, 42);
    assert_eq!(z.0, 42);
}