【问题标题】:Is Haskell really a purely functional language considering unsafePerformIO?考虑到 unsafePerformIO,Haskell 真的是一种纯粹的函数式语言吗?
【发布时间】:2011-03-08 15:57:02
【问题描述】:

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

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

【问题讨论】:

标签: haskell functional-programming type-systems referential-transparency unsafe-perform-io


【解决方案1】:

我们称之为 Haskell 的语言

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

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

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

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

【讨论】:

  • 我理解正确吗,人们应该更准确地说“Haskell 98 是一种纯函数式语言”而不是“Haskell 是一种纯函数式语言”?
  • @Stefan Schmidt:更像是“Haskell is 纯粹的功能性”,但“GHC 是 Haskell 超集的实现,其中包括非纯粹的功能”
  • 感谢您提供有趣的图形!
  • 我觉得我更喜欢这个答案
  • 在 Haskell 2010 方面,unsafePerformIO 出现在规范的什么地方? Haskell 还是 FFI?
【解决方案2】:

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

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

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

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

【讨论】:

  • haskell.org/ghc/docs/6.12.2/html/libraries 你理解的“Haskell 的一部分”是什么?
  • @StefanSchmidt:语言标准中已经定义的东西。仅仅因为某些东西是 ghc 的一部分,它就不再是 haskell 的一部分,就像 gcc 中的任何东西都是 C 语言的一部分一样。
  • @Stefan:对于这样的问题,“Haskell”是指 Haskell 2010 标准,或者可能是剑桥大学发布的 Haskell 98 标准2003 年出版。在其他情况下,许多程序员使用“Haskell”这个词来描述 GHC 在那一周碰巧实现的任何东西。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

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

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

    我知道你必须有一个稍微弱一点的纯定义才能成立。即纯函数在引用上是透明的并且永远不会被调用因为副作用(如打印)。

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

      • 不幸的是?我会说“幸运” :) 我们不再“不惜一切代价避免成功”。
      【解决方案6】:

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

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

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

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

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

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

      【讨论】:

      • 我想对你的答案表示赞同,但我认为深入了解“unsafePerformIO hack”是什么,或者为什么在真正的 IO 库中需要它是让你的答案有帮助的原因。你的大部分回答只是挥手。
      • 我猜“unsafePerformIO hack”与haskell.org/haskellwiki/Top_level_mutable_state中描述的相同
      【解决方案7】:

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

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

      欲了解更多信息:GHC manualSafe Haskell paper

      【讨论】:

        【解决方案8】:

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

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

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

        【讨论】:

          猜你喜欢
          • 2011-01-17
          • 2020-05-12
          • 1970-01-01
          • 2011-08-14
          • 1970-01-01
          • 1970-01-01
          • 2012-11-04
          • 1970-01-01
          • 2014-02-26
          相关资源
          最近更新 更多