-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support deriving LabelledGeneric on enums #158
Conversation
(Rebased and reformatted) |
Sorry for the delay in reviewing this a bunch of life things came up and I'm only currently catching up with work and other things. Just took an overall look at this PR, and it looks really good ! Will try to find some time over the weekend to give it a detailed review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It occurs to me, that the current implementation has the following LabelledGeneric
representation for a given enum
enum Maybe<A> {
Some(x: A),
None
}
type Repr = Coprod!(
Field< Some, Hlist![Field<x, A> ]>,
Field<None, Hlist![]>
)
That is, it's a Coproduct of fields, and the names of the variants are the type-level labels. In my mind, at least, I was thinking that it would just be a Coproduct of the LabelledGeneric representations of the variants, e.g.
type Repr = Coprod!(
Hlist![Field<x, A> ],
Hlist![]
)
Without really having thought about it too much; other than:
- Simpler to implement transmogrification (I think..)
- Not quite sure if people care about variant names, when it comes to transmogrifying enum types into each other?
Having said that though, if I'm reading it right, the documentation for GHC.Generics
does seem to suggest that it might be a good idea to keep the variant name around for sum types (or at least some variant of it, like Con_Node
). Anyway, just thought it might be a good idea to discuss this :) cc @Centril @ExpHP for thoughts
If we do want to label the variants or keep some remnant of their names around though, perhaps it might make sense to have another type (e.g. not Field
, maybe Member
?
Yeah, for my own use case, the names of the variants are essential, as I'm using frunk to implement conversions between "similar" but separate data structures, and it allows me to generically convert from one enum to another. The reason I did this was for two reasons:
However, if you think it would be worth adding all four combinations:
Then that would also work. My eventual goal is to implement something like "transmogrify" in that it uses the field names, but that can be "driven" by the return type. So instead of saying "map X to whatever F(X) happened to return", you could say "convert X to Y" and it would recursively:
I got very close, (got it working at the struct level) but haven't been able to quite get the inference working for coproducts yet 😢 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, for my own use case, the names of the variants are essential, as I'm using frunk to implement conversions between "similar" but separate data structures, and it allows me to generically convert from one enum to another.
The reason I did this was for two reasons:
- If you just care about the types, you could derive
Generic
instead ofLabelledGeneric
- If you care about the field names but not the variant names, you could just map away the
Field
/Variant
type easily enough.However, if you think it would be worth adding all four combinations:
- Unlabelled
- Labelled fields
- Labelled variants
- Labelled fields and variants
Then that would also work.
My eventual goal is to implement something like "transmogrify" in that it uses the field names, but that can be "driven" by the return type. So instead of saying "map X to whatever F(X) happened to return", you could say "convert X to Y" and it would recursively:
- Map the field/variant names.
- Figure out the "from"/"to" types for each field or variant.
- Apply the correct instantiation of "F<From, To>" in order to perform the conversion.
I got very close, (got it working at the struct level) but haven't been able to quite get the inference working for coproducts yet 😢
That makes sense to me. If we don't hear back from @ExpHP in a few days, I think we can merge this in :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have many thoughts on this right now. Whatever works, works!
Co-Authored-By: Michael Lamparski <diagonaldevice@gmail.com>
Awesome stuff ! Thanks for the contribution @Diggsey! |
(Based off #157)
I reused the
Field
type to represent named enum variants.This is just the first step - to make enums actually useful we'll need to:
Another issue I found is the
field!
macro - the way it generates the field name is inconsistent due to limitations of the macro system (eg.field!((_, _0), ...)
has the name__0
when it should be_0
). It should be possible to implement it as a procedural macro, so that one can just writefield!("_0", ...)
and not have to worry about the encoding.