考虑到unsafePerformIO,Haskell真的是一种纯粹的功能语言吗?

时间:2010-06-26 16:25:12

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

Haskell通常被称为纯函数式语言的示例。鉴于System.IO.Unsafe.unsafePerformIO

的存在,如何才能证明这是正当的

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

8 个答案:

答案 0 :(得分:115)

我们称之为Haskell的语言

unsafePerformIO外部函数接口规范的一部分,而不是核心Haskell 98规范。它可用于执行不会逃避某些范围的局部副作用,以暴露纯粹的功能接口。也就是说,当类型检查器无法为我们执行时,我们使用它来隐藏效果(与ST monad不同,它隐藏了静态保证的效果)。

为了准确说明我们称之为“Haskell”的多种语言,请考虑下面的图像。每个环对应于一组特定的计算特征,按安全顺序排列,并且区域与表达能力相关(即,如果您具有该特征,则可以编写的程序数)。

称为Haskell 98的语言在中间指定,允许全部和部分函数。 Agda(或Epigram),只允许使用全部功能,甚至不那么具有表现力,但“更纯粹”,更安全。虽然我们今天使用的Haskell包含FFI的所有内容,其中unsafePerformIO存在。也就是说,您可以在现代Haskell中编写任何,但如果您使用来自外环的东西,那么建立安全和安全保证将更难以通过内环变得简单。

alt text

因此,Haskell程序通常不是通过100%引用透明的代码构建的,但是,它是唯一适用于普通语言的中等语言

答案 1 :(得分:34)

  

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

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

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

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

答案 2 :(得分:4)

语言/实现纯粹是功能性的。它包括一对“逃生舱”,如果你不愿意,你不必使用它。

答案 3 :(得分:2)

我不认为unsafePerformIO意味着haskell不知何故变得不纯洁。您可以从不纯的函数创建纯(引用透明)函数。

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

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

我知道你必须要有一个稍微弱一点的纯粹定义才能坚持下去。即纯函数是引用透明的,从不称为,因为的副作用(如打印)。

答案 4 :(得分:1)

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

好处是你可以(而且应该)将这种“不合时宜”代码的使用限制在程序中几乎没有特定记录的部分。

答案 5 :(得分:1)

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

虽然确实将unsafePerformIO作为FFI附录的一部分正式添加到语言中,但其原因主要是历史性而非逻辑性。它非正式存在,并且在Haskell有FFI之前很久就被广泛使用。它从未正式成为Haskell主要标准的一部分,因为正如您所观察到的那样,这太过于尴尬。我想希望是它会在某种程度上以某种方式消失。嗯,这没有发生,在我看来也不会。

FFI附录的开发为unsafePerformIO提供了一个方便的借口,因为在添加调用外部(IE C)代码的能力方面,它可能在这里看起来似乎并不太糟糕。关于静态确保纯度和类型安全的所有投注都是关闭的)。把它放在这里基本上是出于政治原因也很方便。它培养了Haskell纯粹的神话,只要它不是那些肮脏的“设计糟糕”的C,或“设计糟糕”的操作系统,或“设计糟糕”的硬件或者......无论如何......这当然是真的。 unsafePerformIO经常与FFI相关的代码一起使用,但其原因通常更多地与FFI的设计错误以及Haskell本身的设计有关,而Haskell正在尝试接口的任何外来事物的设计都不错。

正如诺曼·拉姆齐所说,官方的立场是,使用unsafePerformIO是可以的,前提是任何使用它的人都会满足某些证明义务(主要是这样做不会使重要的编译器转换无效,如内联和公共子表达消除)。到目前为止,人们可能会想到这么好。真正的问题是,这些证据义务无法满足可能是unsafePerformIO最常见的一个使用案例,据我估计,这个案例占据了野生环境中所有不安全的形象的50%以上。 。我在谈论被称为“unsafePerformIO hack”的令人震惊的成语,可证明(事实上显然)完全不安全(在内联和cse存在的情况下)。

我真的没有时间,空间或倾向于进入“unsafePerformIO hack”是什么或为什么在真正的IO库中需要它,但最重要的是,在Haskells IO基础设施上工作的人通常是“卡在岩石和坚硬的地方之间“。它们可以提供一个本质上安全的API,它没有安全的实现(在Haskell中),或者它们可以提供一个本质上不安全的API,可以安全地实现,但它们很少能做到的是在API设计和< / strong>实施。从现实世界代码(包括Haskell标准库)中出现的“unsafePerformIO hack”令人沮丧的规律来看,似乎大多数选择前两个选项作为两个邪恶中较小的一个,并且只希望编译器不会破坏内联,cse或任何其他转变。

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

答案 6 :(得分:1)

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

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

有关详情,请访问:GHC manualSafe Haskell paper

答案 7 :(得分:0)

  

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

     

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

IO monad实际上是在Haskell中定义的,实际上你可以看到它的定义here。这种单子不存在处理杂质而是处理副作用。在任何情况下,您实际上可以模仿匹配IO monad的方式,因此unsafePerformIO的存在不应该让您感到困扰。