I’ve increasingly found myself wanting a compiler supported annotations on declarations. There are a number of ad-hoc mechanism that have specialized syntax which I think could be subsumed by annotations.
Newtype
Let’s face it, newtype
is a hideous keyword, and it only exists to enable a (guaranteed) compiler optimization. PureScript’s strictness means there’s no runtime distinction between a newtype
and data
aside from boxing (unlike Haskell, which has subtleties wrt bottom). I would like to see this as an annotation over data
.
Deriving
We have deriving
and deriving newtype
. If we add more strategies in the future we need to extend the syntax of the language for each one. I would like deriving strategies to be annotations on instance declarations.
Deprecations
We have the Deprecated
typeclass, but this only works on value declarations. What if we wanted to deprecate data types (perhaps SProxy
in favor of a polykinded Proxy
) or type synonyms (which may no longer be relevant)? I would like to see warnings and deprecations as annotations.
Roles
There’s a PR for safe coercions, which requires adding a new declaration type for roles, and their various keywords. I would like to see roles as annotations on data declarations.
Inlining Directives
We often need to help the compiler along with optimizations if we want them to always fire. Inlining directives can be annotations on declarations.
And I’m sure many more (backend specific configurations?). I think it would be nice to be able to support these in an extensible way that doesn’t require us to extend the syntax of the language with new keywords.
One idea is reusing the type language. We have compiler solved “magic” typeclasses as a way to extend solving, and we could do something similar with annotations. Annotations could be a specific kind with types in Prim.Annotation
. We could support array literals in types:
import Prim.Annotation (Deriving, Newtype, Roles, Nominal, Deprecated)
[Newtype, Roles [Nominal], Deprecated]
data Foo a = Foo a
[Deriving Newtype]
instance eqFoo :: Eq (Foo a)
Or perhaps via rows
import Prim.Annotation (Newtype, Nominal, Deprecated)
(rep :: Newtype, roles :: [Nominal], warn :: Deprecated)
data Foo a = Foo a
(deriving :: Newtype)
instance eqFoo :: Eq (Foo a)
I’m sure there are other alternatives to this. What do y’all think?