Forgive me if this is already common knowledge or has already been thoroughly discussed, I am not very proficient in Haskell and I am new to discourse.haskell.
One go-to motivating example for the RoleAnnotations extension is a Set a
type with a private constructor where we’d like to restrict a
to be nominal in order to prevent type-safe coercions that could nonetheless break internal invariants of Set
. Coming from the Rust world, my first thought was that a
should be nominal by default if Set
has private constructors. I was told by a friend that “Haskell doesn’t have a Rust-like notion of privacy” and therefor this restriction was “icky”, which I accepted until I learned that this same logic applied to newtypes:
(from Data.Coerce’s documentation)
instance Coercible T b => Coercible NT b
This instance is only usable if the constructor MkNT is in scope.
Surely, a similar rule could theoretically be applied to type constructor coercions, something along the lines of:
… let D be a prototypical type constructor (data or newtype) with three type arguments, which have roles nominal, representational resp. phantom. Then there is an instance of the form
instance Coercible b b’ => Coercible (D a b c) (D a b’ c’)
- (!!) This instance is only usable if all constructors of
D
are in scope or ifD
has an explicit role annotation.
Of course, I can hardly blame the developers of GHC for not accounting for this, and implementing this restriction as the default would constitute a major breaking change.
My broad questions are:
- Is this a fair assessment of role inference? Can this suggestion even be feasibly implemented? What dark corners am I neglecting to consider?
- What, if anything, can be done about this?