### 3 releases (breaking)

0.3.0 | Jan 31, 2020 |
---|---|

0.2.0 | Jan 4, 2020 |

0.1.0 | Jan 2, 2020 |

#**872** in Rust patterns

**MIT/Apache**

57KB

1K
SLoC

# haskell_bits

Various typeclasses and concepts from Haskell, implemented in Rust. (crates.io link)

## Synopsis

Currently this library is an implementation of the Functor/Applicative/Monad hierarchy in Rust. It's very much a work in progress, and I'm releasing it now largely as a request for comment, I'd like to have some discussions about my design choices with others before I go too far down this path.

There has been some work in this space, examples are:

## So what makes this library different?!

The one thing (I believe) makes this library unique is that one can define generic functions on Monads, Applicatives, Functors etc.

The libraries mentioned above seem to define Functor, Applicative, Monad for various types, so

, `fmap`

etc, work on those types, but there doesn't seem to be a way to write one generic function that works on all types defined as, say, Monad.`bind`

This, I think, is a very important issue. The great thing about these typeclasses like Monad in Haskell is that if you've got

different Monads, and `m`

different functions defined on Monads, you've now got effectively `n`

functions defined. Make a new Monad, you've got a huge infrastucture for free. Make a new Monad function, it works on 100s of Monadic types already defined by others.`m *n`

This is one of the key things that makes Monads and associated concepts, and more generally Haskell, so powerful, in that you can combine things in a multiplicative numbers of ways, so much so that many problems just consist of putting together existing bits of code, of which the type system ensures you can only do safely, and almost always, correctly. This is a huge advantage in terms of productivity and in particular, reliability.

## Example

Here is a simple Haskell function:

`monadic_pair` `::` `Monad` `m` `=>` `m` `a` `->` `m` `a` `->` `m` (`a`, `a`)
monadic_pair x y `=` `do`
x_val `<-` x
y_val `<-` y
pure (x_val`,` y_val)

And this is the equivalent using this library in Rust:

`fn` `monadic_pair``<`TCon, T, TArg`>``(``x``:` `&`TArg, `y``:` `&`TArg`)`` ``->` `<`TCon `as` WithTypeArg`<``(`T, T`)``>``>``::`Type
`where`
TCon`:` Monad + `WithTypeArg``<`T`>` + `WithTypeArg``<``(`T, T`)``>`,
TArg`:` `TypeApp``<`TCon, T`>`,
T`:` Clone,
`{`
`mdo!` `{`
x_val `=``<``<` x`;`
y_val `=``<``<` y`;`
`ret``<`TCon`>` `(`x_val`.``clone``(``)``,` y_val`.``clone``(``)``)``;`
`}`
`}`

We can then apply this to vectors:

`let` v1`:` `Vec``<``u32``>` `=` `vec!``[``1``,` `2``,` `3``]``;`
`let` v2`:` `Vec``<``u32``>` `=` `vec!``[``4``,` `5``,` `6``]``;`
`let` v_result `=` `monadic_pair``(``&`v1`,` `&`v2`)``;`
`assert_eq!``(`
v_result`,`
`vec!``[`
`(``1``,` `4``)``,`
`(``1``,` `5``)``,`
`(``1``,` `6``)``,`
`(``2``,` `4``)``,`
`(``2``,` `5``)``,`
`(``2``,` `6``)``,`
`(``3``,` `4``)``,`
`(``3``,` `5``)``,`
`(``3``,` `6``)`
`]`
`)``;`

Or options:

`let` o1`:` `Option``<``u32``>` `=` `Some``(``1``)``;`
`let` o2`:` `Option``<``u32``>` `=` `Some``(``2``)``;`
`let` o3`:` `Option``<``u32``>` `=` `None``;`
`let` o1_result `=` `monadic_pair``(``&`o1`,` `&`o2`)``;`
`let` o2_result `=` `monadic_pair``(``&`o1`,` `&`o3`)``;`
`assert_eq!``(`o1_result`,` `Some``(``(``1``,` `2``)``)``)``;`
`assert_eq!``(`o2_result`,` `None``)``;`

All with the expected results.

We can even do the standard applicative style function application:

`let` _applicative_option_result `=` `(``|``x``|` `move` `|`y`|` x `+` y`)``.``lmap``(`o1`)``.``lap``(`o2`)``;`
`assert_eq!``(``Some``(``3``)``,` _applicative_option_result`)``;`

With some caveats I'll mention later.

## So how does it work?

Rust does not have higher kinded types, well, at least ones we can abstract over. What I mean by this is that whilst Rust happily allows

for some generic `Vec``<`T`>`

, Rust doesn't allow `T`

for some generic `T <u32>`

`T`

. It allows `Vec``<``u32``>`

, but we can't abstract over `Vec`

like we can `u32`

.This has some particular consequences. Lets say we want to write a trait in Rust that encapsulates the idea of applying a function to all parts of a structure. We might try:

`trait` `Mappable` `{`
`map``<`TIn, TOut`>``(`f`:` `impl` `Func``<`TIn, TOut`>`, `x`: `Self``<`TIn`>`) -> `Self``<`TOut`>`;
}
`impl` `Mappable` `for` `Vec` `{`
`...`
`}`
`impl` `Mappable ``for`` ``Option` `{`
`...`
`}`
`//` etc...

#### A slight snag

Except we can't do this. Rust will refuse to compile this, because

, `Vec`

(and by inference, `Option`

) are not types. `Self`

is, but `Option``<``u32``>`

is not.`Option`

So we need to do a trick:

`pub` `trait` `WithTypeArg`<T>
`{`
`type` `Type``;`
`}`

And then:

`pub` `struct` `VecTypeCon``;`
`impl``<`T`>`` ``WithTypeArg``<`T`>` `for`` ``VecTypeCon` `{`
`type` `Type` `=` `Vec``<`T`>``;`
`}`

We can then define

like so:`Mappable`

`pub` `trait` `Mappable` `{`
`fn` `map``<`TIn, TOut`>``(`
`f``:` impl Fn`(``TIn``)` -> TOut,
`x``:` `<``Self` `as` WithTypeArg`<`TIn`>``>``::`Type,
`)`` ``->` `<``Self` `as` WithTypeArg`<`TOut`>``>``::`Type
`}`

And here's a simple implementation for

s:`Option`

`impl` `Mappable ``for`` ``OptionTypeCon` `{`
`fn` `lmap``<`TIn, TOut`>``(`
`f``:` impl Fn`(``TIn``)` -> TOut,
`x``:` `<`OptionTypeCon `as` WithTypeArg`<`TIn`>``>``::`Type,
`)`` ``->` `<`OptionTypeCon `as` WithTypeArg`<`TOut`>``>``::`Type `{`
`Option``::`map`(`x`,` f`)` `//` Option itself already has a specific "map" function
`}`
`}`

and for

s:`Vec`

`impl` `Mappable ``for`` ``VecTypeCon` `{`
`fn` `map``<`TIn, TOut`>``(`
`f``:` impl Fn`(``TIn``)` -> TOut,
`x``:` `<`VecTypeCon `as` WithTypeArg`<`TIn`>``>``::`Type,
`)`` ``->` `<`VecTypeCon `as` WithTypeArg`<`TOut`>``>``::`Type `{`
`let` size `=` x`.``capacity``(``)``;`
`let` `mut` v`:` `Vec``<`TOut`>` `=` `Vec``::`with_capacity`(`size`)``;`
`for` e `in` x `{`
v`.``push``(``f``(`e`)``)``;`
`}`
v
`}`
`}`

This is pretty good so far, but there's a small issue, which we sort out.

Notice the main motivation for this library is to be able to combine these functions in a generic way.

Well lets make a silly function that just does two maps in a row (this is a silly function as it would be more efficient just to combine the functions, but let's go with it):

`fn` `map2``<`TCon, TIn, TMid, TOut`>``(`
`f``:` impl Fn`(``TIn``)` -> TMid,
`g``:` impl Fn`(``TMid``)` -> TOut,
`x``:` `<`TCon `as` WithTypeArg`<`TIn`>``>``::`Type,
`)`` ``->` `<`TCon `as` WithTypeArg`<`TOut`>``>``::`Type
`{`
`map``(`g`,` `map``(`f`,` x`)``)`
`}`

This unfortunately doesn't work, even though it looks quite type-correct. I think it's because Rust won't match against a type unless it's fully resolved, and

can't be resolved because we don't know what `<`TCon `as` `WithTypeArg <TIn>`

`>`

`Type`

`::``TCon`

or `TIn`

is. Therefore we can't pass it to `map`

, even though it wants an argument of `<`TCon `as` `WithTypeArg``<`TIn`>``>``::`Type

(the exact same thing), because I think Rust wants to resolve them both before checking if they're equal.So what we do is adjust our definition of `WithTypeArg`

`pub` `trait` `WithTypeArg`<T> `{`
`type` `Type``:` `TypeApp``<``Self`, T`>``;`
`}`

And write this new trait

:`TypeApp`

`pub` `trait` `TypeApp`<TCon, T>
where
TCon: WithTypeArg<T>,
Self: Is<Type = <TCon as WithTypeArg<T>>::Type>,
`{`
`}`

Yes, notice these traits refer to each other. Also of importance is the

trait. This does take some getting your head around, I would suggest looking at the source of my `Is`

library. But basically what this allows one to do is to force a one-to-one correspondence between a trait and an actual type.`is_type`

So you can basically go back and forth between them at will.

Then we can redefine

as follows, replacing any occurrence of an argument (but not return type) of:`map`

`<`TCon `as` `WithTypeArg <T>`

`>`

`Type`

`::`with:

`impl` `TypeApp``<`TCon, T`>`

`pub` `trait` `Mappable` `{`
`fn` `map``<`TIn, TOut`>``(`
`f``:` impl Fn`(``TIn``)` -> TOut,
`x``:` impl `TypeApp``<`TCon, TIn`>`,
`)`` ``->` `<``Self` `as` WithTypeArg`<`TOut`>``>``::`Type
`}`

and likewise change

:`map2`

`fn` `map2``<`TCon, TIn, TMid, TOut`>``(`
`f``:` impl Fn`(``TIn``)` -> TMid,
`g``:` impl Fn`(``TMid``)` -> TOut,
`x``:` impl `TypeApp``<`TCon, TIn`>`,
`)`` ``->` `<`TCon `as` WithTypeArg`<`TOut`>``>``::`Type
`{`
`map``(`g`,` `map``(`f`,` x`)``)`
`}`

and we're good.

It seems for some reason not to pass

to an argument of type `<`TCon `as` `WithTypeArg <T>`

`>`

`Type`

`::``<`TCon `as` `WithTypeArg``<`T`>``>``::`Type

, but it will pass `<`TCon `as` `WithTypeArg``<`T`>``>``::`Type

to a trait argument of `TypeApp``<`TCon, T`>`

.Note that in the implementation it means we'll have to replace all occurrences of

with `x`

, where `x .into_val()`

`into_val`

is part of the trait `Is`

(in the where clause of `TypeApp`

).Basically, this is the core of the approach. We just do a similar thing for Applicative and Monad, but there are a few details and snags that are run into which I'll detail below.

The above class is called

, whereas actually what I've detailed above it is called `Mappable`

in the library, which I'll also talk about now:`LinearFunctor`

## The structure of the library.

Currently, there are 7 main traits in the library:

`Functor``Lift``Applicative``Monad``LinearFunctor``LinearApplicative``LinearMonad`

Functor, Applicative and Monad are basically copies from Haskell, except the

function of `pure`

is split into the trait `Applicative`

. So both `Lift`

and `Lift`

extend `Applicative`

, and `Functor`

extends both `Monad`

and `Lift`

.`Applicative`

The

are the "by value" versions of `LinearFunctor /LinearApplicative/LinearMonad`

`Functor``/`Applicative`/`Monad

. These typeclasses consume their arguments. This can be an efficiency gain because they don't need to copy their arguments, it also means they can be defined on types which aren't `Cloneable`

.The standard prefix for a "normal" typeclass (i.e. handling data by reference) is either

(in Functor) or no prefix (otherwise), the "by value" versions (i.e. "linear") are prefixed by an `f`

. For example, `l`

is for Functor, and `fmap`

is for LinearFunctor.`lmap`

Note out of the

classes, `Linear *`

`LinearFunctor`

is a bit special. `Functor`

extends `LinearFunctor`

, that is, every `Functor`

is also a `LinearFunctor`

(but not the other way around). This is because if one defines `Functor`

, one can always define `LinearFunctor`

just by using a reference.Both

and `LinearApplicative`

take their function argument as an `LinearMonad`

argument. This is done, particularly in the case of `FnOnce`

, to allow chaining. Running a produced closure twice will probably require an explicit `LinearApplicative`

at some point. That means `.``clone``(``)`

and `LinearApplicative`

can be defined for far less types, generally ones that don't "multiply". Ie. whilst `LinearMonad`

is a `Vec`

it's not a `LinearFunctor`

or `LinearApplicative`

, `LinearMonad`

however is all of these.`Option`

Also all trait functions have plain old top-level functions that call them, as well as often having other trait functions that call them, although these trait functions are only for the purpose of allowing

notation. I'll go into some technical details about the reasoning for these now.`.`

## Technical details (particularly for implementors of traits)

I'm going to give the

trait as an example. You've seen code similar to this before, but this is the actual code:`LinearFunctor`

`//` Implement this trait for LinearFunctor
`pub` `trait` `LinearFunctor` `{`
`fn` `lmap``<`TIn, TOut`>``(`
`f``:` impl Fn`(``TIn``)` -> TOut,
`x``:` `<``Self` `as` WithTypeArg`<`TIn`>``>``::`Type,
`)`` ``->` `<``Self` `as` WithTypeArg`<`TOut`>``>``::`Type
`where`
`Self``:` `WithTypeArg``<`TIn`>` + `WithTypeArg``<`TOut`>`;
}
`//` Call this for lmap(f, x) syntax
pub fn `lmap``<`TCon, TIn, TOut`>``(`
f: impl Fn`(`TIn`)` -> TOut,
x: impl `TypeApp``<`TCon, TIn`>`,
`)` -> `<`TCon `as` WithTypeArg`<`TOut`>``>``::`Type
`where`
TCon`:` LinearFunctor + `WithTypeArg``<`TIn`>` + `WithTypeArg``<`TOut`>` + ?Sized,
`{`
`<`TCon `as` LinearFunctor`>``::`lmap`(`f`,` x`.``into_val``(``)``)`
`}`
`//` This is for x.lmapop(f) syntax
`pub` `trait` `LMapExt` `{`
`fn` `lmap``<`TCon, TIn, TOut`>``(``self`, `x``:` impl `TypeApp``<`TCon, TIn`>` + Sized`)`` ``->` `<`TCon `as` WithTypeArg`<`TOut`>``>``::`Type
`where`
`Self``:` Fn`(`TIn`)` -> TOut + Sized,
TCon`:` LinearFunctor + `WithTypeArg``<`TIn`>` + `WithTypeArg``<`TOut`>`
`{`
`lmap``(``self``,` x`)`
`}`
`}`
`impl``<`T`>`` LMapExt ``for`` ``T` `{``}`

There are three functions here, one is just a definition, the second is an implementation that calls the first, and the third calls the second.

The first is what you actually implement. Note that the arguments here are actual types. There's no

. This means, as discussed above, this function works poorly when it comes to type inference.`x : impl TypeApp<TCon, TIn>`

So the second function here, top level function, instead takes the

trait as it argument, which means it works better with type inference. But then here we need to worry about converting from the trait type to the "real" type, so we call `TypeApp`

from the `into_val``(``)`

crate to do this.`is_type`

The third function is just a trait so we can use the

syntax if we want. This becomes useful if we want to chain things like operators, i.e. `.`

.`f .lmap(x).lap(y)`

Note that only in the case of

, the function `Functor`

works with both by value and by reference arguments, and calls either `map`

or `fmap`

depending on whether the arguments are by value or by reference.`lmap`

## Do notation

The macro

allows one to write in "do-notation" form. This code is largely stolen (slightly modified) from rust-mdo.`!`mdo

Do notation is currently only defined with the by-reference version of the Monad functions, so you'll see

and `&`

in many places. Also, sometimes `Clone`` ::`clone

`(`

`...`

`)`

`Clone``::`clone

is better for type inference than `.``clone``(``)`

. I believe it's because the latter works for both values and references.## Quirks

There's a few little quirks one gets from this approch, which makes things slightly messier than equivalent Haskell code in some cases. I'll talk about features that could be added to Rust in this later section.

`WithTypeArg`

constraints everywhere

`WithTypeArg`

When writing generic functions over Functors, Applicatives or Monads, one has to use the constraint

all over the place. Indeed the function `WithTypeArg`

above doesn't actually work, one needs to write it like this:`map2`

` ``fn` `lmap2``<`TIn, TMid, TOut, TCon`>``(`
`f``:` impl Fn`(``TIn``)` -> TMid,
`g``:` impl Fn`(``TMid``)` -> TOut,
`x``:` impl `TypeApp``<`TCon, TIn`>`,
`)`` ``->` `<`TCon `as` WithTypeArg`<`TOut`>``>``::`Type
`where`
TCon`:` LinearFunctor + `WithTypeArg``<`TIn`>` + `WithTypeArg``<`TMid`>` + `WithTypeArg``<`TOut`>`
`{`
`lmap``(`g`,` `lmap``(`f`,` x`)``)`
`}`

Notice all the

constraints. One has to put this constraint for every type argument application that even exists in the function. For example in `WithTypeArg <TIn>`

`+`

`WithTypeArg`

`<`TMid`>``+`

`WithTypeArg`

`<`TOut`>``lmap2`

above, we need `WithTypeArg``<`TMid`>`

even though it's not an input or output argument, just because it's the result of the innermost map call.### Closure types can't be named

In stable rust, closure types can't be named, nor (as far as I could work out) can one define types that implement

(I think this requires extensions). This comes into play when trying to define some functions. For example, consider the definition of `Fn`

:`Applicative`

`pub` `trait` `Applicative`: Functor + Lift `{`
`fn` `ap``<`TIn, TOut, TFunc`>``(`
`f``:` `&``<``Self` `as` WithTypeArg`<`TFunc`>``>``::`Type,
`x``:` `&``<``Self` `as` WithTypeArg`<`TIn`>``>``::`Type,
`)`` ``->` `<``Self` `as` WithTypeArg`<`TOut`>``>``::`Type
`where`
`Self``:` `WithTypeArg``<`TFunc`>` + `WithTypeArg``<`TIn`>` + `WithTypeArg``<`TOut`>`,
TFunc`:` Fn`(``&`TIn`)` -> TOut,
`{`
`<``Self` `as` Applicative`>``::`lift2`(``|``y1``:` `&`TFunc`,` `y2``:` `&`TIn`|` `y1``(`y2`)``,` f`,` x`)`
`}`
`fn` `lift2``<`TIn1, TIn2, TOut, TFunc`>``(`
`f``:` TFunc,
`x1``:` `&``<``Self` `as` WithTypeArg`<`TIn1`>``>``::`Type,
`x2``:` `&``<``Self` `as` WithTypeArg`<`TIn2`>``>``::`Type,
`)`` ``->` `<``Self` `as` WithTypeArg`<`TOut`>``>``::`Type
`where`
`Self``:` `WithTypeArg``<`TIn1`>` + `WithTypeArg``<`TIn2`>` + `WithTypeArg``<`TOut`>`,
TFunc`:` Fn`(``&`TIn1, `&`TIn2`)` -> TOut;
}

We've defined

in terms of `ap`

, but it would be nice to define it the other way around also, so implementers could choose which to implement. Roughly speaking, the identity is as follows:`lift2`

`lift2``(`f`,` x`,` y`)` `=` f`.``fmap``(`x`)``.``ap``(`y`)`

But in Rust, without automatic currying, we'll need something like:

`lift2``(`f`,` x`,` y`)` `=` `(``|``x``|` `|`y`|` `f``(`x`,`y`)``)``.``fmap``(`x`)``.``ap``(`y`)`

So in this case, the output of

is an Applicative of Functions. Eventually `fmap`

applies and this becomes just values, but the intermediate step is a function.`ap`

But this function doesn't have a type we can name. As above, Rust wants a

constraint for every argument to our functions, but we can't write the type for this function because is doesn't have a type name.`WithTypeArg`

A practical issue with this is that you can't create say, an M<F>, where F is some function, inside a function. You have to always pass it in.

### Some fiddling with type inference

Notice there's actually two

functions defined (`lift``(``)`

is just `lift`

, but `pure`

was a former keyword so I've avoided it).`pure`

`//` lift(x)
`pub` `fn` `lift``<`TCon, T`>``(``x``:` T`)`` ``->` `<`TCon `as` WithTypeArg`<`T`>``>``::`Type
`where`
`<`TCon `as` WithTypeArg`<`T`>``>``::`Type `:` `TypeApp``<`TCon, T`>`,
TCon`:` Lift + `WithTypeArg``<`T`>`,
`{`
`<`TCon `as` Lift`>``::``lift``::``<`T`>``(`x`)`
`}`
`//` lift_c(x)
`pub` `fn` `lift_c``<`TCon, T, U`>``(``x``:` `U``::`Param`)`` ``->` U
`where`
T `:` `Is``<`Type = `U``::`Param`>`,
U `:` `TypeApp``<`TCon, T`>`,
`<`TCon `as` WithTypeArg`<`T`>``>``::`Type `:` `TypeApp``<`TCon, T`>`,
TCon`:` Lift + `WithTypeArg``<`T`>`,
`{`
`Is``::`from_val`(``lift``::``<`TCon, T`>``(``Is``::`from_val`(`x`)``)``)`
`}`

They both do the same thing, indeed the second just calls the first, but their effect on type inference is different.

The first doesn't allow the inference to go backwards. For example, if one goes:

`x``:` `Option``<``u32``>` `=` `lift``(``5``)``;`

then the compiler will not be able to work this out, because

could be many different numeric types.`5`

But if one goes like this:

`x``:` `Option``<``u32``>` `=` `lift_c``(``5``)``;`

It will be able to work out from the result type that

is a `5`

.`u32`

The problem is that

doesn't work as well in generic functions over, say, all monads.`lift_c`

So think of

as "lift concrete", as in we have a concrete type.`lift_c`

There's a similar division with

and `bind`

.`bind_c`

As a result also, there's both

and `!`mdo

macros, `!`mdo_c

should be used in generic code, but `!`mdo

may be used when one knows the concrete types as there may be better inference in that case.`!`mdo_c

One example of this is as follows:

`let` o1`:` `Option``<``u32``>` `=` `lift_c``(``5``)``;`
`let` o2`:` `Option``<``u32``>` `=` `lift_c``(``7``)``;`
`let` _do_result`:` `Option``<``(``u32`, `u32``)``>` `=` `mdo_c!` `{`
x `=``<``<` `&`o1`;`
y `=``<``<` `&`o2`;`
ret `(``Clone``::`clone`(`x`)``,` `Clone``::`clone`(`y`)``)``;`
`}``;`

Notice here one doesn't have to be explicit about the type constructor of the return value out of the macro.

But in

:`monadic_pair`

` ``fn` `monadic_pair``<`TCon, T, TArg`>``(``x``:` `&`TArg, `y``:` `&`TArg`)`` ``->` `<`TCon `as` WithTypeArg`<``(`T, T`)``>``>``::`Type
`where`
TCon`:` Monad + `WithTypeArg``<`T`>` + `WithTypeArg``<``(`T, T`)``>`,
TArg`:` `TypeApp``<`TCon, T`>`,
T`:` Clone,
`{`
`mdo!` `{`
x_val `=``<``<` x`;`
y_val `=``<``<` y`;`
`ret``<`TCon`>` `(`x_val`.``clone``(``)``,` y_val`.``clone``(``)``)``;`
`}`
`}`

We have to explicitly specify the type constructor of the result of the do-block, namely

. Rust can't infer this, unfortunately.`TCon`

## Features that could be added to Rust to make things nicer

Overall, despite this looking a bit messy in some places, for client code, it's not too bad. Generic functions over monads are a bit messy to define, but if what you're doing mostly is just using these functions on a particular concrete monad, the code is actually fairly clean. But here's some things that would help:

### Higher kinded types (HKT)

An actual implementation and syntax for higher kinded types would be ideal, as we wouldn't need to do the "hack things into a trait" thing to make this work, and presumably type inference would also be nicer if this were done. But failing that, there are a few features which would go a long way to help:

### Forall in where clauses

If we could define something like:

`trait` `TypeCon`<TCon> where TCon : forall U. WithTypeArg<U>

This would solve the

constraints everywhere issue. It would also solve the closure types can't be named issue because they'd be no need to explicitly name such temporary types.`WithTypeArg`

### Generic associated types (GAT)

This is an actual feature under development which may help, similarly to a "forall" option, but the particular part of this feature needed hasn't landed in nightly yet and has no particular timeline, according to this issue reply.

## Future work

There's a few obvious things to be done, firstly implementing Functor/Applicative/Monad for types other than

and `Option`

, `Vec`

is the obvious next choice, along with other things in the standard Rust library.`Result`

is also a possibility, and I think implementing some of Haskell's `IO`

could allow for some good illistrative examples of the power of this.`Parsec`

Also, some of the more useful monadic functions, like

require traversable and foldable, so these are the obvious traits to implement next.`mapM`

## Where to start digging around the code.

There's a whole lot of test code dumped in the

function in `test``(``)`

, that's probably the best place to start as there's plenty of examples of usage there.`lib.rs`

## Discussion and comments welcome!

The whole point of this was to open up discussion, particularly on core aspects of the design at this point, so please feel free to open up a github issue if you have any suggestions. Or even open up a PR if those changes are more concrete or would just like to add some functions, traits or implementations.