46

Haskell 通常被称为纯函数式语言的示例。鉴于 的存在,这怎么能证明是合理的System.IO.Unsafe.unsafePerformIO

编辑:我认为“纯功能”意味着不可能将不纯的代码引入程序的功能部分。

4

8 回答 8

119

我们称之为 Haskell 的语言

unsafePerformIO外部函数接口规范的一部分,而不是核心Haskell 98规范。它可以用来做不逃脱某些范围的局部副作用,以便公开一个纯粹的功能接口。也就是说,当类型检查器无法为我们执行此操作时,我们使用它来隐藏效果(与 ST monad 不同,后者通过静态保证隐藏效果)。

为了准确说明我们称之为“Haskell”的多种语言,请考虑下图。每个环对应于一组特定的计算特征,按安全性排序,面积与表达能力相关(即,如果你有这个特征,你可以编写的程序数量)。

被称为 Haskell 98 的语言在中间被指定,承认全部和部分功能。Agda(或Epigram)只允许使用全部函数,表达力更差,但“更纯粹”且更安全。虽然我们今天使用的 Haskell 包含了 FFI 的所有内容,unsafePerformIO 所在的地方。也就是说,您可以在现代 Haskell 中编写任何东西,但如果您使用外环的东西,则很难建立由内环简化的安全和安全保证。

替代文字

因此,Haskell 程序通常不是由 100% 引用透明的代码构建的,但是,它是唯一一种默认情况下是纯的中等通用语言。

于 2010-06-26T17:04:22.603 回答
34

我认为“纯功能”意味着不可能引入不纯的代码......

真正的答案是它unsafePerformIO不是Haskell的一部分,就像垃圾收集器或运行时系统是 Haskell 的一部分一样。 unsafePerformIO是否存在于系统中,以便构建系统的人可以在非常有效的硬件上创建纯功能抽象。所有真正的语言都有漏洞,使系统构建者能够以比使用 C 代码或汇编代码更有效的方式完成工作。

至于副作用和 I/O 如何通过IOmonad 融入 Haskell 的更广泛的图景,我认为考虑 Haskell 的最简单方法是它是一种描述有效计算的纯语言。当描述的计算为main时,运行时系统忠实地执行那些效果。

unsafePerformIO是一种以不安全的方式获得效果的方法;其中“不安全”的意思是“程序员必须保证安全”——编译器不检查任何内容。如果您是一位精明的程序员并且愿意承担繁重的证明义务,您可以使用unsafePerformIO. 但此时你不再使用 Haskell 编程了。您正在使用一种看起来很像 Haskell 的不安全语言进行编程。

于 2010-06-26T17:20:57.837 回答
4

语言/实现纯粹是功能性的。它包括几个“逃生舱口”,如果您不想使用,则不必使用。

于 2010-06-26T16:28:37.213 回答
2

我不认为 unsafePerformIO 意味着 haskell 以某种方式变得不纯。您可以从不纯函数创建纯(引用透明)函数。

考虑跳过列表。为了使其正常工作,它需要访问 RNG,一个不纯的函数,但这不会使数据结构不纯。如果您添加一个项目,然后将其转换为列表,则每次给定您添加的项目都会返回相同的列表。

出于这个原因,我认为 unsafePerformIO 应该被认为是 promisePureIO。一个函数意味着具有副作用并因此会被类型系统标记为不纯的函数可以被类型系统确认为引用透明的。

我知道你必须有一个稍微弱一点的纯定义才能保持这一点。即纯函数是引用透明的,并且由于副作用(如打印)而从不调用。

于 2011-01-04T11:02:45.150 回答
1

不幸的是,语言必须做一些现实世界的工作,这意味着与外部环境交谈。

好消息是您可以(并且应该)将这种“过时”代码的使用限制在程序中少数特定的有据可查的部分。

于 2010-06-26T16:29:28.297 回答
1

我有一种感觉,我会因为说出我要说的话而非常不受欢迎,但我觉得我必须对这里提供的一些(在我看来是错误的)信息做出回应。

尽管 unsafePerformIO 确实作为 FFI 附录的一部分被正式添加到该语言中,但其原因主要是历史而非逻辑。它是非官方的,早在 Haskell 有 FFI 之前就被广泛使用。它从未正式成为主要 Haskell 标准的一部分,因为正如您所观察到的,这太尴尬了。我猜希望它会在未来的某个时候以某种方式消失。好吧,这还没有发生,在我看来也不会发生。

FFI 附录的开发为 unsafePerformIO 潜入官方语言标准提供了一个方便的借口,因为与添加调用外国 (IE C) 代码的能力相比,它在这里可能看起来还不错(所有赌注都是无论如何都要静态地确保纯度和类型安全)。出于政治上的原因,把它放在这里也很方便。它助长了 Haskell 是纯粹的神话,如果不是因为所有肮脏的“设计糟糕”的 C,或“设计糟糕”的操作系统,或“设计糟糕”的硬件或...... unsafePerformIO 经常与 FFI 相关代码一起使用,但其原因通常更多地与 FFI 以及 Haskell 本身的糟糕设计有关,

因此,正如 Norman Ramsey 所说,官方的立场是使用 unsafePerformIO 是可以的,只要使用它的人都满足了某些证明义务(主要是这样做不会使重要的编译器转换无效,如内联和公共子表达式消除) . 到目前为止一切都很好,或者人们可能会这么认为。真正的问题是,unsafePerformIO 可能是最常见的单一用例无法满足这些证明义务,据我估计,这种情况占野外所有 unsafePerformIO 的 50% 以上。我说的是被称为“unsafePerformIO hack”的骇人听闻的成语,可证明(事实上显然)完全不安全(在存在内联和 cse 的情况下)。

我真的没有时间、空间或意愿去探讨“unsafePerformIO hack”是什么,或者为什么在真正的 IO 库中需要它,但最重要的是,从事 Haskells IO 基础设施工作的人通常“陷入岩石和坚硬的地方”。他们可以提供一个没有安全实现的固有安全 API(在 Haskell 中),或者他们可以提供一个可以安全实现的固有不安全 API,但他们很少能在 API 设计执行。从“unsafePerformIO hack”出现在现实世界代码(包括 Haskell 标准库)中的令人沮丧的规律来看,似乎大多数人选择前一个选项作为两个弊端中较小的一个,只是希望编译器不会搞砸使用内联、cse 或任何其他转换来解决问题。

我真希望这一切都不是这样。不幸的是,它是。

于 2010-06-27T16:49:43.940 回答
1

Safe Haskell 是 GHC 的最新扩展,对这个问题给出了新的答案。unsafePerformIO是 GHC Haskell 的一部分,但不是安全方言的一部分。

unsafePerformIO应该只用于构建引用透明的函数;例如,记忆。在这些情况下,包的作者将其标记为“可信赖”。一个安全的模块只能导入安全可靠的模块;它不能导入不安全的模块。

更多信息:GHC 手册Safe Haskell 论文

于 2012-07-24T12:11:24.073 回答
0

Haskell 通常被称为纯函数式语言的示例。鉴于 System.IO.Unsafe.unsafePerformIO 的存在,如何证明这是合理的?

编辑:我认为“纯功能”意味着不可能将不纯的代码引入程序的功能部分。

IO monad 实际上是在 Haskell 中定义的,您实际上可以在这里看到它的定义。这个 monad 的存在不是为了处理杂质,而是为了处理副作用。在任何情况下,您实际上都可以从IOmonad 中进行模式匹配,因此 的存在不unsafePerformIO应该对您造成困扰。

于 2018-04-13T23:32:20.640 回答