diff options
author | Iavor S. Diatchki <iavor.diatchki@gmail.com> | 2014-03-17 23:19:16 -0700 |
---|---|---|
committer | Iavor S. Diatchki <iavor.diatchki@gmail.com> | 2014-03-17 23:20:02 -0700 |
commit | 3099e40d2737172c746a6456ddcd34b54e120aa0 (patch) | |
tree | ee94fab09b3c989eff407483c81a7f1a3fd90e88 /docs | |
parent | a5ab610f8d7ea5ddbcfe2c27fa18fceb02923ee2 (diff) | |
download | haskell-3099e40d2737172c746a6456ddcd34b54e120aa0.tar.gz |
Add some documentation about type-level literals.
I moved the "promoted literals" sub-section into a separate section,
as many folks were not finding the docs. I also added some additional
paragraphs describing the current state of the feature.
Diffstat (limited to 'docs')
-rw-r--r-- | docs/users_guide/glasgow_exts.xml | 178 |
1 files changed, 135 insertions, 43 deletions
diff --git a/docs/users_guide/glasgow_exts.xml b/docs/users_guide/glasgow_exts.xml index 9dbd545069..33124122ef 100644 --- a/docs/users_guide/glasgow_exts.xml +++ b/docs/users_guide/glasgow_exts.xml @@ -6590,14 +6590,78 @@ Note that this requires <option>-XTypeOperators</option>. </para> </sect2> -<sect2 id="promoted-literals"> -<title>Promoted Literals</title> +<sect2 id="promotion-existentials"> +<title>Promoting existential data constructors</title> +<para> +Note that we do promote existential data constructors that are otherwise suitable. +For example, consider the following: +<programlisting> +data Ex :: * where + MkEx :: forall a. a -> Ex +</programlisting> +Both the type <literal>Ex</literal> and the data constructor <literal>MkEx</literal> +get promoted, with the polymorphic kind <literal>'MkEx :: forall k. k -> Ex</literal>. +Somewhat surprisingly, you can write a type family to extract the member +of a type-level existential: +<programlisting> +type family UnEx (ex :: Ex) :: k +type instance UnEx (MkEx x) = x +</programlisting> +At first blush, <literal>UnEx</literal> seems poorly-kinded. The return kind +<literal>k</literal> is not mentioned in the arguments, and thus it would seem +that an instance would have to return a member of <literal>k</literal> +<emphasis>for any</emphasis> <literal>k</literal>. However, this is not the +case. The type family <literal>UnEx</literal> is a kind-indexed type family. +The return kind <literal>k</literal> is an implicit parameter to <literal>UnEx</literal>. +The elaborated definitions are as follows: +<programlisting> +type family UnEx (k :: BOX) (ex :: Ex) :: k +type instance UnEx k (MkEx k x) = x +</programlisting> +Thus, the instance triggers only when the implicit parameter to <literal>UnEx</literal> +matches the implicit parameter to <literal>MkEx</literal>. Because <literal>k</literal> +is actually a parameter to <literal>UnEx</literal>, the kind is not escaping the +existential, and the above code is valid. +</para> + +<para> +See also <ulink url="http://ghc.haskell.org/trac/ghc/ticket/7347">Trac #7347</ulink>. +</para> +</sect2> + +<sect2> +<title>Promoting type operators</title> +<para> +Type operators are <emphasis>not</emphasis> promoted to the kind level. Why not? Because +<literal>*</literal> is a kind, parsed the way identifiers are. Thus, if a programmer +tried to write <literal>Either * Bool</literal>, would it be <literal>Either</literal> +applied to <literal>*</literal> and <literal>Bool</literal>? Or would it be +<literal>*</literal> applied to <literal>Either</literal> and <literal>Bool</literal>. +To avoid this quagmire, we simply forbid promoting type operators to the kind level. +</para> +</sect2> + + +</sect1> + +<sect1 id="type-level-literals"> +<title>Type-Level Literals</title> +<para> +GHC supports numeric and string literals at the type level, giving convenient +access to a large number of predefined type-level constants. +Numeric literals are of kind <literal>Nat</literal>, while string literals +are of kind <literal>Symbol</literal>. +This feature is enabled by the <literal>XDataKinds</literal> +language extension. +</para> + <para> -Numeric and string literals are promoted to the type level, giving convenient -access to a large number of predefined type-level constants. Numeric literals -are of kind <literal>Nat</literal>, while string literals are of kind -<literal>Symbol</literal>. These kinds are defined in the module -<literal>GHC.TypeLits</literal>. +The kinds of the literals and all other low-level operations for this feature +are defined in module <literal>GHC.TypeLits</literal>. Note that the module +defines some type-level operators that clash with their value-level +counterparts (e.g. <literal>(+)</literal>). Import and export declarations +referring to these operators require an explicit namespace +annotation (see <xref linkend="explicit-namespaces"/>). </para> <para> @@ -6632,56 +6696,84 @@ instance Has Point "y" Int where from (Point _ y) _ = y example = from (Point 1 2) (Get :: Label "x") </programlisting> </para> -</sect2> -<sect2 id="promotion-existentials"> -<title>Promoting existential data constructors</title> +<sect2 id="typelit-runtime"> +<title>Runtime Values for Type-Level Literals</title> <para> -Note that we do promote existential data constructors that are otherwise suitable. -For example, consider the following: +Sometimes it is useful to access the value-level literal assocaited with +a type-level literal. This is done with the functions +<literal>natVal</literal> and <literal>symbolVal</literal>. For example: <programlisting> -data Ex :: * where - MkEx :: forall a. a -> Ex +GHC.TypeLits> natVal (Proxy :: Proxy 2) +2 </programlisting> -Both the type <literal>Ex</literal> and the data constructor <literal>MkEx</literal> -get promoted, with the polymorphic kind <literal>'MkEx :: forall k. k -> Ex</literal>. -Somewhat surprisingly, you can write a type family to extract the member -of a type-level existential: +These functions are overloaded because they need to return a different +result, depending on the type at which they are instantiated. <programlisting> -type family UnEx (ex :: Ex) :: k -type instance UnEx (MkEx x) = x +natVal :: KnownNat n => proxy n -> Integer + +-- instance KnownNat 0 +-- instance KnownNat 1 +-- instance KnownNat 2 +-- ... </programlisting> -At first blush, <literal>UnEx</literal> seems poorly-kinded. The return kind -<literal>k</literal> is not mentioned in the arguments, and thus it would seem -that an instance would have to return a member of <literal>k</literal> -<emphasis>for any</emphasis> <literal>k</literal>. However, this is not the -case. The type family <literal>UnEx</literal> is a kind-indexed type family. -The return kind <literal>k</literal> is an implicit parameter to <literal>UnEx</literal>. -The elaborated definitions are as follows: +GHC discharges the constraint as soon as it knows what concrete +type-level literal is being used in the program. Note that this works +only for <emphasis>literals</emphasis> and not arbitrary type expressions. +For example, a constraint of the form <literal>KnownNat (a + b)</literal> +will <emphasis>not</emphasis> be simplified to +<literal>(KnownNat a, KnownNat b)</literal>; instead, GHC will keep the +constraint as is, until it can simplify <literal>a + b</literal> to +a constant value. +</para> +</sect2> + +<para> +It is also possible to convert a run-time integer or string value to +the corresponding type-level literal. Of course, the resulting type +literal will be unknown at compile-time, so it is hidden in an existential +type. The conversion may be performed using <literal>someNatVal</literal> +for integers and <literal>someSymbolVal</literal> for strings: <programlisting> -type family UnEx (k :: BOX) (ex :: Ex) :: k -type instance UnEx k (MkEx k x) = x +someNatVal :: Integer -> Maybe SomeNat +SomeNat :: KnownNat n => Proxy n -> SomeNat </programlisting> -Thus, the instance triggers only when the implicit parameter to <literal>UnEx</literal> -matches the implicit parameter to <literal>MkEx</literal>. Because <literal>k</literal> -is actually a parameter to <literal>UnEx</literal>, the kind is not escaping the -existential, and the above code is valid. +The operations on strings are similar. </para> +<sect2 id="typelit-tyfuns"> +<title>Computing With Type-Level Naturals</title> <para> -See also <ulink url="http://ghc.haskell.org/trac/ghc/ticket/7347">Trac #7347</ulink>. +GHC 7.8 can evaluate arithmetic expressions involving type-level natural +numbers. Such expressions may be constructed using the type-families +<literal>(+), (*), (^)</literal> for addition, multiplication, +and exponentiation. Numbers may be compared using <literal>(<=?)</literal>, +which returns a promoted boolean value, or <literal>(<=)</literal>, which +compares numbers as a constraint. For example: +<programlisting> +GHC.TypeLits> natVal (Proxy :: Proxy (2 + 3)) +5 +</programlisting> +</para> +<para> +At present, GHC is quite limited in its reasoning about arithmetic: +it will only evalute the arithmetic type functions and compare the results--- +in the same way that it does for any other type function. In particular, +it does not know more general facts about arithmetic, such as the commutativity +and associativity of <literal>(+)</literal>, for example. </para> -</sect2> -<sect2> -<title>Promoting type operators</title> <para> -Type operators are <emphasis>not</emphasis> promoted to the kind level. Why not? Because -<literal>*</literal> is a kind, parsed the way identifiers are. Thus, if a programmer -tried to write <literal>Either * Bool</literal>, would it be <literal>Either</literal> -applied to <literal>*</literal> and <literal>Bool</literal>? Or would it be -<literal>*</literal> applied to <literal>Either</literal> and <literal>Bool</literal>. -To avoid this quagmire, we simply forbid promoting type operators to the kind level. +However, it is possible to perform a bit of "backwards" evaluation. +For example, here is how we could get GHC to compute arbitrary logarithms +at the type level: +<programlisting> +lg :: Proxy base -> Proxy (base ^ pow) -> Proxy pow +lg _ _ = Proxy + +GHC.TypeLits> natVal (lg (Proxy :: Proxy 2) (Proxy :: Proxy 8)) +3 +</programlisting> </para> </sect2> |