unsafeCoerce lets you convince the type system of what you like. This, therefore, is only “safe” when you can be completely sure that the ad you are announcing is true. For example:
unsafeCoerce True :: Int
is a violation and may lead to erratic, poor execution.
unsafeCoerce (3 :: Int) :: Int
(obviously) fine and won't lead to runtime behavior.
So what is the non-trivial use of unsafeCoerce ? Say we have an existential type with a type binding
module MyClass ( SomethingMyClass (..), intSomething ) where class MyClass x where {} instance MyClass Int where {} data SomethingMyClass = forall a. MyClass a => SomethingMyClass a
Let it also be indicated that typeclass MyClass not exported and therefore no one can instantiate. In fact, Int is the only thing that creates it and the only thing that will ever be.
Now, when we map the template to destroy the value of SomethingMyClass , we can pull the "something" from the inside
foo :: SomethingMyClass -> ... foo (SomethingMyClass a) = -- here we have a value `a` with type `exists a . MyClass a => a` -- -- this is totally useless since `MyClass` doesn't even have any -- methods for us to use! ...
Now, at this moment, as follows from the commentary, the value we pulled out does not have type information - it was “forgotten” by the existential context. This could be absolutely everything MyClass creates.
Of course, in this particular situation, we know that the only thing that implements MyClass is Int . Therefore, our value of a must be of type Int . We could never convince typechecker that this is true, but due to external evidence, we know that it is.
Therefore we can (very carefully)
intSomething :: SomethingMyClass -> Int intSomething (SomethingMyClass a) = unsafeCoerce a -- shudder!
Now, I hope I suggested that this is a terrible, dangerous idea, but it can also give an idea of what information we can use in order to know things that typechecker tags cannot execute.
In non-pathological situations, this is rare. Even rarer is the situation when using something we know and the typechecker method is not pathological in itself. In the above example, we must be absolutely sure that no one expands our MyClass module to create more types for MyClass , otherwise our use of unsafeCoerce will become unsafe.
> instance MyClass Bool where {} > intSomething (SomethingMyClass True) 6917529027658597398
It looks like our internal compiler components are leaking!
A more common example where this behavior can be valuable is the use of newtype wrappers. This is a fairly common idea that we could wrap a type in a newtype wrapper to specialize instance definitions.
For example, Int has no Monoid definition, since there are two natural monoids on Int s: sums and products. Instead, we use newtype wrappers for a more explicit expression.
newtype Sum a = Sum { getSum :: a } instance Num a => Monoid (Sum a) where mempty = Sum 0 mappend (Sum a) (Sum b) = Sum (a+b)
Now, as a rule, the compiler is pretty smart and admits that it can eliminate all of these Sum constructors in order to create more efficient code. Unfortunately, there are times when this is not possible, especially in highly polymorphic situations.
If you (a) know that some type a is actually just a wrapped newtype b and (b) knows that the compiler is not capable of outputting it yourself, then you might want to do
unsafeCoerce (x :: a) :: b
for a slight increase in efficiency. This, for example, is often found in the lens and is expressed in the Data.Profunctor.Unsafe module profunctors , depending on the lens .
But let me assume again that you really need to know what is going on before using unsafeCoerce , as if it were nothing but very dangerous.
The last thing to compare is the "typafe cast ", available in Data.Typeable . This feature is a bit like unsafeCoerce , but with a lot more ceremony.
unsafeCoerce :: a -> b cast :: (Typeable a, Typeable b) => a -> Maybe b
What can you think about what is implemented using unsafeCoerce and the typeOf :: Typeable a => a -> TypeRep , where TypeRep are not applicable run-time tokens that reflect the type of value. Then we have
cast :: (Typeable a, Typeable b) => a -> Maybe b cast a = if (typeOf a == typeOf b) then Just b else Nothing where b = unsafeCoerce a
That way, cast can ensure that types a and b do match at run time, and it can decide to return Nothing if they are not. As an example:
{-
which we can do as follows
> getAnA (Forget A) Just A > getAnA (Forget B) Nothing
So, if we compare this use of cast with unsafeCoerce , we will see that it can achieve some of the same functions. In particular, this allows us to re-learn information that may have been forgotten by ExistentialQuantification . However, cast manually checks the types at runtime to make sure that they are really the same and therefore cannot be used insecurely. This requires that both the source and target types allow reflection at runtime of their types using the Typeable class.