safecopy
safecopy copied to clipboard
Unnecessary constraint in generated SafeCopy instance
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...
I've commented on a similar issue in https://github.com/mboes/th-lift/issues/20#issuecomment-157955277, 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.
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.
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.