Safe.Coerce
- Package
- purescript-safe-coerce
- Repository
- purescript/purescript-safe-coerce
#coerce Source
coerce :: forall a b. Coercible a b => a -> b
Coerce a value of one type to a value of some other type, without changing
its runtime representation. This function behaves identically to
unsafeCoerce
at runtime. Unlike unsafeCoerce
, it is safe, because the
Coercible
constraint prevents any use of this function from compiling
unless the compiler can prove that the two types have the same runtime
representation.
One application for this function is to avoid doing work that you know is a
no-op because of newtypes. For example, if you have an Array (Conj a)
and you
want an Array (Disj a)
, you could do Data.Array.map (un Conj >>> Disj)
, but
this performs an unnecessary traversal of the array, with O(n) cost.
coerce
accomplishes the same with only O(1) cost:
mapConjToDisj :: forall a. Array (Conj a) -> Array (Disj a)
mapConjToDisj = coerce
Re-exports from Prim.Coerce
#Coercible
class Coercible (a :: k) (b :: k)
Coercible is a two-parameter type class that has instances for types a
and 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 according to a set of rules.
First, Coercible obeys reflexivity - any type has the same representation as itself:
instance coercibleReflexive :: Coercible a a
Second, Coercible obeys symmetry - if a type a
can be coerced to some
other type b
, then b
can also be coerced back to a
:
instance coercibleSymmetric :: Coercible a b => Coercible b a
Third, Coercible obeys transitivity - if a type a
can be coerced to some
other type b
which can be coerced to some other type c
, then a
can
also be coerced to c
:
instance coercibleTransitive :: (Coercible a b, Coercible b c) => Coercible a c
Fourth, for every type constructor there is an instance that allows one
to coerce under the type constructor (data
or newtype
). For example,
given a definition:
data D a b = D a
there is an instance:
instance coercibleConstructor :: Coercible a a' => Coercible (D a b) (D a' b')
Note that, since the type variable a
plays a role in D
's representation,
we require that the types a
and a'
are themselves Coercible
. However,
since the variable b
does not play a part in D
's representation (a type
such as b
is thus typically referred to as a "phantom" type), b
and b'
can differ arbitrarily.
Fifth, for every newtype NT = MkNT T
, there is a pair of instances which
permit coercion in and out of the newtype
:
instance coercibleNewtypeLeft :: Coercible a T => Coercible a NT
instance coercibleNewtypeRight :: Coercible T b => Coercible NT b
To prevent breaking abstractions, these instances are only usable if the
constructor MkNT
is in scope.
Sixth, every pair of unsaturated type constructors can be coerced if there is an instance for the fully saturated types. For example, given the definitions:
newtype NT1 a = MkNT1 a newtype NT2 a b = MkNT2 b
there is an instance:
instance coercibleUnsaturedTypes :: Coercible (NT1 b) (NT2 a b) => Coercible NT1 (NT2 a)
This rule may seem puzzling since it is impossible to apply coerce
to a term
of type NT1
but it is necessary to coerce types with higher kinded parameters.
Seventh, every pair of rows can be coerced if they have the same labels, the corresponding types for each label and their tails are coercible:
instance coercibleRow :: (Coercible a b, Coercible r s) => Coercible ( label :: a | r ) ( label :: b | s )
Closed rows can't be coerced to open rows.
- Modules
- Safe.
Coerce