使用'unsafeCoerce' [英] Use of 'unsafeCoerce'
问题描述
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
因此, 我们可以运行如下 因此,如果我们比较 In Haskell, there is a function called Provide an example of a way that is a violation and can lead to wonky, bad runtime behavior. is (obviously) fine and will not lead to runtime misbehavior. So what's a non-trivial use of Let's also say, as noted here, that the typeclass Now when we pattern match to destruct a value of 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 Of course, in this very particular situation we know that the only thing implementing Therefore, we can (very carefully)
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 Looks like our compiler internals are leaking! A more common example where this sort of behavior might be valuable is when using For example, Now, normally the compiler is pretty smart and recognizes that it can eliminate all of those If you (a) know that some type for a slight efficiency gain. This, for instance, occurs frequently in But let me again suggest that you really need to know what's going on before using One final thing to compare is the "typesafe Which, you might think of as being implemented using Thus, which we can run as follows
So if we compare this usage of 这篇关于使用'unsafeCoerce'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! 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
类运行时反映它们的类型。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?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
unsafeCoerce (3 :: Int) :: Int
unsafeCoerce
? Let's say we've got an typeclass-bound existential typemodule MyClass ( SomethingMyClass (..), intSomething ) where
class MyClass x where {}
instance MyClass Int where {}
data SomethingMyClass = forall a. MyClass a => SomethingMyClass a
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.SomethingMyClass
we'll be able to pull a "something" out from insidefoo :: 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!
...
MyClass
.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.intSomething :: SomethingMyClass -> Int
intSomething (SomethingMyClass a) = unsafeCoerce a -- shudder!
MyClass
module to instantiate more types to MyClass
otherwise our use of unsafeCoerce
becomes instantly unsafe.> instance MyClass Bool where {}
> intSomething (SomethingMyClass True)
6917529027658597398
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.Int
does not have a Monoid
definition because there are two natural monoids over Int
s: 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)
Sum
constructors in order to produce more efficient code. Sadly, there are times when it cannot, especially in highly polymorphic situations.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 dounsafeCoerce (x :: a) :: b
lens
and is expressed in the Data.Profunctor.Unsafe
module of profunctors
, a dependency of lens
.unsafeCoerce
like this is anything but highly unsafe.
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
unsafeCoerce
and a function typeOf :: Typeable a => a -> TypeRep
where TypeRep
are unforgeable, runtime tokens which reflect the type of a value. Then we havecast :: (Typeable a, Typeable b) => a -> Maybe b
cast a = if (typeOf a == typeOf b) then Just b else Nothing
where b = unsafeCoerce a
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
> getAnA (Forget A)
Just A
> getAnA (Forget B)
Nothing
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.