diff options
author | Richard Eisenberg <eir@cis.upenn.edu> | 2013-09-11 00:52:56 -0400 |
---|---|---|
committer | Richard Eisenberg <eir@cis.upenn.edu> | 2013-09-17 21:37:23 -0400 |
commit | f4046b508a5a71ff2e28f438b30048867dbad428 (patch) | |
tree | ba1df224cdf834979e85f71367e705862b0382fc /docs | |
parent | 96421e0674ba2b69bb19445822886fb179e97608 (diff) | |
download | haskell-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.xml | 151 | ||||
-rw-r--r-- | docs/users_guide/separate_compilation.xml | 1 |
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> |