diff options
author | Joachim Breitner <mail@joachim-breitner.de> | 2013-09-13 18:40:36 +0200 |
---|---|---|
committer | Joachim Breitner <mail@joachim-breitner.de> | 2013-09-13 21:58:26 +0200 |
commit | 17a868afa169c52d8525a95cbed87b2fc12044c6 (patch) | |
tree | a04530408077a286a80ca8d34c5d6dc0c98eddc6 /compiler/prelude | |
parent | 638da2fecaaaf743c4da7f8e2522f4afc0d8400c (diff) | |
download | haskell-coercible.tar.gz |
Introduce coerce :: Coercible a b -> a -> bcoercible
This is the result of the design at
http://ghc.haskell.org/trac/ghc/wiki/NewtypeWrappers
The goal is to be able to convert between, say [First Int] and [Last
Int] with zero run-time overhead. To that end, we introduce a special
two parameter type class Coercible whose instances are created
automatically and on-the fly. This relies on and exploits the recent
addition of roles to core.
Diffstat (limited to 'compiler/prelude')
-rw-r--r-- | compiler/prelude/PrelInfo.lhs | 2 | ||||
-rw-r--r-- | compiler/prelude/PrelNames.lhs | 15 | ||||
-rw-r--r-- | compiler/prelude/TysWiredIn.lhs | 27 | ||||
-rw-r--r-- | compiler/prelude/primops.txt.pp | 56 |
4 files changed, 97 insertions, 3 deletions
diff --git a/compiler/prelude/PrelInfo.lhs b/compiler/prelude/PrelInfo.lhs index 4a39977797..0ef5b32eb3 100644 --- a/compiler/prelude/PrelInfo.lhs +++ b/compiler/prelude/PrelInfo.lhs @@ -130,7 +130,7 @@ ghcPrimExports = map (Avail . idName) ghcPrimIds ++ map (Avail . idName . primOpId) allThePrimOps ++ [ AvailTC n [n] - | tc <- funTyCon : primTyCons, let n = tyConName tc ] + | tc <- funTyCon : coercibleTyCon : primTyCons, let n = tyConName tc ] \end{code} diff --git a/compiler/prelude/PrelNames.lhs b/compiler/prelude/PrelNames.lhs index acac400c04..dfb3f82b7b 100644 --- a/compiler/prelude/PrelNames.lhs +++ b/compiler/prelude/PrelNames.lhs @@ -352,7 +352,7 @@ genericTyConNames = [ pRELUDE :: Module pRELUDE = mkBaseModule_ pRELUDE_NAME -gHC_PRIM, gHC_PRIMWRAPPERS, gHC_TYPES, gHC_GENERICS, gHC_MAGIC, +gHC_PRIM, gHC_PRIMWRAPPERS, gHC_TYPES, gHC_GENERICS, gHC_MAGIC, gHC_COERCIBLE, gHC_CLASSES, gHC_BASE, gHC_ENUM, gHC_GHCI, gHC_CSTRING, gHC_SHOW, gHC_READ, gHC_NUM, gHC_INTEGER_TYPE, gHC_LIST, gHC_TUPLE, dATA_TUPLE, dATA_EITHER, dATA_STRING, dATA_FOLDABLE, dATA_TRAVERSABLE, dATA_MONOID, @@ -370,6 +370,7 @@ gHC_TYPES = mkPrimModule (fsLit "GHC.Types") gHC_MAGIC = mkPrimModule (fsLit "GHC.Magic") gHC_CSTRING = mkPrimModule (fsLit "GHC.CString") gHC_CLASSES = mkPrimModule (fsLit "GHC.Classes") +gHC_COERCIBLE = mkPrimModule (fsLit "GHC.Coercible") gHC_BASE = mkBaseModule (fsLit "GHC.Base") gHC_ENUM = mkBaseModule (fsLit "GHC.Enum") @@ -1486,6 +1487,11 @@ doubleX2PrimTyConKey = mkPreludeTyConUnique 171 int32X4PrimTyConKey = mkPreludeTyConUnique 172 int64X2PrimTyConKey = mkPreludeTyConUnique 173 +ntTyConKey:: Unique +ntTyConKey = mkPreludeTyConUnique 174 +coercibleTyConKey :: Unique +coercibleTyConKey = mkPreludeTyConUnique 175 + ---------------- Template Haskell ------------------- -- USES TyConUniques 200-299 ----------------------------------------------------- @@ -1504,7 +1510,7 @@ unitTyConKey = mkTupleTyConUnique BoxedTuple 0 charDataConKey, consDataConKey, doubleDataConKey, falseDataConKey, floatDataConKey, intDataConKey, nilDataConKey, ratioDataConKey, stableNameDataConKey, trueDataConKey, wordDataConKey, - ioDataConKey, integerDataConKey, eqBoxDataConKey :: Unique + ioDataConKey, integerDataConKey, eqBoxDataConKey, coercibleDataConKey :: Unique charDataConKey = mkPreludeDataConUnique 1 consDataConKey = mkPreludeDataConUnique 2 doubleDataConKey = mkPreludeDataConUnique 3 @@ -1544,6 +1550,8 @@ gtDataConKey = mkPreludeDataConUnique 29 integerGmpSDataConKey, integerGmpJDataConKey :: Unique integerGmpSDataConKey = mkPreludeDataConUnique 30 integerGmpJDataConKey = mkPreludeDataConUnique 31 + +coercibleDataConKey = mkPreludeDataConUnique 32 \end{code} %************************************************************************ @@ -1710,6 +1718,9 @@ undefinedKey = mkPreludeMiscIdUnique 155 magicSingIKey :: Unique magicSingIKey = mkPreludeMiscIdUnique 156 + +coerceKey :: Unique +coerceKey = mkPreludeMiscIdUnique 157 \end{code} Certain class operations from Prelude classes. They get their own diff --git a/compiler/prelude/TysWiredIn.lhs b/compiler/prelude/TysWiredIn.lhs index d8c880f1c3..443c09cf1e 100644 --- a/compiler/prelude/TysWiredIn.lhs +++ b/compiler/prelude/TysWiredIn.lhs @@ -68,6 +68,7 @@ module TysWiredIn ( -- * Equality predicates eqTyCon_RDR, eqTyCon, eqTyConName, eqBoxDataCon, + coercibleTyCon, coercibleDataCon, coercibleClass, mkWiredInTyConName -- This is used in TcTypeNats to define the -- built-in functions for evaluation. @@ -88,6 +89,7 @@ import Type ( mkTyConApp ) import DataCon import Var import TyCon +import Class ( Class, mkClass ) import TypeRep import RdrName import Name @@ -147,6 +149,7 @@ wiredInTyCons = [ unitTyCon -- Not treated like other tuples, because , listTyCon , parrTyCon , eqTyCon + , coercibleTyCon , typeNatKindCon , typeSymbolKindCon ] @@ -172,6 +175,10 @@ eqTyConName, eqBoxDataConName :: Name eqTyConName = mkWiredInTyConName BuiltInSyntax gHC_TYPES (fsLit "~") eqTyConKey eqTyCon eqBoxDataConName = mkWiredInDataConName UserSyntax gHC_TYPES (fsLit "Eq#") eqBoxDataConKey eqBoxDataCon +coercibleTyConName, coercibleDataConName :: Name +coercibleTyConName = mkWiredInTyConName UserSyntax gHC_TYPES (fsLit "Coercible") coercibleTyConKey coercibleTyCon +coercibleDataConName = mkWiredInDataConName UserSyntax gHC_TYPES (fsLit "MkCoercible") coercibleDataConKey coercibleDataCon + charTyConName, charDataConName, intTyConName, intDataConName :: Name charTyConName = mkWiredInTyConName UserSyntax gHC_TYPES (fsLit "Char") charTyConKey charTyCon charDataConName = mkWiredInDataConName UserSyntax gHC_TYPES (fsLit "C#") charDataConKey charDataCon @@ -451,6 +458,26 @@ eqBoxDataCon = pcDataCon eqBoxDataConName args [TyConApp eqPrimTyCon (map mkTyVa k = mkTyVarTy kv a:b:_ = tyVarList k args = [kv, a, b] + + +coercibleTyCon :: TyCon +coercibleTyCon = mkClassTyCon + coercibleTyConName kind tvs [Representational, Representational] + rhs coercibleClass NonRecursive + where kind = mkArrowKinds [liftedTypeKind, liftedTypeKind] constraintKind + a:b:_ = tyVarList liftedTypeKind + tvs = [a, b] + rhs = DataTyCon [coercibleDataCon] False + +coercibleDataCon :: DataCon +coercibleDataCon = pcDataCon coercibleDataConName args [TyConApp eqReprPrimTyCon (liftedTypeKind : map mkTyVarTy args)] coercibleTyCon + where + a:b:_ = tyVarList liftedTypeKind + args = [a, b] + +coercibleClass :: Class +coercibleClass = mkClass (tyConTyVars coercibleTyCon) [] [] [] [] [] coercibleTyCon + \end{code} \begin{code} diff --git a/compiler/prelude/primops.txt.pp b/compiler/prelude/primops.txt.pp index 094c2f55e6..a59dfd624d 100644 --- a/compiler/prelude/primops.txt.pp +++ b/compiler/prelude/primops.txt.pp @@ -2279,6 +2279,62 @@ primop TraceMarkerOp "traceMarker#" GenPrimOp has_side_effects = True out_of_line = True +------------------------------------------------------------------------ +section "Safe coercions" +------------------------------------------------------------------------ + +pseudoop "coerce" + Coercible a b => a -> b + { The function {\tt coerce} allows you to safely convert between values of + types that have the same representation with no run-time overhead. In the + simplest case you can use it instead of a newtype constructor, to go from + the newtype's concrete type to the abstract type. But it also works in + more complicated settings, e.g. converting a list of newtypes to a list of + concrete types. + } + +primclass Coercible a b + { This two-parameter class has instances for types {\tt a} and {\tt b} if + the compiler can infer that they have the same representation. This class + does not have regular instances; instead they are created on-the-fly during + type-checking. Trying to manually declare an instance of {\tt Coercible} + is an error. + + Nevertheless one can pretend that the following three kinds of instances + exist. First, as a trivial base-case: + + {\tt instance a a} + + Furthermore, for every type constructor there is + an instance that allows to coerce under the type constructor. For + example, let {\tt D} be a prototypical type constructor ({\tt data} or {\tt + newtype}) with three type arguments, which have roles Nominal, + Representational resp. Phantom. Then there is an instance of the form + + {\tt instance Coercible b b' => Coercible (D a b c) (D a b' c')} + + Note that the nominal type arguments are equal, the representational type + arguments can differ, but need to have a {\tt Coercible} instance + themself, and the phantom type arguments can be changed arbitrarily. + + In SafeHaskell code, this instance is only usable if the constructors of + every type constructor used in the definition of {\tt D} (including + those of {\tt D} itself) is in scope. + + The third kind of instance exists for every {\tt newtype NT = MkNT T} and + comes in two variants, namely + + {\tt instance Coercible a T => Coercible a NT} + + {\tt instance Coercible T b => Coercible NT b} + + This instance is only usable if the constructor {\tt MkNT} is in scope. + + If, as a library author of a type constructor like {\tt Set a}, you + want to prevent a user of your module to write + {\tt coerce :: Set T -> Set NT}, + you need to set the role of {\tt Set}'s type parameter to Nominal. + } ------------------------------------------------------------------------ section "Float SIMD Vectors" |