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
Generic Deriving #1138
Generic Deriving #1138
Conversation
(Also note the silliness in how I had to write the Proxy type in the gist. This may be fixed in 7, but in 6.9.5 doing the obvious phantom type thing failed) |
This is awesome! |
The Gist is now mainly updated to work with purescript 0.7. Either the typechecker got better or the explicit ascription trick for Proxy worked or both, as now I can do things the sane way. Since it was a very quick translation, the code for genericShow is now especially ugly -- but it was always included simply as a demonstration and not a proper implementation anyway. |
The library component to go with this now lives here, btw: purescript-deprecated/purescript-generics#3 |
lamCase s = lam s . Case [mkVar s] | ||
|
||
liftApplicative :: Expr -> [Expr] -> Expr | ||
liftApplicative = foldl' (\x e -> App (App (mkPrelVar "ap") x) e) |
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.
Maybe use apply
here to avoid an indirection via Monad
in the generated code?
Sorry this PR has been left unmerged for a while. Here are my thoughts on it:
newtype Generic a = Generic
{ toSpine :: a -> Spine
, toSignature :: Proxy a -> GenericSignature
, fromSpine :: GenericSpine -> Maybe a
}
showGeneric :: forall a. Generic a -> a -> String This way, wrapping up |
I'd be happy to make the changes requested if we can agree that then this could be merged :-) On the concern that this modifies the typechecker... I understand this. However, bear in mind that the only change to the typechecker proper is a "one liner" as follows: +check' e@TypeClassInstanceMemberFunction{} ty = flip check' ty =<< elaborateInstance e This should be really robust to virtually any refactor. If there was an "elaboration" phase where we had more information available but prior to the checking phase, then the checker wouldn't need to be changed at all, imho. I've been thinking about this for a few ways now, and given a desire to not have orphans, I don't see how this can be a standalone tool 😢 In particular, we don't just want to do "instance Generic Foo" but then also "instance Show Foo where show = genericShow" etc. So being able to derive generic in the same file as the definition isn't just about putting it to work elsewhere, but then piggybacking on that to derive a lot of other instances. And since those other instances need to be "real" instances not handed a manual dictionary -- i.e. so that the Show instance can be used by something that shows pairs of things, etc, then there isn't much of a way around this... I think the power/weight ratio remains good here, since the idea is that by deriving Generic, then "once and forall" a whole bunch of userland things become possible at once, and the net effect is to take a whole bunch of other potential feature requests and allow them to be undertaken in userland instead. So if you'll agree to merge once I make the requested changes, I'll proceed? |
let missingDecls = mapMaybe declName tyDecls \\ mapMaybe declName decls | ||
let mkMissingDecl cls fun = | ||
case tys of | ||
[TypeConstructor t] | unQualify className == ProperName cls && Ident fun `elem` missingDecls -> return [ValueDeclaration (Ident fun) Public [] (Right $ TypeClassInstanceMemberFunction (Ident fun) className t)] |
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 think we'd want to match the whole qualified name, since having something called Generic
in a different not-generics module shouldn't apply, right?
I take it these additional comments indicate a willingness to pull? The qualified names choice was deliberate, but is open to discussion. The idea of pulling whatever "Generic" is in scope is to let users swap in whatever Generic class they want, if they don't like the version in that particular package -- as long as instances can be given in the same way, you could write something that worked over an alternate sort of skeleton datatype, etc. I wanted to be as general-purpose and agnostic as possible. If you think that this will be more confusing than useful, then I can of course change it to specify. |
I think you're right about the power-to-weight ratio being well worth it. I put a couple of other minor comments on the code above. Also, I guess we'd need to update the I don't think the qualified name thing is about "what's in scope" so much as, what if someone doesn't know this feature exists and defines a And yes regarding willingness to pull 😄 Could you please add a link to the MIT license to the top of your new file as well? We're probably going to go through and do this for other files too. I know it says |
Ok. So I'll go ahead and try to clean all the above. And I'll go ahead and add qualified names for now to point to "Data.Generic.Generic". We can always revisit and generalize later. |
|
||
toSpineFun :: Expr -> Type -> Expr | ||
toSpineFun i (ObjectType rec) = | ||
lam "q" . recordConstructor . ArrayLiteral . |
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.
Might this q
shadow some other name? Why not use a NullBinder
?
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.
didn't knwo about nullbinders is why 😄. That said, the shadowing won't hurt afaik because there shouldn't be any names inside the thing we're building... But I'll go put in null binders where appropriate.
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.
true but you might shadow something at the top level.
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.
Not that it matters, but it will generate a warning.
Ok, changed things vis a vis constants and null binders. Still have yet to force only using "Data.Generics" as the modulename, but otherwise things seem to still be working. |
I think I've made all requested changes and tested. To integrate with the main testsuite it would make sense to have the new generic module on bower since that's where the testsuite seems to pull the modules from, so that can be a second pass. |
I'll merge your |
Also, thanks! 👍 |
@gbaz I'm getting ready to merge this, thanks. From above:
|
License link added. I haven't added something to use proxy from the proxy library yet. To be honest I'd just like to tie off this pull request as is and do that as a separate patch next :-) |
Yep, sounds good. Thanks 👍 |
I'll merge this and then work on adding tests later today. |
Hmm, CI build is failing on 7.6.3 because of |
done |
Let's see what CI says this time... |
Addresses issue #647 among other features.
Works hand in hand with userland library code as in https://gist.github.com/gbaz/a2c867b6c07731a403eb