2013-01-14 68 views
13

我有一个情况,我现在正在使用非常可怕的函数unsafeCoerce。幸运的是,这并不是什么重要的事情,但我想知道这是否是对这个功能的安全使用,或者是否有另一种方法来解决其他人知道的这个特殊问题。这是安全使用不安全的协议吗?

我的代码是类似以下内容:

data Token b = Token !Integer 

identical :: Token a -> Token b -> Bool 
identical (Token a) (Token b) = a == b 

data F a = forall b. F (Token b) (a -> b) 

retrieve :: Token b -> F a -> Maybe (a -> b) 
retrieve t (F t' f) = if identical t t' then Just (unsafeCoerce f) else Nothing 

两个额外的事情需要注意,是这些令牌,我用它来确保整数为他们供应是唯一一个单子内使用(即我不会做同样的标记两次)。我也使用一个全量化的阴影类型变量,就像ST monad一样,确保(假设只有我在模块中公开的方法被使用),没有办法返回一个标记(或者实际上甚至是F)从monad没有它是一个类型错误。我也不公开这个令牌构造函数。

我认为,据我所知,这应该是一个安全使用不安全的Coerce,正如我可以说的(我希望)相当高的信心,我强制的价值实际上正是我的类型我强迫它,但我可能是错的。我也尝试过使用Data.Typeable,它很好地工作,但目前我正在试图避免Typeable约束,特别是因为gcast似乎在很多方面都有类似的做法,而且我仍然需要令牌来区分不同的同类型的Fs。

非常感谢任何帮助/建议。

+4

这看起来很像'Data.Typeable',它使用'unsafeCoerce'来实现'cast'。 –

+1

这很像这样 - 事实上我在问题最后一段的后半部分曾经提到过iirc。不管怎么说,还是要谢谢你。 – DarkOtter

+2

如果您有效地复制'cast',那么'unsafeCoerce'的使用是安全的,但是您确实会丢失编译器生成的'typeOf' /'TypeRep'。您可以考虑在您的令牌中使用“TypeRep”而不是“Integer”。 –

回答

9

这不是安全本身:

oops :: F Bool 
oops = F (Token 12) not 

bad :: Token Int 
bad = Token 12 

*Token> maybe 3 ($ True) $ retrieve bad oops 
1077477808 

F a是一个存在性量化的类型,你不知道b走进它是什么类型。由于identical并不关心Token的类型参数,因此它不能检查是否来自retrieve的第一个参数与进入F a的内容有什么关系。

无论您保护

两个额外的事情需要注意,是这些令牌,我用什么来保证一个单子内使用,对他们整数的供应是唯一的(即我不作相同的令牌两次)。我也使用一个全量化的阴影类型变量,就像ST monad一样,确保(假设只有我在模块中公开的方法被使用),没有办法返回一个标记(或者实际上甚至是F)从monad没有它是一个类型错误。我也不公开这个令牌构造函数。

强度足以让它在实践中安全,我不能没有看到它。如果在计算之外确实没有创建Token,并且TokenInteger值唯一地表征了类型参数,那么这将是安全的。

14

您已经实施了动态打字的限制形式,大致遵循Data.Dynamic的风格 - 即将(不透明)值与其类型的证据配对。 在运行时,您可以根据随数据提供的证据进行不安全的强制。

fromDyn (Dynamic t v) def 
    | typeOf def == t = unsafeCoerce v 
    | otherwise  = def 

这是canoncial方法,具有悠久的历史,可以追溯到:

Mart'ın阿巴迪,卢卡·卡迪利,本杰明·皮尔斯和戈登普洛特金。动态输入静态类型语言。 ACM交易上编程语言和系统, 13(2):237-268,1991年4月

该方法的安全性依赖于运行时类型的令牌的不可伪造。在你的情况下,任何人都可以构建等同于两种类型的令牌 - 你需要保证从类型到令牌的1-1映射,并确保恶意用户不能构造不正确的令牌。在GHC的情况下,我们相信可键入的实例(和模块抽象)。