RE: We need to add role annotations for 7.8

When Brandon says “potential bugs/unsafety and all” he means GeneralisedNewtypeDeriving (GND). That already exists, and already allows you to do bad things (Trac #1496 among others).
See the thread on https://ghc.haskell.org/trac/ghc/ticket/8827, starting especially at comment 17 for more discussion and some insightful comments. As Edward (on the ticket) and Brandon (here) point out, going to “nominal by default” will be very safe but it’ll break many Haskell packages which use GND.
This is really a library/user question, not a GHC one. We can implement whatever default you want. But GHC 7.8 is about to be released and this debate has been going on for some weeks, at high intensity on ghc-devs, plus Richard specifically advertised it to the libraries list back in Novemberhttp://www.haskell.org/pipermail/libraries/2013-November/021707.html, and again a couple of weeks agohttp://www.haskell.org/pipermail/libraries/2014-March/022321.html.
I think the status quo is not unreasonable. One question you might want to debate is whether, as Brandon suggests, you want to move to “nominal by default” in due course.
Simon
From: Libraries [mailto:libraries-bounces@haskell.org] On Behalf Of Brandon Allbery
Sent: 24 March 2014 14:28
To: Mark Lentczner
Cc: GHC Development Mailing List; libraries@haskell.org Libraries
Subject: Re: We need to add role annotations for 7.8
On Mon, Mar 24, 2014 at 10:14 AM, Mark Lentczner

Thanks for the pointers, Simon. I appologize for coming to this quite so late... I didn't realize the global impact of this feature.
From a "meaning" perspective, I'm agnostic on the default. From a "engineering" perspective, I want a default that "does a good enough, reasonably safe thing" if programmers ignore the feature.
The later is subtle as there are different vantage points for different developers. In the Platform, we have many libraries that we are encouraging both end-programmers, and other library authors to make use of and depend on extensively. This means those libraries have to work for both programmers that are ignoring the feature, and those that use it. In that later case, there is the even more subtle distinction of those that use the feature for their own code, and those that use it in libraries they make available. The later case is issue: It seems a real mess if a library author who wanted to use the new feature, had to circumvent a HP library because it didn't annotate. Similar thought experiment: What would be the downside if containers didn't annotate? Would that just make the feature unusable because everything uses containers? To put it more directly: with the satus-quo default of representations, what is the down side if a library, a widely used library, doesn't bother to annotate? What would be the loss if containers didn't annotate? (I know it did, this is the thought experiment... because I've got 30+ libraries in HP that are in this boat.)

Again, sorry for the 11:59 meddling.... The syntax of role annotations is very heavy weight, and requires lots of CPP where there wasn't need before. Two thoughts: 1) Couldn't we do something like use "cue" type constraints? For example, assuming the default is representational, and that phantom can just be inferred, all we need is a way to indicate nominal: data (Nominal k) => Map k v = ... This could then probably be easily made to compile in other compilers with some null library implementation of Nominal 2) An alternative to the above. We generally frown on constraints in a data / newtype declaration, but perhaps this is exactly the case for them, whereby we can now do away with the type role syntax: We can infer nominal if there are *any* constraints on a type parameter, *representational* if there are none, and *phantom *if there are no mentions in the right hand side: data (Eq k) => Map k v = ... This seems even nicer and just works with all compilers... but perhaps I'm missing something. (Yes, I imagine there are type constraints that shouldn't force nominal, but perhaps not worth worrying about?) Mind you, this all just about syntax... the issue of what is the implication for libraries of the default being representational is still at hand.

This puts the constraint on the wrong thing.
I did argue for a pragma-like syntax for the annotations when they were
first proposed, as the need for library authors to hide these behind CPP
pragmas bothers me a great deal, but I think for better or worse that
syntax decision is largely behind us.
A type-class driven approach does have some promise, but that said, it
can't look like what you've written.
What you've written:
data Nominal k => Map k a
is saying something about the argument k, not about if you can turn
Map kinto Map
k', which is actually about Map, k is just along for the ride.
Really what we're talking about is that the next argument is
representational as applied. Also, the right 'open world' version would be
to talk about it as classes would be:
class Representational t where
rep :: Coercion a b -> Coercion (t a) (t b)
class Representational t => Phantom t where
phantom :: Coercion (t a) (t b)
With "Nominal" simply being the absence of either of those instances.
data Map k a
would need
instance Representational (Map k)
since the 'a' can be coerced as it has a representational role.
instance Representational (->)
instance Representational ((->) a)
But there are actually still open issues I don't know how to solve, even
with this approach.
-Edward
On Mon, Mar 24, 2014 at 11:36 AM, Mark Lentczner
Again, sorry for the 11:59 meddling....
The syntax of role annotations is very heavy weight, and requires lots of CPP where there wasn't need before. Two thoughts:
1) Couldn't we do something like use "cue" type constraints? For example, assuming the default is representational, and that phantom can just be inferred, all we need is a way to indicate nominal:
data (Nominal k) => Map k v = ...
This could then probably be easily made to compile in other compilers with some null library implementation of Nominal
2) An alternative to the above. We generally frown on constraints in a data / newtype declaration, but perhaps this is exactly the case for them, whereby we can now do away with the type role syntax: We can infer nominal if there are *any* constraints on a type parameter, *representational* if there are none, and *phantom *if there are no mentions in the right hand side:
data (Eq k) => Map k v = ...
This seems even nicer and just works with all compilers... but perhaps I'm missing something. (Yes, I imagine there are type constraints that shouldn't force nominal, but perhaps not worth worrying about?)
Mind you, this all just about syntax... the issue of what is the implication for libraries of the default being representational is still at hand.
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://www.haskell.org/mailman/listinfo/ghc-devs

Mark,
We're currently planning to retain the existing behavior of
GeneralizedNewtypeDeriving with regards to Safe Haskell. That is, Safe
Haskell and GND still won't mix in 7.8 due to these same security concerns.
I think a key observation with regards to GeneralizedNewtypeDeriving is
with representational roles as default the new roles machinery with the
representational default lets you write nothing you couldn't write before.
No new security vulnerabilities are introduced. They were there all along!
We're also disabling the Safe flag on Data.Coerce. In that light, `coerce`
then can be viewed as a more friendly but still evil version of
unsafeCoerce. It lets you write nothing you couldn't write before with
`unsafeCoerce`. I view it as merely an occasional situational improvement
over the existing unsafeCoerce in that it at least enforces
representational equality.
Making the default role annotation nominal comes at a very very real cost.
Namely, *all* of generalized newtype deriving anywhere breaks, and everyone
forever will have to put annotations in to fix it.
The 'backwards' representational default puts the burden on a small
minority of library authors.
I'm not a huge fan of the representational machinery, in that it hoists us
upon this dilemma, but given the choice between everyone paying in
perpetuity and a small minority of skilled library authors adding a handful
of annotations that for the most part have already been added, and which
expose them to no more risk than they'd had before if they forget, I'm
definitely in favor of the current solution.
-Edward
On Mon, Mar 24, 2014 at 11:26 AM, Mark Lentczner
Thanks for the pointers, Simon. I appologize for coming to this quite so late... I didn't realize the global impact of this feature.
From a "meaning" perspective, I'm agnostic on the default. From a "engineering" perspective, I want a default that "does a good enough, reasonably safe thing" if programmers ignore the feature.
The later is subtle as there are different vantage points for different developers. In the Platform, we have many libraries that we are encouraging both end-programmers, and other library authors to make use of and depend on extensively. This means those libraries have to work for both programmers that are ignoring the feature, and those that use it. In that later case, there is the even more subtle distinction of those that use the feature for their own code, and those that use it in libraries they make available.
The later case is issue: It seems a real mess if a library author who wanted to use the new feature, had to circumvent a HP library because it didn't annotate. Similar thought experiment: What would be the downside if containers didn't annotate? Would that just make the feature unusable because everything uses containers?
To put it more directly: with the satus-quo default of representations, what is the down side if a library, a widely used library, doesn't bother to annotate? What would be the loss if containers didn't annotate? (I know it did, this is the thought experiment... because I've got 30+ libraries in HP that are in this boat.)
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

In that light, `coerce` then can be viewed as a more friendly but still evil version of unsafeCoerce
Coerce embodies one rather compelling improvement: it is type-sound. unsafeCoerce can cause arbitrary seg-faults etc. ‘coerce’ cannot. Call me an old-fashioned “well-typed programs don’t go wrong” man, but I think that’s a big plus. Much more than “an occasional situation improvement”.
Granted, “type-sound” doesn’t guarantee “correct”, but then it never did.
The role machinery doesn’t exactly hoist us on a dilemma – it merely exposes the dilemma that was there all the time.
Simon
From: Edward Kmett [mailto:ekmett@gmail.com]
Sent: 24 March 2014 19:11
To: Mark Lentczner
Cc: Simon Peyton Jones; libraries@haskell.org Libraries; ghc-devs@haskell.org
Subject: Re: We need to add role annotations for 7.8
Mark,
We're currently planning to retain the existing behavior of GeneralizedNewtypeDeriving with regards to Safe Haskell. That is, Safe Haskell and GND still won't mix in 7.8 due to these same security concerns.
I think a key observation with regards to GeneralizedNewtypeDeriving is with representational roles as default the new roles machinery with the representational default lets you write nothing you couldn't write before. No new security vulnerabilities are introduced. They were there all along!
We're also disabling the Safe flag on Data.Coerce. In that light, `coerce` then can be viewed as a more friendly but still evil version of unsafeCoerce. It lets you write nothing you couldn't write before with `unsafeCoerce`. I view it as merely an occasional situational improvement over the existing unsafeCoerce in that it at least enforces representational equality.
Making the default role annotation nominal comes at a very very real cost. Namely, all of generalized newtype deriving anywhere breaks, and everyone forever will have to put annotations in to fix it.
The 'backwards' representational default puts the burden on a small minority of library authors.
I'm not a huge fan of the representational machinery, in that it hoists us upon this dilemma, but given the choice between everyone paying in perpetuity and a small minority of skilled library authors adding a handful of annotations that for the most part have already been added, and which expose them to no more risk than they'd had before if they forget, I'm definitely in favor of the current solution.
-Edward
On Mon, Mar 24, 2014 at 11:26 AM, Mark Lentczner

Fair enough. I did try to convey that in the following sentence about how it at least enforces representational equality, but I can see how my statement might be taken as understating the importance of that property. Sent from my iPhone
On Mar 24, 2014, at 6:57 PM, Simon Peyton Jones
wrote: In that light, `coerce` then can be viewed as a more friendly but still evil version of unsafeCoerce
Coerce embodies one rather compelling improvement: it is type-sound. unsafeCoerce can cause arbitrary seg-faults etc. ‘coerce’ cannot. Call me an old-fashioned “well-typed programs don’t go wrong” man, but I think that’s a big plus. Much more than “an occasional situation improvement”.
Granted, “type-sound” doesn’t guarantee “correct”, but then it never did.
The role machinery doesn’t exactly hoist us on a dilemma – it merely exposes the dilemma that was there all the time.
Simon
From: Edward Kmett [mailto:ekmett@gmail.com] Sent: 24 March 2014 19:11 To: Mark Lentczner Cc: Simon Peyton Jones; libraries@haskell.org Libraries; ghc-devs@haskell.org Subject: Re: We need to add role annotations for 7.8
Mark,
We're currently planning to retain the existing behavior of GeneralizedNewtypeDeriving with regards to Safe Haskell. That is, Safe Haskell and GND still won't mix in 7.8 due to these same security concerns.
I think a key observation with regards to GeneralizedNewtypeDeriving is with representational roles as default the new roles machinery with the representational default lets you write nothing you couldn't write before. No new security vulnerabilities are introduced. They were there all along!
We're also disabling the Safe flag on Data.Coerce. In that light, `coerce` then can be viewed as a more friendly but still evil version of unsafeCoerce. It lets you write nothing you couldn't write before with `unsafeCoerce`. I view it as merely an occasional situational improvement over the existing unsafeCoerce in that it at least enforces representational equality.
Making the default role annotation nominal comes at a very very real cost. Namely, all of generalized newtype deriving anywhere breaks, and everyone forever will have to put annotations in to fix it.
The 'backwards' representational default puts the burden on a small minority of library authors.
I'm not a huge fan of the representational machinery, in that it hoists us upon this dilemma, but given the choice between everyone paying in perpetuity and a small minority of skilled library authors adding a handful of annotations that for the most part have already been added, and which expose them to no more risk than they'd had before if they forget, I'm definitely in favor of the current solution.
-Edward
On Mon, Mar 24, 2014 at 11:26 AM, Mark Lentczner
wrote: Thanks for the pointers, Simon. I appologize for coming to this quite so late... I didn't realize the global impact of this feature.
From a "meaning" perspective, I'm agnostic on the default.
From a "engineering" perspective, I want a default that "does a good enough, reasonably safe thing" if programmers ignore the feature.
The later is subtle as there are different vantage points for different developers. In the Platform, we have many libraries that we are encouraging both end-programmers, and other library authors to make use of and depend on extensively. This means those libraries have to work for both programmers that are ignoring the feature, and those that use it. In that later case, there is the even more subtle distinction of those that use the feature for their own code, and those that use it in libraries they make available.
The later case is issue: It seems a real mess if a library author who wanted to use the new feature, had to circumvent a HP library because it didn't annotate. Similar thought experiment: What would be the downside if containers didn't annotate? Would that just make the feature unusable because everything uses containers?
To put it more directly: with the satus-quo default of representations, what is the down side if a library, a widely used library, doesn't bother to annotate? What would be the loss if containers didn't annotate? (I know it did, this is the thought experiment... because I've got 30+ libraries in HP that are in this boat.)
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

I have a few responses to various themes in this thread, but nothing terribly unexpected: - The introduction of roles is the end of the story that began with the discovery of bug #1496. The alternative would be to do away with GND. `coerce` is just a convenient application of roles, not the reason they were introduced. - The concrete syntax for role ascriptions was debated in public, in bug #8185. There is further discussion of the design choice in the appendix of the extended version of our recent draft paper on the subject: www.cis.upenn.edu/~eir/papers/2014/coercible/coercible-ext.pdf I'm afraid it's too late now to make changes. I don't love what we ended up with, but I believe it's the best syntax that was proposed. - I agree with Simon that `coerce` is quite a bit safer than `unsafeCoerce`. Under the assumption that all libraries are written correctly (that is, with proper role annotations), `coerce` is in fact fully safe. - I surely recognize why and how this causes a Major Pain for Mark, and for other library maintainers. I wish there were an easier solution. However, I will perhaps repeat others in saying that a library that doesn't add role annotations is no more wrong in 7.8 than it was since GND was introduced. The only difference with 7.8 is that, now, there is a way to be right. Richard On Mar 24, 2014, at 9:32 PM, Edward A Kmett wrote:
Fair enough. I did try to convey that in the following sentence about how it at least enforces representational equality, but I can see how my statement might be taken as understating the importance of that property.
Sent from my iPhone
On Mar 24, 2014, at 6:57 PM, Simon Peyton Jones
wrote: In that light, `coerce` then can be viewed as a more friendly but still evil version of unsafeCoerce
Coerce embodies one rather compelling improvement: it is type-sound. unsafeCoerce can cause arbitrary seg-faults etc. ‘coerce’ cannot. Call me an old-fashioned “well-typed programs don’t go wrong” man, but I think that’s a big plus. Much more than “an occasional situation improvement”.
Granted, “type-sound” doesn’t guarantee “correct”, but then it never did.
The role machinery doesn’t exactly hoist us on a dilemma – it merely exposes the dilemma that was there all the time.
Simon
From: Edward Kmett [mailto:ekmett@gmail.com] Sent: 24 March 2014 19:11 To: Mark Lentczner Cc: Simon Peyton Jones; libraries@haskell.org Libraries; ghc-devs@haskell.org Subject: Re: We need to add role annotations for 7.8
Mark,
We're currently planning to retain the existing behavior of GeneralizedNewtypeDeriving with regards to Safe Haskell. That is, Safe Haskell and GND still won't mix in 7.8 due to these same security concerns.
I think a key observation with regards to GeneralizedNewtypeDeriving is with representational roles as default the new roles machinery with the representational default lets you write nothing you couldn't write before. No new security vulnerabilities are introduced. They were there all along!
We're also disabling the Safe flag on Data.Coerce. In that light, `coerce` then can be viewed as a more friendly but still evil version of unsafeCoerce. It lets you write nothing you couldn't write before with `unsafeCoerce`. I view it as merely an occasional situational improvement over the existing unsafeCoerce in that it at least enforces representational equality.
Making the default role annotation nominal comes at a very very real cost. Namely, all of generalized newtype deriving anywhere breaks, and everyone forever will have to put annotations in to fix it.
The 'backwards' representational default puts the burden on a small minority of library authors.
I'm not a huge fan of the representational machinery, in that it hoists us upon this dilemma, but given the choice between everyone paying in perpetuity and a small minority of skilled library authors adding a handful of annotations that for the most part have already been added, and which expose them to no more risk than they'd had before if they forget, I'm definitely in favor of the current solution.
-Edward
On Mon, Mar 24, 2014 at 11:26 AM, Mark Lentczner
wrote: Thanks for the pointers, Simon. I appologize for coming to this quite so late... I didn't realize the global impact of this feature.
From a "meaning" perspective, I'm agnostic on the default.
From a "engineering" perspective, I want a default that "does a good enough, reasonably safe thing" if programmers ignore the feature.
The later is subtle as there are different vantage points for different developers. In the Platform, we have many libraries that we are encouraging both end-programmers, and other library authors to make use of and depend on extensively. This means those libraries have to work for both programmers that are ignoring the feature, and those that use it. In that later case, there is the even more subtle distinction of those that use the feature for their own code, and those that use it in libraries they make available.
The later case is issue: It seems a real mess if a library author who wanted to use the new feature, had to circumvent a HP library because it didn't annotate. Similar thought experiment: What would be the downside if containers didn't annotate? Would that just make the feature unusable because everything uses containers?
To put it more directly: with the satus-quo default of representations, what is the down side if a library, a widely used library, doesn't bother to annotate? What would be the loss if containers didn't annotate? (I know it did, this is the thought experiment... because I've got 30+ libraries in HP that are in this boat.)
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
participants (5)
-
Edward A Kmett
-
Edward Kmett
-
Mark Lentczner
-
Richard Eisenberg
-
Simon Peyton Jones