Using 'unsafeCoerce'

Haskell has a function called unsafeCoerce that turns something into any other type of thing. What exactly is this used for? For example, why would we like to turn things into each other in such a "unsafe" way?

Imagine an example of using unsafeCoerce . Link to Hackage will help. Sample code in some question will not.

+6
source share
3 answers

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:

 {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ExistentialQuantification #-} data A = A deriving (Show, Typeable) data B = B deriving (Show, Typeable) data Forget = forall a . Typeable a => Forget a getAnA :: Forget -> Maybe A getAnA (Forget something) = cast something 

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.

+16
source

The only time I ever felt compelled to use unsafeCoerce was on finite natural numbers.

 {-# LANGUAGE DataKinds, GADTs, TypeFamilies, StandaloneDeriving #-} data Nat = Z | S Nat deriving (Eq, Show) data Fin (n :: Nat) :: * where FZ :: Fin (S n) FS :: Fin n -> Fin (S n) deriving instance Show (Fin n) 

Fin n is a simply connected data structure statically guaranteed to be less than a natural number of level type n with which it is parameterized.

 -- OK, 1 < 2 validFin :: Fin (S (SZ)) validFin = FS FZ -- type error, 2 < 2 is false invalidFin :: Fin (S (SZ)) invalidFin = FS (FS FZ) 

Fin can be used to safely index into various data structures. This is pretty standard depending on typed languages, although not in Haskell.

Sometimes we want to convert the value of Fin n to Fin m , where m greater than n .

 relaxFin :: Fin n -> Fin (S n) relaxFin FZ = FZ relaxFin (FS n) = FS (relaxFin n) 

relaxFin by definition not an opportunist, but for types that need to be checked, value movement is still required. Therefore, we can use unsafeCoerce instead of relaxFin . More pronounced speed gains may result from forcing large data structures containing Fin -s (for example, you could have lambda members with Fin -s as related variables).

This is admittedly an exotic example, but I find it interesting in the sense that it is fairly safe: I can't think about how to use external libraries or a secure user code. Maybe I'm wrong, and I would be willing to hear about potential security issues.

+6
source

You cannot use unsafeCoerce , I really can recommend it, but I see that in some cases such a thing can be useful.

The first use that comes to mind is the implementation of Typeable routines. In particular, cast :: (Typeable a, Typeable b) => a -> Maybe b provides safe type behavior, so it is safe to use, but it must play dirty tricks in its implementation.

Maybe unsafeCoerce might find some use in importing FFI routines to make types match. In the end, FFI already allows you to import impure C functions as pure, so it is internally safe. Please note that "unsafe" does not mean impossibility to use, but simply "putting the burden of proof on the programmer."

Finally, pretend that sortBy does not exist. Consider the following example:

 -- Like Int, but using the opposite ordering newtype Rev = Rev { unRev :: Int } instance Ord Rev where compare (Rev x) (Rev y) = compare yx sortDescending :: [Int] -> [Int] sortDescending = map unRev . sort . map Rev 

The code above works, but it feels silly IMHO. We execute two Rev,unRev using functions such as Rev,unRev , which, as we know, are not executed at runtime. Therefore, we simply scan the list twice for no reason, but convince the compiler to use the correct instance of Ord .

The efficiency of these cards should be small, since we also sort the list. However, it is tempting to rewrite map Rev as unsafeCoerce :: [Int]->[Rev] and save some time.

Note that with the coherence function

 castNewtype :: IsNewtype t1 t2 => f t2 -> f t1 

where the restriction means that t1 is a new type for t2 , but that would be very dangerous. Consider

 castNewtype :: Data.Set Int -> Data.Set Rev 

The above will lead to a break in the data structure, as we change the order below! Since Data.Set is implemented as a binary search tree, this can do a lot of damage.

+2
source

All Articles