Skip to content

Commit

Permalink
[ example ] add test with monad other than Option or Result
Browse files Browse the repository at this point in the history
  • Loading branch information
AlgebraicWolf committed Jan 6, 2024
1 parent 5e3aebd commit c1a6372
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 1 deletion.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,15 @@ This Rust version aims to be as general as possible.
It is supposed to work with any type that provides `and_then` method with a suitable signature.
If you found any example where this doesn't work, let me know!

## Order of binding
## Is this any better than `?` operator?

Yes! AFAIK, currently `?` operator works only with `Option` and `Result` types.
There is an [experimental `Try` trait](https://doc.rust-lang.org/std/ops/trait.Try.html) that could be used to overload `?` operator,
but it provides semantics different to `!`-notation in this crate.
Unlike `?`, `!`-notation is applicable to arbitrary types that provide a suitable interface for `and_then`.
As an example, you might want to take a look at `list001` test that uses a custom-written `List` monad.

## In what order are values bound?

The expressions marked with `!` are bound left-to-right, in order they are written in the source code.
In case of nested expressions marked with `!`, the inner expressions are bound first.
Expand Down
76 changes: 76 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,79 @@ fn order002() {
assert_eq!(res.tag[i], i.try_into().unwrap());
}
}

// For fun
#[derive(Debug)]
enum List<T> {
Nil,
Cons(T, Box<List<T>>),
}

impl<T: Clone> Clone for List<T> {
fn clone(&self) -> Self {
match self {
Self::Nil => Self::Nil,
Self::Cons(arg0, arg1) => Self::Cons(arg0.clone(), arg1.clone()),
}
}
}

impl<T> List<T> {
fn new() -> List<T> {
List::Nil
}

fn from_vec(mut xs: Vec<T>) -> List<T> {
let mut list = List::Nil;

xs.reverse();
for elem in xs {
list = List::Cons(elem, Box::new(list))
}

list
}

fn pure(x: T) -> List<T> {
List::Cons(x, Box::new(List::Nil))
}

fn append(self, ys: List<T>) -> List<T> {
match self {
List::Nil => ys,
List::Cons(x, xs) => List::Cons(x, Box::new(xs.append(ys))),
}
}

fn and_then<F, U>(self, f: F) -> List<U>
where
F: FnOnce(T) -> List<U>,
F: Clone,
{
match self {
List::Nil => List::Nil,
List::Cons(x, xs) => f.clone()(x).append(xs.and_then(f)),
}
}
}

impl<T: PartialEq> PartialEq for List<T> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Cons(l0, l1), Self::Cons(r0, r1)) => l0 == r0 && l1 == r1,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}

impl<T: Eq> Eq for List<T> {}

#[test]
fn list001() {
let xs = List::from_vec(vec![1, 3, 5]);
let ys = List::from_vec(vec![2, 4, 6]);

let zs = bang!(List::pure(!xs + !ys));
let zss = List::from_vec(vec![3, 5, 7, 5, 7, 9, 7, 9, 11]);
assert_eq!(zs, zss);
}

0 comments on commit c1a6372

Please sign in to comment.