1

I'm using a trick I've learnt in F# to simulate existential types, and I want to see if this works in Rust:

/*
To replicate:
type SuperArrow<'a, 'b> = 
    | Fn of (('a -> 'b) * string)
    | Compose of SuperArrowCompose<'a,'b>
and SuperArrowCompose<'a,'b> = 
    abstract member eval : SuperArrowComposeEvaluator<'a, 'b, 'r> -> 'r
and SuperArrowComposeEvaluator<'a, 'b, 'r> = 
    abstract member apply<'c> : SuperArrow<'a, 'c> -> SuperArrow<'c, 'b> -> 'r
*/

//#[derive(Debug)]
struct MyFnWrapper<A, B> {
    f: Box<Fn(A) -> B>,
    n: String,
}

impl<A, B> MyFnWrapper<A, B> {
    pub fn new<F: 'static + Fn(A) -> B>(f: F, s: String) -> Self {
        MyFnWrapper {
            f: Box::new(f),
            n: s,
        }
    }
}

trait SuperArrowComposeEval<A, B, R> {
    fn apply<C>(&self, &SuperArrow<A, C>, &SuperArrow<C, B>) -> R;
}

trait SuperArrowCompose<A, B> {
    fn eval<R, E: SuperArrowComposeEval<A, B, R>>(&self, &E) -> R;
}

struct SuperArrowComposeImpl<A, B, C> {
    fnA: Box<SuperArrow<A, C>>,
    fnB: Box<SuperArrow<C, B>>,
}

impl<A, B, C> SuperArrowComposeImpl<A, B, C> {
    fn new(fnA: SuperArrow<A, C>, fnB: SuperArrow<B, C>) {
        SuperArrowComposeImpl {
            fnA: Box::new(fnA),
            fnB: Box::new(fnB),
        }
    }
}

impl<A, B, C> SuperArrowCompose<A, B> for SuperArrowComposeImpl<A, B, C> {
    fn eval<R, E: SuperArrowComposeEval<A, B, R>>(&self, evaler: &E) -> R {
        evaler.eval::<C>(self.fnA, self.fnB)
    }
}

//#[derive(Debug)]
enum SuperArrow<A, B> {
    Function(MyFnWrapper<A, B>),
    Compose(Box<SuperArrowCompose<A, B>>),
}

(gist)

The double-interface layer hides the intermediate 'c existential type in the composition behind the first interface (SuperArrowCompose<'a,'b> in the comment at the top).

The reason this works in F# is that it can dynamically generate the code needed for the 'r-specific implementation at runtime. This clearly isn't possible in Rust and it won't allow me to have a trait with generic type parameters:

error[E0038]: the trait `SuperArrowCompose` cannot be made into an object
  --> existential.rs:57:13
   |
57 |     Compose(Box<SuperArrowCompose<A,B>>),
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `SuperArrowCompose` cannot be made into an object
   |
   = note: method `eval` has generic type parameters

It needs to be generic to allow it to be generic on the return type, but at the same time it needs to be dynamic to hide the specific C type that joins the two functions.

4

0 回答 0