lloydmeta / frunk

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

`Coproduct::map()` #203

Closed BGR360 closed 1 year ago

BGR360 commented 1 year 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 1 year 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