使用'unsafeCoerce' [英] Use of 'unsafeCoerce'

查看:125
本文介绍了使用'unsafeCoerce'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Haskell中,有一个名为 unsafeCoerce 的函数,它把任何东西变成任何其他类型的东西。这究竟是用来干什么的?就像,为什么我们要以不安全的方式将事物转化为对方? 提供 unsafeCoerce实际上使用。与Hackage的链接将有所帮助。

解决方案

unsafeCoerce 让你说服你喜欢的任何财产的类型系统。因此,只有当你完全确定你声明的属性是真实的时候,它才是安全的。因此,例如:

  unsafeCoerce True :: Int 

是违规行为,并且可能会导致运行不良,运行不良。

  unsafeCoerce(3 :: Int):: Int 

显然很好,不会导致运行时不正确的行为。






那么, unsafeCoerce ?假设我们有一个类型类型的存在类型

  module MyClass(SomethingMyClass(..),intSomething)其中

class MyClass x其中{}

实例MyClass Int其中{}

数据SomethingMyClass = forall a。 MyClass a => SomethingMyClass a

我们还要说,正如这里所指出的,类型类 MyClass 未导出,因此没有其他人可以创建它的实例。事实上, Int 是实例化它的唯一事物,也是唯一一个会这样做的事情。



现在当我们模式匹配以破坏 SomethingMyClass 的值,我们可以从里面拉出一些东西。

  foo :: SomethingMyClass  - > ... 
foo(SomethingMyClass a)=
- 这里我们有一个类型为'a`的值存在a。 MyClass a => a`
-
- 这是完全没用的,因为MyClass甚至没有任何
- 方法供我们使用!
...

现在,正如评论所示,已经退出了没有类型信息 - 它被存在的情境遗忘了。它可以是任何实例 MyClass



当然,在非常特别情况,我们知道实现 MyClass 只是 Int 。因此,我们的值 a 必须实际上的类型为 Int 。我们永远无法说服类型分析者这是真的,但由于外部证据,我们知道它是。

因此,我们可以(非常仔细地)

  intSomething :: SomethingMyClass  - > Int 
intSomething(SomethingMyClass a)= unsafeCoerce a - 不寒而栗!






现在,我希望我已经提出这是一个可怕的,危险的想法,但它也可以让我们了解我们可以利用哪些信息来了解类型分析者不能做的事情。



在非 - 病理情况,这很少见。更稀有的情况是,使用我们知道的东西以及类型检测器本身不是病态的。在上面的例子中,我们必须完全确定没有人将我们的 MyClass 模块扩展为实例化更多类型为 MyClass 否则我们使用 unsafeCoerce 会立即变得不安全。

 >实例MyClass Bool其中{} 
> intSomething(SomethingMyClass True)
6917529027658597398

看起来像我们的编译器内部错误!






这种行为可能有价值的一个更常见的例子是使用 newtype 包装。我们可能会在 newtype 封装器中封装一个类型,以专门化它的实例定义。 / p>

例如, Int 没有 Monoid 定义因为在 Int s:总和和乘积之上有两个自然单数形式。相反,我们使用 newtype 包装更加明确。

  newtype Sum a = Sum {getSum :: a} 

实例Num a => Monoid(Sum a)其中
mempty = Sum 0
mappend(Sum a)(Sum b)= Sum(a + b)

现在,编译器通常很聪明,并且认识到它可以消除所有这些 Sum 构造函数,以便产生更多高效的代码。如果你(a)知道某些类型 a 实际上只是一个新类型包装的 b ,并且(b)知道编译器无法自己推断它,那么您可能需要执行

  unsafeCoerce(x :: a):: b 

略微提高效率。例如,这通常出现在 lens 中,并以 Data.Profunctor.Unsafe 模块 profunctors ,一个依赖于镜头



但是,让我再次建议您在使用前确实需要知道发生了什么 unsafeCoerce 就像这是一件非常不安全的事情。






最后一件事比较是 Data.Typeable 中可用的typesafe cast 。这个函数看起来有点像 unsafeCoerce ,但是仪式更多。

  unsafeCoerce :: a  - > b 
cast ::(可键入a,可键入b)=> a - >也许b

您可能认为使用 unsafeCoerce code>和一个函数 typeOf :: Typeable a => a - > TypeRep 其中 TypeRep 是不可伪造的,反映值类型的运行时令牌。然后我们有

  cast ::(Typeable a,Typeable b)=> a  - >也许b 
cast a = if(typeOf a == typeOf b)then Just b else Nothing
where b = unsafeCoerce a

因此, cast 能够确保 a b 在运行时确实是相同的,如果它们可以决定返回 Nothing 不是。举个例子:

$ $ p $ $ $ $ $ $ $ $ $ {$#$ $ $ $ $ {$#$ $ $ $ $ $ $ {$#$语言存在量化# - }

数据A = A派生(显示,可键入)
数据B = B派生(显示,可键入)

数据Forget = forall a。可键入的a =>忘记

getAnA ::忘记 - >也许A
getAnA(忘记某些东西)=抛出一些东西

我们可以运行如下

 > getAnA(忘记A)
只需A
> getAnA(忘记B)
Nothing






因此,如果我们比较 cast unsafeCoerce 的这种用法,我们可以看到它可以实现一些相同的功能。特别是,它允许我们重新发现可能被 ExistentialQuantification 遗忘的信息。但是, cast 会在运行时手动检查类型,以确保它们确实相同,因此不能不安全地使用。为此,它要求源类型和目标类型都允许通过 Typeable 类运行时反映它们的类型。


In Haskell, there is a function called unsafeCoerce, that turns anything into any other type of thing. What exactly is this used for? Like, why we would you want to transform things into each other in such an "unsafe" way?

Provide an example of a way that unsafeCoerce is actually used. A link to Hackage would help. Example code in someones question would not.

解决方案

unsafeCoerce lets you convince the type system of whatever property you like. It's thus only "safe" exactly when you can be completely certain that the property you're declaring is true. So, for instance:

unsafeCoerce True :: Int

is a violation and can lead to wonky, bad runtime behavior.

unsafeCoerce (3 :: Int) :: Int

is (obviously) fine and will not lead to runtime misbehavior.


So what's a non-trivial use of unsafeCoerce? Let's say we've got an typeclass-bound existential type

module MyClass ( SomethingMyClass (..), intSomething ) where

class MyClass x where {}

instance MyClass Int where {}

data SomethingMyClass = forall a. MyClass a => SomethingMyClass a

Let's also say, as noted here, that the typeclass MyClass is not exported and thus nobody else can ever create instances of it. Indeed, Int is the only thing that instantiates it and the only thing that ever will.

Now when we pattern match to destruct a value of SomethingMyClass we'll be able to pull a "something" out from 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 point, as the comment suggests, the value we've pulled out has no type information—it's been "forgotten" by the existential context. It could be absolutely anything which instantiates MyClass.

Of course, in this very particular situation we know that the only thing implementing MyClass is Int. So our value a must actually have type Int. We could never convince the typechecker that this is true, but due to an outside proof we know that it is.

Therefore, we can (very carefully)

intSomething :: SomethingMyClass -> Int
intSomething (SomethingMyClass a) = unsafeCoerce a    -- shudder!


Now, hopefully I've suggested that this is a terrible, dangerous idea, but it also may give a taste of what kind of information we can take advantage of in order to know things that the typechecker cannot.

In non-pathological situations, this is rare. Even rarer is a situation where using something we know and the typechecker doesn't isn't itself pathological. In the above example, we must be completely certain that nobody ever extends our MyClass module to instantiate more types to MyClass otherwise our use of unsafeCoerce becomes instantly unsafe.

> instance MyClass Bool where {}
> intSomething (SomethingMyClass True)
6917529027658597398

Looks like our compiler internals are leaking!


A more common example where this sort of behavior might be valuable is when using newtype wrappers. It's a fairly common idea that we might wrap a type in a newtype wrapper in order to specialize its instance definitions.

For example, Int does not have a Monoid definition because there are two natural monoids over Ints: sums and products. Instead, we use newtype wrappers to be more explicit.

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, normally the compiler is pretty smart and recognizes that it can eliminate all of those Sum constructors in order to produce more efficient code. Sadly, there are times when it cannot, especially in highly polymorphic situations.

If you (a) know that some type a is actually just a newtype-wrapped b and (b) know that the compiler is incapable of deducing this itself, then you might want to do

unsafeCoerce (x :: a) :: b

for a slight efficiency gain. This, for instance, occurs frequently in lens and is expressed in the Data.Profunctor.Unsafe module of profunctors, a dependency of lens.

But let me again suggest that you really need to know what's going on before using unsafeCoerce like this is anything but highly unsafe.


One final thing to compare is the "typesafe cast" available in Data.Typeable. This function looks a bit like unsafeCoerce, but with much more ceremony.

unsafeCoerce ::                             a ->       b
cast         :: (Typeable a, Typeable b) => a -> Maybe b

Which, you might think of as being implemented using unsafeCoerce and a function typeOf :: Typeable a => a -> TypeRep where TypeRep are unforgeable, runtime tokens which reflect the type of a 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

Thus, cast is able to ensure that the types of a and b really are the same at runtime, 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 run as follows

> getAnA (Forget A)
Just A
> getAnA (Forget B)
Nothing


So if we compare this usage of cast with unsafeCoerce we see that it can achieve some of the same functionality. In particular, it allows us to rediscover information that may have been forgotten by ExistentialQuantification. However, cast manually checks the types at runtime to ensure that they are truly the same and thus cannot be used unsafely. To do this, it demands that both the source and target types allow for runtime reflection of their types via the Typeable class.

这篇关于使用'unsafeCoerce'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆