summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorRichard Eisenberg <eir@cis.upenn.edu>2013-09-11 00:52:56 -0400
committerRichard Eisenberg <eir@cis.upenn.edu>2013-09-17 21:37:23 -0400
commitf4046b508a5a71ff2e28f438b30048867dbad428 (patch)
treeba1df224cdf834979e85f71367e705862b0382fc /docs
parent96421e0674ba2b69bb19445822886fb179e97608 (diff)
downloadhaskell-f4046b508a5a71ff2e28f438b30048867dbad428.tar.gz
Change role annotation syntax.
This fixes bugs #8185, #8234, and #8246. The new syntax is explained in the comments to #8185, appears in the "Roles" subsection of the manual, and on the [wiki:Roles] wiki page. This change also removes the ability for a role annotation on type synonyms, as noted in #8234.
Diffstat (limited to 'docs')
-rw-r--r--docs/users_guide/glasgow_exts.xml151
-rw-r--r--docs/users_guide/separate_compilation.xml1
2 files changed, 87 insertions, 65 deletions
diff --git a/docs/users_guide/glasgow_exts.xml b/docs/users_guide/glasgow_exts.xml
index d27336b511..b49ad9fd70 100644
--- a/docs/users_guide/glasgow_exts.xml
+++ b/docs/users_guide/glasgow_exts.xml
@@ -3701,7 +3701,7 @@ where
via this new mechanism.
</para></listitem>
<listitem><para>
- The role of the last parameter of each of the <literal>ci</literal> is <emphasis>not</emphasis> <literal>N</literal>. (See <xref linkend="roles"/>.)</para></listitem>
+ The role of the last parameter of each of the <literal>ci</literal> is <emphasis>not</emphasis> <literal>nominal</literal>. (See <xref linkend="roles"/>.)</para></listitem>
</itemizedlist>
Then, for each <literal>ci</literal>, the derived instance
declaration is:
@@ -10845,35 +10845,36 @@ about <literal>Age</literal> and <literal>Int</literal> in order to show that
T Int Bool c</literal>).</para>
<para>GHC supports three different roles for type parameters: nominal,
-representational, and phantom. If a type parameter has a nominal (N) role,
-then the two types that differ must not actually differ at all: they must be
+representational, and phantom. If a type parameter has a nominal role, then
+the two types that differ must not actually differ at all: they must be
identical (after type family reduction). If a type parameter has a
-representational (R) role, then the two types must have the same
-representation. (If <literal>T</literal>'s first parameter's role is R, then
+representational role, then the two types must have the same representation.
+(If <literal>T</literal>'s first parameter's role is representational, then
<literal>T Age Bool c</literal> and <literal>T Int Bool c</literal> would have
-the same representation, because <literal>Age</literal> and <literal>Int</literal>
-have the same representation.) If a type parameter has a phantom (P) role,
-then we need no further information.</para>
+the same representation, because <literal>Age</literal> and
+<literal>Int</literal> have the same representation.) If a type parameter has
+a phantom role, then we need no further information.</para>
<para>Here are some examples:</para>
<programlisting>
- data Simple a = MkSimple a -- a has role R
+ data Simple a = MkSimple a -- a has role representational
type family F
type instance F Int = Bool
type instance F Age = Char
- data Complex a = MkComplex (F a) -- a has role N
+ data Complex a = MkComplex (F a) -- a has role nominal
- data Phant a = MkPhant Bool -- a has role P
+ data Phant a = MkPhant Bool -- a has role phantom
</programlisting>
-<para>The type <literal>Simple</literal> has its parameter at role R, which is
-generally the most common case. <literal>Simple Age</literal> would have the same
-representation as <literal>Simple Int</literal>. The type <literal>Complex</literal>,
-on the other hand, has its parameter at role N, because <literal>Simple Age</literal>
-and <literal>Simple Int</literal> are <emphasis>not</emphasis> the same. Lastly,
+<para>The type <literal>Simple</literal> has its parameter at role
+representational, which is generally the most common case. <literal>Simple
+Age</literal> would have the same representation as <literal>Simple
+Int</literal>. The type <literal>Complex</literal>, on the other hand, has its
+parameter at role nominal, because <literal>Simple Age</literal> and
+<literal>Simple Int</literal> are <emphasis>not</emphasis> the same. Lastly,
<literal>Phant Age</literal> and <literal>Phant Bool</literal> have the same
representation, even though <literal>Age</literal> and <literal>Bool</literal>
are unrelated.</para>
@@ -10886,17 +10887,19 @@ are unrelated.</para>
<para>
What role should a given type parameter should have? GHC performs role
inference to determine the correct role for every parameter. It starts with a
-few base facts: <literal>(->)</literal> has two R parameters;
-<literal>(~)</literal> has two N parameters; all type families' parameters are
-N; and all GADT-like parameters are N. Then, these facts are propagated to all
-places where these types are used. By defaulting parameters to role P, any
-parameters unused in the right-hand side (or used only in other types in P
-positions) will be P. Whenever a parameter is used in an R position (that is,
-used as a type argument to a constructor whose corresponding variable is at
-role R), we raise its role from P to R. Similarly, when a parameter is used in
-an N position, its role is upgraded to N. We never downgrade a role from N to
-P or R, or from R to P. In this way, we infer the most-general role for each
-parameter.
+few base facts: <literal>(->)</literal> has two representational parameters;
+<literal>(~)</literal> has two nominal parameters; all type families'
+parameters are nominal; and all GADT-like parameters are nominal. Then, these
+facts are propagated to all places where these types are used. By defaulting
+parameters to role phnatom, any parameters unused in the right-hand side (or
+used only in other types in phantom positions) will be phantom. Whenever a
+parameter is used in a representational position (that is, used as a type
+argument to a constructor whose corresponding variable is at role
+representational), we raise its role from phantom to representational.
+Similarly, when a parameter is used in a nominal position, its role is
+upgraded to nominal. We never downgrade a role from nominal to phantom or
+representational, or from representational to phantom. In this way, we infer
+the most-general role for each parameter.
</para>
<para>There is one particularly tricky case that should be explained:</para>
@@ -10905,20 +10908,22 @@ parameter.
data Tricky a b = MkTricky (a b)
</programlisting>
-<para>What should <literal>Tricky</literal>'s roles be? At first blush, it would
-seem that both <literal>a</literal> and <literal>b</literal> should be at role R,
-since both are used in the right-hand side and neither is involved in a type family.
-However, this would be wrong, as the following example shows:</para>
+<para>What should <literal>Tricky</literal>'s roles be? At first blush, it
+would seem that both <literal>a</literal> and <literal>b</literal> should be
+at role representational, since both are used in the right-hand side and
+neither is involved in a type family. However, this would be wrong, as the
+following example shows:</para>
<programlisting>
data Nom a = MkNom (F a) -- type family F from example above
</programlisting>
<para>Is <literal>Tricky Nom Age</literal> representationally equal to
-<literal>Tricky Nom Int</literal>? No! The former stores a <literal>Char</literal>
-and the latter stores a <literal>Bool</literal>. The solution to this is
-to require all parameters to type variables to have role N. Thus, GHC would
-infer role R for <literal>a</literal> but role N for <literal>b</literal>.</para>
+<literal>Tricky Nom Int</literal>? No! The former stores a
+<literal>Char</literal> and the latter stores a <literal>Bool</literal>. The
+solution to this is to require all parameters to type variables to have role
+nominal. Thus, GHC would infer role representational for <literal>a</literal>
+but role nominal for <literal>b</literal>.</para>
</sect2>
@@ -10937,35 +10942,38 @@ example, the base library contains the following definition:
</programlisting>
<para>
-The idea is that <literal>a</literal> should really be an R parameter, but
-role inference assigns it to P. This makes some level of sense: a pointer to
-an <literal>Int</literal> really is representationally the same as a pointer
-to a <literal>Bool</literal>. But, that's not at all how we want to use
-<literal>Ptr</literal>s! So, we want to be able to say</para>
+The idea is that <literal>a</literal> should really be a representational
+parameter, but role inference assigns it to phantom. This makes some level of
+sense: a pointer to an <literal>Int</literal> really is representationally the
+same as a pointer to a <literal>Bool</literal>. But, that's not at all how we
+want to use <literal>Ptr</literal>s! So, we want to be able to say</para>
<programlisting>
- data Ptr a@R = Ptr Addr#
+ type role Ptr representational
+ data Ptr a = Ptr Addr#
</programlisting>
<para>
-The <literal>@R</literal> (enabled with <option>-XRoleAnnotations</option>) annotation forces the
-parameter a to be at role R, not role P. GHC then checks
-the user-supplied roles to make sure they don't break any promises. It would
-be bad, for example, if the user could make <literal>BadIdea</literal>'s role be R.
+The <literal>type role</literal> (enabled with
+<option>-XRoleAnnotations</option>) declaration forces the parameter
+<literal>a</literal> to be at role representational, not role phantom. GHC
+then checks the user-supplied roles to make sure they don't break any
+promises. It would be bad, for example, if the user could make
+<literal>BadIdea</literal>'s role be representational.
</para>
<para>As another example, we can consider a type <literal>Set a</literal> that
represents a set of data, ordered according to <literal>a</literal>'s
<literal>Ord</literal> instance. While it would generally be type-safe to
-consider <literal>a</literal> to be at role R, it is possible that a
-<literal>newtype</literal> and its base type have
+consider <literal>a</literal> to be at role representational, it is possible
+that a <literal>newtype</literal> and its base type have
<emphasis>different</emphasis> orderings encoded in their respective
<literal>Ord</literal> instances. This would lead to misbehavior at runtime.
So, the author of the <literal>Set</literal> datatype would like its parameter
-to be at role N. This would be done with a declaration</para>
+to be at role nominal. This would be done with a declaration</para>
<programlisting>
- data Set a@N = ...
+ type role Set nominal
</programlisting>
<para>The other place where role annotations may be necessary are in
@@ -10973,27 +10981,40 @@ to be at role N. This would be done with a declaration</para>
the right-hand sides of definitions can be omitted. As usual, the
types/classes declared in an <literal>hs-boot</literal> file must match up
with the definitions in the <literal>hs</literal> file, including down to the
-roles. The default role is R in <literal>hs-boot</literal> files,
+roles. The default role is representational in <literal>hs-boot</literal> files,
corresponding to the common use case.</para>
<para>
-Role annotations are allowed on type variables in data, newtype, class,
-and type declarations. They are not allowed on type/data family
-declarations or in explicit foralls in function type signatures.
-The syntax for a role annotation is an <literal>@</literal> sign followed
-by one of <literal>N</literal>, <literal>R</literal>, or <literal>P</literal>,
-directly following a type variable. If the type variable has an explicit
-kind annotation, the role annotation goes after the kind annotation, outside
-the parentheses. Here are some examples:</para>
+Role annotations are allowed on data, newtype, and class declarations. A role
+annotation declaration starts with <literal>type role</literal> and is
+followed by one role listing for each parameter of the type. (This parameter
+count includes parameters implicitly specified by a kind signature in a
+GADT-style data or newtype declaration.) Each role listing is a role
+(<literal>nominal</literal>, <literal>representational</literal>, or
+<literal>phantom</literal>) or a <literal>_</literal>. Using a
+<literal>_</literal> says that GHC should infer that role. The role annotation
+may go anywhere in the same module as the datatype or class definition
+(much like a value-level type signature).
+Here are some examples:</para>
<programlisting>
- data T1 a b@P = MkT1 a -- b is not used; annotation is fine but unnecessary
- data T2 a b@P = MkT2 b -- ERROR: b is used and cannot be P
- data T3 a b@N = MkT3 a -- OK: N is higher than necessary, but safe
- data T4 (a :: * -> *)@N = MkT4 (a Int) -- OK, but N is higher than necessary
- class C a@R b where ... -- OK
- type X a@N = ... -- OK
- type family F a@R -- ERROR: annotations not allowed on family declarations
+ type role T1 _ phantom
+ data T1 a b = MkT1 a -- b is not used; annotation is fine but unnecessary
+
+ type role T2 _ phantom
+ data T2 a b = MkT2 b -- ERROR: b is used and cannot be phantom
+
+ type role T3 _ nominal
+ data T3 a b = MkT3 a -- OK: nominal is higher than necessary, but safe
+
+ type role T4 nominal
+ data T4 a = MkT4 (a Int) -- OK, but N is higher than necessary
+
+ type role C representational _
+ class C a b where ... -- OK
+
+ type role X nominal
+ type X a@N = ... -- ERROR: role annotations not allowed for type synonyms
</programlisting>
</sect2>
diff --git a/docs/users_guide/separate_compilation.xml b/docs/users_guide/separate_compilation.xml
index 2f8b9d6f33..c571c39fb7 100644
--- a/docs/users_guide/separate_compilation.xml
+++ b/docs/users_guide/separate_compilation.xml
@@ -878,6 +878,7 @@ methods entirely; but you must either omit them all or put them all in.
</para></listitem>
<listitem><para> You can include instance declarations just as in Haskell; but omit the "where" part.
</para></listitem>
+<listitem><para>The default role for class and datatype parameters is now representational. To get another role, use a role annotation. (See <xref linkend="roles"/>.)
</itemizedlist>
</para>
</sect2>