safecopy icon indicating copy to clipboard operation
safecopy copied to clipboard

Unnecessary constraint in generated SafeCopy instance

Open ddssff opened this issue 8 years ago • 3 comments

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...

ddssff avatar Aug 29 '16 17:08 ddssff

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.

RyanGlScott avatar Jan 08 '17 20:01 RyanGlScott

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 avatar Jan 12 '17 00:01 ddssff

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.

ddssff avatar Sep 02 '17 15:09 ddssff