在Haskell中有几种“不安全”的概念。
计算值会涉及到I/O操作。主要嫌疑人是unsafePerformIO
。关于惰性I/O和unsafeInterleaveIO
是否应被视为不安全存在争议。
某些事情会破坏类型系统。主要嫌疑人是unsafeCoerce
,但unsafePerformIO
也可能会这样做。
某些事情会破坏内存安全而不破坏类型系统(感谢Carl提醒我)。主要嫌疑人是unsafe
数组或向量索引操作以及错误使用外部函数接口。
计算结果取决于评估的顺序。主要嫌疑人是unsafePerformIO
,但unsafeInterleaveST
也可以做到。
评估可能会导致异常或无限循环。这是一种相对温和的不安全性...除非它不是。
某些事情会破坏Haskell程序员用来推理其代码的约定(“法律”)。是否应将其视为“不安全”存在争议。例如:将seq
应用于函数,在使用coerce
以更改函数相对于其参考实现的元数的方式时,并在将先前的部分应用程序应用于可能是底部的内容时引起问题(在某些情况下,这样做有很好的性能原因),编写违反functor、applicative、monad、traversable等定律的类实例。期望参数满足前提条件但不检查它们是否满足(例如,将升序列表快速转换为集合或映射的函数)。
为了帮助程序员控制某些不安全的形式,安全的Haskell系统根据模块使用的导入和语言扩展将模块分类为安全或不安全。我没有研究细节,但GarethR 指出:
我认为你的第1到3个概念在安全的Haskell中会被认为是不安全的。值得一读,因为安全的Haskell作者显然深思熟虑。
Ørjan Johansen 指出:
安全的Haskell也禁止一些符合第6点的事情,例如可以规避模块导出边界(模板Haskell,广义新类型派生)或更改导入代码行为(规则,重叠实例)的扩展。
程序员可以将模块标记为Safe
,表示他们希望GHC检查其是否安全;标记为Unsafe
,表示它不安全;或标记为Trustworthy
,表示作者声称相信其API是安全的,尽管其实现使用了不安全的特性。
fmap f (Identity x) = Identity (f x)
给出了fmap
的arity 2。fmap = coerce
将其设置为arity 1。因此,依赖于使用不可靠的seq
来使其arity为2的人会感到惊讶。 - dfeuer
seq
是不安全的有点极端。 - Ørjan Johansen