针对unsafePerformIO的部门限制

时间:2010-10-08 18:17:00

标签: haskell coding-style monads unsafe-perform-io

在工作中有一些关于将其作为禁止使用unsafePerformIO及其同类的部门范围政策的讨论。就个人而言,我并不介意,因为我一直认为,如果我发现自己想要使用它,通常意味着我需要重新考虑我的方法。

这种限制听起来合理吗?我似乎记得在某个地方读到它主要是为了FFI,但是我不记得我现在在哪里读到它。

编辑: 好的,那是我的错。它不会受到合理需要的限制,即。 FFI。政策的重点更多是为了阻止懒惰和代码气味。

5 个答案:

答案 0 :(得分:13)

许多核心库如ByteString使用unsafePerformIO,例如自定义内存分配。

当您使用这样的库时,您相信库作者已证明其导出的API的引用透明性,并且记录了用户的任何必要前提条件。您的部门应该制定政策和审核流程,以便在内部进行类似的保证,而不是全面禁止。

答案 1 :(得分:12)

嗯, unsafePerformIO的有效用途。它不仅仅是装饰性的,也不是测试你的美德的诱惑。但是,这些用途都不涉及为日常代码添加有意义的副作用。以下是一些可以潜在被证明合理的用途示例,并有不同程度的怀疑:

  • 包装内部不纯的函数,但没有外部可观察到的副作用。这与ST monad的基本思想相同,不同之处在于程序员负担显示杂质不会“泄漏”。

  • 以某种有限的方式伪装一种故意不纯的功能。例如,只写杂质看起来与“从内部”的总纯度相同,因为没有办法观察产生的输出。这对于某些类型的日志记录或调试非常有用,您明确需要IO monad所需的一致性和明确定义的顺序。一个例子是Debug.Trace.trace,我有时将其称为unsafePerformPrintfDebugging

  • 对纯计算的反思,产生纯粹的结果。一个典型的例子就像unambiguous choice operator,可以并行运行两个等效的纯函数,以便更快地得到答案。

  • 内部无法观察到参照透明度的破坏,例如在初始化数据时引入非确定性。只要每个不纯函数只被评估一次,在程序的任何单次运行期间都会有效地保留引用透明度,即使使用相同参数调用的相同的仿纯函数在不同的运行中给出不同的结果。

    < / LI>

关于上述所有内容的重要注意事项是所产生的杂质受到严格控制并且范围有限。鉴于控制副作用的细粒度系统比全能IO monad,这些都是切割半纯度的明显候选者,就像前面提到的受控可变状态{{ 1}} monad。


Post scriptum:如果正在考虑针对任何非必要的ST使用的强硬立场,我强烈鼓励 将禁令扩大到包括unsafePerformIO以及允许观察其行为的任何功能。如果你问我,它至少和我上面列出的一些unsafeInterleaveIO例子一样粗略。

答案 2 :(得分:7)

unsafePerformIO是IO monad的runST。它有时是必不可少的。但是,与runST不同,编译器无法检查您是否保留了参照透明度。

因此,如果您使用它,程序员有责任解释为什么使用是安全的。它不应该被禁止,它应该伴随着证据。

答案 3 :(得分:4)

在“应用程序”代码中取消unsafePerformIO是一个很好的主意。在我看来,unsafePerformIO在正常代码中没有任何借口,根据我的经验,它不是必需的。它实际上不是语言的一部分,所以如果你使用它,你就不再在Haskell中编程了。你怎么知道它甚至意味着什么?

另一方面,如果你知道自己在做什么,在FFI绑定中使用unsafePerformIO是合理的。

答案 4 :(得分:3)

将notafePerformIO取消是一个糟糕的想法,因为它有效地将代码锁定到IO monad中:例如,ac库绑定几乎总是在IO monad中 - 但是,使用unsafePerformIO可以构建更高级别的纯函数库顶部。

可以说,unsafePerformIO反映了个人计算机的高度有状态模型与纯粹的无状态haskell模型之间的妥协;从计算机的角度来看,即使函数调用也是有状态的,因为它需要将参数推送到堆栈,弄乱寄存器等,但是这些操作基于这些操作实际上在功能上构成的知识。