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

Unnecessary constraint in generated SafeCopy instance #46

Open
ddssff opened this issue Aug 29, 2016 · 3 comments
Open

Unnecessary constraint in generated SafeCopy instance #46

ddssff opened this issue Aug 29, 2016 · 3 comments

Comments

@ddssff
Copy link
Collaborator

ddssff commented Aug 29, 2016

The Proxy type in Data.Proxy looks like

data Proxy t = Proxy

It has a type parameter t, but no actual data of that type. However, the SafeCopy instance that deriveSafeCopy generates has the unnecessary constraint

instance SafeCopy t => SafeCopy (Proxy t) where...
@RyanGlScott
Copy link
Contributor

I've commented on a similar issue in RyanGlScott/th-lift#20 (comment), but I'll reiterate my comments here.

What you're aiming for is something tantamount to implementing GHC's type inference in Template Haskell. Well, maybe you didn't say that outright, but if you want to do this The Right Way, that's what you'd need. That is, you'd need some way to infer the correct constraints for any derived SafeCopy, and in general that's quite tricky to do.

This example might be straightforward enough to implement (just check which type variables don't appear as fields of any constructor), but it certainly wouldn't over all cases. For example, you might have:

newtype Wrap f a = Wrap (f a)

Currently, deriveSafeCopy thinks this instance should get a context of (SafeCopy f, SafeCopy a), when it should actually be (SafeCopy (f a)).

In my opinion, the best solution to these sorts of problems is to expose the ability to splice in method definitions directly. For instance, aeson exposes the function mkParseJSON which allows you to do this:

instance FromJSON (f a) => FromJSON (Wrap f a) where
  parseJSON = $(mkFromJSON defaultOptions ''Wrap)

Now you can specify whatever context you wish while still eliminating most of the boilerplate using Template Haskell. And importantly, this entirely sidesteps the thorny issue of type inference.

In my opinion, an approach like this is the way to go to solve the issue you're having.

@ddssff
Copy link
Collaborator Author

ddssff commented Jan 12, 2017

The most unpleasant part of many days is the moment when I realize I need access to GHC's type inference engine. It happens fairly often. In this case I wrote a custom instance of SafeCopy for Proxy.

@ddssff
Copy link
Collaborator Author

ddssff commented Sep 2, 2017

I've actually solved this problem here: https://github.com/seereason/th-typegraph. The solution is, as @RyanGlScott implied, a deep traversal of the "contains" relation on the types to discover which of the type variables actually refer to concrete types and which are phantoms. There is an improved implementation of deriveSafeCopy included in the th-typegraph package. (Also includes implementations of makeAcidic and derivePathInfo.)

Tangentially related: It also adds a Migrate Foo superclass to the SafeCopy Foo instance if the kind field is "extension". It may or may not be worth incorporating all this into the safecopy package.

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

No branches or pull requests

2 participants