
Richard,
Right, but I was thinking about the debate between
"nominal/non-parametric-by-default" or
"representational/parametric-by-default" for parameters of data types
that aren't forced to nominal from inspecting the datatype
implementation. As I understand it, representational-by-default
(currently used) may leave libraries that don't add role annotations
open for abuse, but won't unnecessarily break library users' code and
nominal-by-default prevents all abuse but may unnecessarily break code
that uses libraries that have not added proper role annotations.
What I was wondering about is if the dilemma could be solved by
choosing nominal-by-default in the long term for the role inference
(so that library writers cannot accidentally leave abstraction holes
open by forgetting to add role annotations) and use them in the
long-term-supported SafeNewtypeDeriving extension, but provide a
deprecated not-quite-as-safe GND extension for helping out users of
libraries that have not yet added role annotations. I would fancy that
this not-quite-as-safe GND could use unsafeCoerce wherever the safe
one would give an error about annotated roles.
Regards,
Dominique
2014-03-31 17:05 GMT+02:00 Richard Eisenberg
Hi Dominique,
When implementing roles, I was indeed worried about the problem you're addressing: that code that previously worked with GND now won't. However, it turns out that few people have really complained about this. IIRC, in all of Hackage, only 3 packages needed to be changed because of this. If there were a larger impact to the GND breakage, I think your suggestion would be a good one.
The problem I'm adressing in this thread is different: that library authors have been given a new, not-backward-compatible way of preventing abuses of their datatypes, and no proposal I have seen really addresses all of the problems here. I'm hoping my no-role-annots package might be helpful, but it doesn't fully resolve the issues.
Richard
On Mar 31, 2014, at 2:51 AM, Dominique Devriese
wrote: Richard,
(re-posting because I first used an address that is not subscribed to the lists)
I've been wondering about the following: it seems like the main problem in this situation is that the GeneralizedNewtypeDeriving extension changed meaning from "just coerce everything while deriving" to "only coerce stuff if it's allowed by the relevant role annotations". Would it not be an alternative solution to split up the GND extension into
* a backwards-compatible one (called GeneralizedNewtypeDeriving for backwards compatibility ;)) that ignores role annotations (as before) and uses unsafeCoerce whereever necessary * a safe one (called e.g. SafeNewtypeDeriving) that respects role annotations
The first one could then be deprecated and removed in a release or two. That might give library maintainers time to move their packages to SafeNewtypeDeriving when they have tested that everything works...
Regards, Dominique
P.S.: The above is based on a limited understanding of the problem, so I'm sorry if it misses some aspect of the problem...
2014-03-31 2:14 GMT+02:00 Richard Eisenberg
: I spent some time thinking about what, precisely, can be done here to make folks happier. (See the thread beginning here: http://www.haskell.org/pipermail/libraries/2014-March/022321.html) And, the answer seemed to all be in the concrete syntax. The only logical alternative (that I could think of) to having roles is to disallow GND, and I don't think anyone is proposing that. And, it is impossible to infer an author's desired roles for a datatype. The heuristics mentioned here all seem far too fragile and hard to predict to become a lasting feature of GHC (in my opinion). What's left? Concrete syntax.
So, I have written and uploaded no-role-annots-1.0, a backward-compatible alternative to role annotations -- no CPP required. It's not as principled as proper role annotations, but it should do the job for most users.
Here are two examples:
1. Datatypes:
import Language.Haskell.RoleAnnots
data Map k v = (Nominal k, Representational v) => MkMap [(k,v)]
The constraints (which need be put on only one data constructor, if there are many) will get the built-in role inference mechanism to do what the user requests. In this example, the `Representational v` is actually redundant, but causes no harm. Because these classes have universal instances ("instance Nominal a") and have no methods, they should have no run-time significance. The only downside I can see is that the code above needs -XGADTs or -XExistentialQuantification to work, though it is neither a GADT nor has existentials. (Pattern-matching on such a definition needs no extensions.)
2. Newtypes:
Newtype constructors cannot be constrained, unfortunately. So, we have to resort to Template Haskell:
import Language.Haskell.RoleAnnots
roleAnnot [NominalR, RepresentationalR] [d| newtype Map k v = MkMap [(k, v)] |]
This is clearly worse, but I was able to come up with no other solution that worked for newtypes. Note that, in the example, I used the fact that Template Haskell interprets a bare top-level expression as a Template Haskell splice. We could also wrap that line in $( ... ) to be more explicit about the use of TH. Also problematic here is that the code above requires -XRoleAnnotations in GHC 7.8. To get this extension enabled without using CPP, put it in a condition chunk in your .cabal file, like this:
if impl(ghc >= 7.8) default-extensions: RoleAnnotations
I hope this is helpful to everyone. Please feel free to post issues/pull requests to my github repo at github.com/goldfirere/no-role-annots.
Thanks, Richard
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://www.haskell.org/mailman/listinfo/ghc-devs
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries