Skip to content
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

Merged
merged 2 commits into from
Jul 26, 2019

Conversation

Diggsey
Copy link
Contributor

@Diggsey Diggsey commented Jun 24, 2019

(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:

  • Support "taking", etc. by field name only.
  • Implement a general-purpose "sculpt"-like function which works on HLists/Coproducts of Fields, and only uses the field's name to determine the sculpting to perform.
  • Make this work recursively, so that you can re-order struct-like variants and the fields within those variants at the same time.

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 write field!("_0", ...) and not have to worry about the encoding.

@Diggsey
Copy link
Contributor Author

Diggsey commented Jun 29, 2019

(Rebased and reformatted)

@lloydmeta
Copy link
Owner

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.

Copy link
Owner

@lloydmeta lloydmeta left a 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:

  1. Simpler to implement transmogrification (I think..)
  2. 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?

@Diggsey
Copy link
Contributor Author

Diggsey commented Jul 8, 2019

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 of LabelledGeneric
  • 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 😢

Copy link
Owner

@lloydmeta lloydmeta left a 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 of LabelledGeneric
  • 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 :)

Copy link
Collaborator

@ExpHP ExpHP left a 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!

derives/src/derive_labelled_generic.rs Outdated Show resolved Hide resolved
Co-Authored-By: Michael Lamparski <diagonaldevice@gmail.com>
@lloydmeta lloydmeta merged commit c7e33a0 into lloydmeta:master Jul 26, 2019
@lloydmeta
Copy link
Owner

Awesome stuff ! Thanks for the contribution @Diggsey!

lloydmeta added a commit that referenced this pull request Dec 21, 2019
## [0.3.1] - 2019-12-21
- Refactoring derives (#157)
- Add support for deriving LabelledGeneric on enums (#158)
- Added HZippable (#160)
- Add a type macro for paths (#161)
@lloydmeta lloydmeta mentioned this pull request Dec 21, 2019
lloydmeta added a commit that referenced this pull request Dec 21, 2019
## [0.3.1] - 2019-12-21
- Refactoring derives (#157)
- Add support for deriving LabelledGeneric on enums (#158)
- Added HZippable (#160)
- Add a type macro for paths (#161)
@Diggsey Diggsey deleted the enum-derives branch May 24, 2020 01:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants