lloydmeta / frunk

Funktional generic type-level programming in Rust: HList, Coproduct, Generic, LabelledGeneric, Validated, Monoid and friends.
https://beachape.com/frunk/
MIT License
1.29k stars 58 forks source link

`Coproduct::map()` #203

Closed BGR360 closed 2 years ago

BGR360 commented 2 years ago

I'm tinkering around with developing an algebraic effect system in Rust.

Heavy inspiration is taken from the old eff project. The basic idea is that each effectful function is a Generator that yields Coprod!(Each, Different, Effect, Raised):

struct A;
struct B;
struct C;

impl Effect for A {
    type Output = ();
}
impl Effect for B {
    type Output = i32;
}
impl Effect for C {
    type Output = ();
}

fn make_effectful_function() -> impl Effectful<Output = String, Effect = Coprod!(A, B, C)> {
    || {
        let a: () = raise!(A);
        let b: i32 = raise!(B);
        let c: () = raise!(C);

        b.to_string()
    }
}

With each of those raise! macros expanding to something like:

{
    type Effect = Coprod!(A, B, C);
    type Output = Coprod!((), i32, ());

    let (send, recv): (Sender<Effect>, Receiver<Output>) = channel();
    yield (Effect::inject(B), send);
    let output = recv.recv();
    let output = output.uninject::<i32, _>(output); // in reality this remembers the Index used to inject B
    output
}

Coproduct::map() for handling effects

Whoever drives these effectful computations needs to handle the raised effects, mapping each possible effect to its proper output type. Here's how that could work if Coproduct had a map() like HList does:

let mut effectful = make_effectful_function();

let result = loop {
    match effectful.poll() {
        Poll::Complete(result) => break result,
        Poll::Effect(effect, resume) => {
            let output = effect.map(poly![
                |A| (),
                |B| 100,
                |C| (),
            ]);
            resume.send(output);
        },
    }
}

Way forward?

I'd be willing to take a stab at adding these bits of functionality, but don't know how I'd best go about that. Like, should I repurpose HMappable to also work with Coproduct? Or should I create a brand new CMappable?

lloydmeta commented 2 years ago

Like, should I repurpose HMappable to also work with Coproduct? Or should I create a brand new CMappable?

I think it would make sense to have a new trait, similar to

https://github.com/lloydmeta/frunk/blob/bb4e8d51f0edb582e56731e2482850b80febab22/core/src/coproduct.rs#L753-L764

being an Coproduct version of

https://github.com/lloydmeta/frunk/blob/bb4e8d51f0edb582e56731e2482850b80febab22/core/src/hlist.rs#L1281-L1294