什么是Haskell中的不安全函数?

11

我认为不安全的函数是指一个函数声称它将返回某种类型的值,但实际上它可能会抛出异常并结束执行,因此根本没有返回任何值,但我不确定。

或者说不安全的函数是指可以返回与签名中声明的类型不同的值的函数吗?这不是一种弱类型函数吗?

在Haskell中,弱类型和不安全是否是同义词?

这可能是一个愚蠢的问题,但我找不到一个直接的答案。

我查看了readLn的文档,希望看到不安全函数的引用,但没有找到。

这篇文章“不安全函数”提到了破坏类型系统的事情,但没有具体说明是通过异常还是通过返回错误类型的值实现的。

那么,在Haskell中什么是不安全函数?


"Unsafe" 没有正式的定义。我认为它是指“可以打破语言的正常规则”。 - melpomene
5
看那个维基百科文章,称 seq 是不安全的有点极端。 - Ørjan Johansen
1
@melpomene,现在,“unsafe”是一个“汉普蒂鸭嘴兽词语”,意思是“可以打破”说话者认为他们希望编程的语言的“正常规则”,但由于某种原因没有遵守。它曾经表示dfeuer's 1-4(绝对不是5或6)。 - Reid Barton
2个回答

21

在Haskell中有几种“不安全”的概念。

  1. 计算值会涉及到I/O操作。主要嫌疑人是unsafePerformIO。关于惰性I/O和unsafeInterleaveIO是否应被视为不安全存在争议。

  2. 某些事情会破坏类型系统。主要嫌疑人是unsafeCoerce,但unsafePerformIO也可能会这样做。

  3. 某些事情会破坏内存安全而不破坏类型系统(感谢Carl提醒我)。主要嫌疑人是unsafe数组或向量索引操作以及错误使用外部函数接口。

  4. 计算结果取决于评估的顺序。主要嫌疑人是unsafePerformIO,但unsafeInterleaveST也可以做到。

  5. 评估可能会导致异常或无限循环。这是一种相对温和的不安全性...除非它不是。

  6. 某些事情会破坏Haskell程序员用来推理其代码的约定(“法律”)。是否应将其视为“不安全”存在争议。例如:将seq应用于函数,在使用coerce以更改函数相对于其参考实现的元数的方式时,并在将先前的部分应用程序应用于可能是底部的内容时引起问题(在某些情况下,这样做有很好的性能原因),编写违反functor、applicative、monad、traversable等定律的类实例。期望参数满足前提条件但不检查它们是否满足(例如,将升序列表快速转换为集合或映射的函数)。

安全的Haskell

为了帮助程序员控制某些不安全的形式,安全的Haskell系统根据模块使用的导入和语言扩展将模块分类为安全或不安全。我没有研究细节,但GarethR 指出

我认为你的第1到3个概念在安全的Haskell中会被认为是不安全的。值得一读,因为安全的Haskell作者显然深思熟虑。

Ørjan Johansen 指出

安全的Haskell也禁止一些符合第6点的事情,例如可以规避模块导出边界(模板Haskell,广义新类型派生)或更改导入代码行为(规则,重叠实例)的扩展。

程序员可以将模块标记为Safe,表示他们希望GHC检查其是否安全;标记为Unsafe,表示它不安全;或标记为Trustworthy,表示作者声称相信其API是安全的,尽管其实现使用了不安全的特性。


2
@OrjanJohansen,我想我没有描述得很准确。与参考实现相比,它可以改变函数的arity。fmap f (Identity x) = Identity (f x)给出了fmap的arity 2。fmap = coerce将其设置为arity 1。因此,依赖于使用不可靠的seq来使其arity为2的人会感到惊讶。 - dfeuer
你忽略了牺牲内存安全的情况,比如在Vector或Array中出现的情况。 - Carl
很好的回答。您忘记添加“Safe Haskell”的概念了。我认为您的第1至3点概念在“Safe Haskell”中可能被视为不安全。阅读一下相关内容可能会值得,因为“Safe Haskell”的作者们显然对安全性有深入的思考。 - GarethR
@GarethR,我会添加它,但这是一种相反的方式,因为它是保守的。 - dfeuer
Safe Haskell 还禁止了一些符合第6点的东西,例如可以绕过模块导出边界的扩展(Template Haskell、generalized newtype deriving),或者改变导入代码行为的扩展(rules、overlapping instances)。 - Ørjan Johansen
显示剩余3条评论

2
来自Haskell FFI wiki
如果在一个外部导入声明中使用unsafe关键字进行注释,这表明编译器不会调用其他的Haskell函数,直接或间接地,并且您不介意系统中运行的任何其他Haskell线程在调用期间被阻塞。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接