Closed matt-noonan closed 5 years ago
As for introducing existentially-quantified names, there are two approaches:
type a ~~ name = Named name a
newtype Named name a = Named a
type role Named nominal representational
or
type role Named nominal nominal
At first glance, it isn't quite clear to me which one of these is correct. Probably the nominal
/ nominal
one.
I'd say nominal
/ nominal
, because, for instance if a
is, say, supposed to be a sorted list w.r.t its elements' own Ord
instance, representational
would stop the show.
I think this is a very sneaky issue, since in certain programming styles one could bump into this issue without even trying. For example if one is using newtypes like Product
for their Monoid
instances inside Map
s, they would probably like to use coerce
for simplicity and efficiency. If the same container happens to contain a gdp somewhere in it, all bets are off.
Fixed in 6e09928.
@enobayram discovered in the
justified-containers
library that using phantom roles for phantom type parameters in GDP-style interfaces leads to a back-door:https://github.com/matt-noonan/justified-containers/issues/8
The solution is to ensure that the phantom type variables used for gdp things always have role
nominal
. For example, the inferred role ofp
indata Proof p = QED
isphantom
, so a user(!) could writecoerce :: Proof TRUE -> Proof FALSE
. That's bad.Instead, we should write:
The same goes for library-defined predicates and names, like
data IsCons xs
ornewtype Head xs = Head Defn
. The role ofxs
in each case needs to be explicitly annotated asnominal
, or else the user could coerce theIsCons xs
fact from a non-emptyxs
to nil. Gross.