针对 unsafePerformIO 的部门限制
工作中有人讨论将其制定为部门范围的政策,禁止使用 unsafePerformIO
及其同类产品。就我个人而言,我并不介意,因为我一直认为,如果我发现自己想要使用它,通常意味着我需要重新考虑我的方法。
这个限制听起来合理吗?我似乎记得在某处读到它主要是为 FFI 提供的,但我现在不记得在哪里读到过。
编辑: 好吧,那是我的错。它不会在合理需要的地方受到限制,即。 FFI。该政策的目的更多是为了阻止懒惰和代码异味。
There has been some talk at work about making it a department-wide policy of prohibiting the use of unsafePerformIO
and its ilk. Personally, I don't really mind as I've always maintained that if I found myself wanting to use it, it usually meant that I need to rethink my approach.
Does this restriction sound reasonable? I seem to remember reading somewhere that it was included mainly for FFI, but I can't remember where I read that at the moment.
edit:
Ok, that's my fault. It wouldn't be restricted where it's reasonably needed, ie. FFI. The point of the policy is more to discourage laziness and code smells.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
许多核心库(例如 ByteString)在底层使用了 unsafePerformIO,例如自定义内存分配。
当您使用这样的库时,您相信库作者已经证明了其导出的 API 的引用透明度,并且记录了用户的任何必要先决条件。您的部门应该制定政策和审查流程,以便在内部做出类似的保证,而不是全面禁止。
A lot of core libraries like
ByteString
useunsafePerformIO
under the hood, for example to customize memory allocation.When you use such a library, you're trusting that the library author has proven the referential transparency of their exported API, and that any necessary preconditions for the user are documented. Rather than a blanket ban, your department should establish a policy and a review process for making similar assurances internally.
好吧,有
unsafePerformIO
的有效用途。它不只是为了装饰,或者作为考验你美德的诱惑。然而,这些用途都不涉及向日常代码添加有意义的副作用。以下是一些可能可能合理的使用示例,但存在不同程度的怀疑:包装一个内部不纯的函数,但没有外部可观察到的副作用。这与
ST
monad 的基本思想相同,只是程序员有责任证明不纯性不会“泄漏”。以某种受限方式故意伪装不纯粹的函数。例如,只写杂质看起来与“从内部”的总纯度相同,因为无法观察所产生的输出。这对于某些类型的日志记录或调试很有用,在这些情况下,您明确不想要
IO
monad 所需的一致性和明确定义的顺序。一个例子是Debug.Trace.trace
,我有时将其称为unsafePerformPrintfDebugging
。对纯计算的内省,产生纯结果。一个典型的例子是明确的选择运算符,它可以并行运行两个等效的纯函数为了更快地得到答案。
内部不可观察地破坏引用透明度,例如在初始化数据时引入不确定性。只要每个不纯函数仅计算一次,在程序的任何单次运行期间都会有效地保留引用透明度,即使使用相同参数调用的同一伪纯函数在不同的运行中给出不同的结果。
关于上述所有内容,需要注意的重要一点是,所产生的杂质受到仔细控制并限制其范围。给定比通用 IO monad 更细粒度的控制副作用的系统,这些都将是切掉半纯度位的明显候选者,就像前面提到的
ST
monad。Post scriptum:如果正在考虑对任何非必需使用
unsafePerformIO
采取强硬立场,我强烈鼓励将禁止范围扩大到包括
unsafeInterleaveIO
以及任何允许观察其行为的函数。如果你问我的话,它至少和我上面列出的一些unsafePerformIO
示例一样粗略。Well, there are valid uses for
unsafePerformIO
. It's not there just to be decorative, or as a temptation to test your virtue. None of those uses, however, involve adding meaningful side effects to everyday code. Here's a few examples of uses that can potentially be justified, with various degrees of suspicion:Wrapping a function that's impure internally, but has no externally observable side effects. This is the same basic idea as the
ST
monad, except that here the burden is on the programmer to show that the impurity doesn't "leak".Disguising a function that's deliberately impure in some restricted way. For instance, write-only impurity looks the same as total purity "from the inside", since there's no way to observe the output that's produced. This can be useful for some kinds of logging or debugging, where you explicitly don't want the consistency and well-defined ordering required by the
IO
monad. An example of this isDebug.Trace.trace
, which I sometimes refer to asunsafePerformPrintfDebugging
.Introspection on pure computations, producing a pure result. A classic example is something like the unambiguous choice operator, which can run two equivalent pure functions in parallel in order to get an answer quicker.
Internally unobservable breaking of referential transparency, such as introducing nondeterminism when initializing data. As long as each impure function is evaluated only once, referential transparency will be effectively preserved during any single run of the program, even if the same faux-pure function called with the same arguments gives different results on different runs.
The important thing to note about all of the above is that the resulting impurity is carefully controlled and limited in scope. Given a more fine-grained system of controlling side-effects than the all-purpose
IO
monad, these would all be obvious candidates for slicing off bits of semi-purity, much like the controlled mutable state in the aforementionedST
monad.Post scriptum: If a hard-line stance against any non-required use of
unsafePerformIO
is being considered, I strongly encourageextending the prohibition to include
unsafeInterleaveIO
and any functions that allow observation of its behavior. It's at least as sketchy as some of theunsafePerformIO
examples I listed above, if you ask me.unsafePerformIO 是 IO monad 的 runST。有时这是必要的。但是,与 runST 不同,编译器无法检查您是否保留了引用透明度。
所以如果你使用它,程序员就有负担解释为什么使用是安全的。不应该禁止,应该有证据。
unsafePerformIO is the runST of the IO monad. It is sometimes essential. However, unlike runST, the compiler cannot check that you are preserving referential transparency.
So if you use it, the programmer has a burden to explain why the use is safe. It shouldn't be banned, it should be accompanied with evidence.
在“应用程序”代码中禁止
unsafePerformIO
是一个好主意。在我看来,在正常代码中没有理由使用unsafePerformIO
,并且根据我的经验,它是不需要的。它实际上不是该语言的一部分,因此如果您使用它,您就不再是真正的 Haskell 编程了。你怎么知道它到底意味着什么?另一方面,如果您知道自己在做什么,则在 FFI 绑定中使用
unsafePerformIO
是合理的。Outlawing
unsafePerformIO
in "application" code is an excellent idea. In my opinion there is no excuse forunsafePerformIO
in normal code and in my experience it is not needed. It is really not part of the language so you are not really programming in Haskell any more if you use it. How do you know what it even means?On the other hand, using
unsafePerformIO
in an FFI binding is reasonable if you know what you are doing.禁止 unsafePerformIO 是一个糟糕的主意,因为它有效地将代码锁定到 IO monad 中:例如,ac 库绑定几乎总是在 IO monad 中 - 然而,使用 unsafePerformIO 可以在其之上构建更高级别的纯功能库。
可以说,unsafePerformIO反映了个人计算机的高状态模型和haskell的纯粹无状态模型之间的折衷;从计算机的角度来看,即使函数调用也是有状态的,因为它需要将参数推入堆栈、弄乱寄存器等,但其用法是基于这些操作实际上在功能上组合的知识。
Outlawing unsafePerformIO is a terrible idea, because it effectively locks code into the IO monad: for example, a c library binding will almost always be in the IO monad - however, using unsafePerformIO a higher-level purely functional library can be built on top of it.
Arguably, unsafePerformIO reflects the compromise between the highly stateful model of the personal computer and the pure, stateless model of haskell; even a function call is a stateful from the computer's point of view since it requires pushing arguments onto a stack, messing with registers, etc., but the usage is based on the knowledge that these operations do in fact compose functionally.