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

Is there a way to map enum -> coprod in-place? #165

Closed hirrolot closed 4 years ago

hirrolot commented 4 years ago

I have an enumeration from a third-party crate, I want to map it to a coproduct in-place. So basically I need to match it, but it's a lot of boilerplate, isn't it? Is there a solution?

ExpHP commented 4 years ago

What do you mean by in-place? Normally I take that to mean "without copying data", but Coproducts and enums have different layouts so copying is required.

Without knowing the full details of your problem, the boilerplatey-ness of conversion of any type into a frunk type is usually best handled by leveraging derive(Generic) or derive(LabelledGeneric) in some way. Of course, you cannot do this directly for a type in an external crate, but you can define a similar looking type in your crate, derive the traits on that, and write simple conversions between your type and the original. This is still boilerplate, but hopefully not as much. (a macro can often take care of the to/from conversions as well).

hirrolot commented 4 years ago

By "in-place" I mean that given a variable foo of an enumeration type in a third-party crate, convert it to a coproduct representation. Are there macros for it?

ExpHP commented 4 years ago

A macro can only generate code based on the input tokens provided to it. There is no form of reflection or type system access that could allow it to discover what the variants of an enum are called (or what they contain) without all of this information being provided directly to the macro.

So, no matter what, your crate will at bare minimum need to contain some sort of listing of all of the variants of this enum and all of their fields. (ideally, this may just mean copying and pasting the enum definition from the external crate). After that, the general idea is to put #[attribute]-style macros on it or place it inside some my_autoderiving_macro! call to generate all the rest of the boilerplate you need.

hirrolot commented 4 years ago

Ok, thank you for the help, just expected that someone has already written a macro like this:

enum Foo {
    A(i32),
    B(String),
}

type MyCoprodType = Coprod!(i32, Foo);

fn main() {
    let foo: MyCoprodType = magic_macro!(Foo, (A: i32), (B: String), Foo::A(123));
    assert_eq!(foo, MyCoprodType::inject(123));
}