Search code examples
genericsrustgadt

GADTs for multiple generics transformations in Rust


I'm trying to design an execution graph with Recursive Generics Enums in Rust. I want to make an eval function which computes a predefined map closure recursively, but each map function could change the enum type so I get a compilation error due the different types. Here is my code:

use std::rc::Rc;

enum ExecutionGraph<T, H, F = fn(T) -> H> {
    Cons(Vec<T>),
    Map(F, Rc<ExecutionGraph<T, H>>),
}

// Impl for add a Cons "node" or a Map "node"
impl<T, H> ExecutionGraph<T, H, fn(T) -> H> {
    fn new_data(data: Vec<T>) -> Self {
        ExecutionGraph::Cons(data)
    }

    fn add_map(a_function: fn(T) -> H, execution_graph: ExecutionGraph<T, H, fn(T) -> H>) -> Self {
        ExecutionGraph::Map(a_function, Rc::new(execution_graph))
    }
}

// Complete evaluation of the execution graph
fn eval<T, H>(execution_graph: &ExecutionGraph<T, H, fn(T) -> H>) -> Vec<T>
where
    T: std::clone::Clone,
    std::vec::Vec<T>: std::iter::FromIterator<H>,
{
    match execution_graph {
        ExecutionGraph::Cons(data) => data.to_vec(),
        ExecutionGraph::Map(closure, e1) => eval(&(*e1)).into_iter().map(closure).collect(),
    }
}

fn main() {
    let execution_graph = ExecutionGraph::new_data(vec![1, 2, 3]);

    // let map_function: fn(u32) -> u32 = |x: u32| x * 8; // This work!
    let map_function: fn(u32) -> bool = |x: u32| x % 2 == 0; // This doesn't
    let execution_graph_2 = ExecutionGraph::add_map(map_function, execution_graph);
    let execution_result = eval(&execution_graph_2);

    println!("Result of execution = {:#?}", execution_result);
}

The add_data and add_map functions work as expected. But then when I call the eval function I'm getting the following error:

   |     let execution_result = eval(&execution_graph_2);
   |                                 ^^^^^^^^^^^^^^^^^^ expected u32, found bool
   |
   = note: expected type `&ExecutionGraph<_, _>`
              found type `&ExecutionGraph<u32, bool>`>`

Why am I getting this error? I defined to get a generic argument and a generic function which could change the result type. How could allow all possible transformations in the data? In the future, there will be a lot of more functions with the same problem, such as a cartesian product which returns a tuple. Note that if the closure has the same return type as the input there is no problem. If someone could give me a hand with this problem I would appreciate it.


Solution

  • As mentioned by a user in my question in the official forum of Rust. This language has no support for Generalized Algebraic Data Types (GADTs) which would be the right tool to solve this kind of problems.

    As an alternative, instead of proposing a scheme where the types belong to the ENUM making it not very flexible, a class approach is proposed, which is allowed to introduce new types.

    I do not put the code here so that it can be consulted directly in the question with the edits or new answers that may arise.