避免常见使用 unsafePerformIO 的方法
我经常在 Haskell 代码中发现这种模式:
options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar
...
doSomething :: Foo -> Bar
doSomething = unsafePerformIO $ do
opt <- readMVar options
doSomething' where ...
基本上,一个人有一个选项记录或类似的东西,它最初是在程序开始时设置的。由于程序员很懒,他不想在整个程序中携带 options
记录。他定义了一个 MVar
来保存它 - 通过使用 unsafePerformIO
来定义。程序员确保状态仅在发生任何操作之前设置一次。现在程序的每个部分都必须再次使用unsafePerformIO
,只是为了提取选项。
在我看来,这样的变量被认为是务实纯粹的(别打败我)。是否有一个库可以抽象这个概念并确保该变量仅设置一次,即在初始化之前不进行任何调用,并且不必编写 unsafeFireZeMissilesAndMakeYourCodeUglyAnd
DisgustingBecauseOfThisLongFunctionName< /代码>
I often find this pattern in Haskell code:
options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar
...
doSomething :: Foo -> Bar
doSomething = unsafePerformIO $ do
opt <- readMVar options
doSomething' where ...
Basically, one has a record of options or something similar, that is initially set at the program's beginning. As the programmer is lazy, he doesn't want to carry the options
record all over the program. He defines an MVar
to keep it - defined by an ugly use of unsafePerformIO
. The programmer ensures, that the state is set only once and before any operation has taken place. Now each part of the program has to use unsafePerformIO
again, just to extract the options.
In my opinion, such a variable is considered pragmatically pure (don't beat me). Is there a library that abstracts this concept away and ensures that the variable is set only once, i.e. that no call is done before that initialization and that one doesn't have to write unsafeFireZeMissilesAndMakeYourCodeUglyAnd
DisgustingBecauseOfThisLongFunctionName
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这是一个坏主意。您发现的代码是错误的代码。*
无法安全地完全封装此模式,因为它不是安全模式。不要在您的代码中执行此操作。不要寻找安全的方法来执行此操作。没有安全的方法可以做到这一点。将
unsafePerformIO
慢慢地放在地板上,然后远离控制台...*人们使用顶级 MVar 有正当理由,但这些原因与与外部代码的绑定有关在大多数情况下,或者其他一些事情,替代方案非常混乱。然而,据我所知,在这些情况下,顶级 MVar 无法从
unsafePerformIO
后面访问。This is a bad idea. The code that you're finding this in is bad code.*
There's no way to fully wrap this pattern up safely, because it is not a safe pattern. Do not do this in your code. Do not look for a safe way to do this. There is not a safe way to do this. Put the
unsafePerformIO
down on the floor, slowly, and back away from the console...*There are legitimate reasons that people do use top level MVars, but those reasons have to do with bindings to foreign code for the most part, or a few other things where the alternative is very messy. In those instances, as far as I know, however, the top level MVars are not accessed from behind
unsafePerformIO
.如果您使用 MVar 来保存设置或类似的东西,为什么不尝试 reader monad?
(如果你不需要 IO,也可以使用常规 Reader :P)
If you are using MVar for holding settings or something similar, why don't you try reader monad?
(And regular Reader if you don't require IO :P)
使用隐式参数。与让每个函数在其类型中都具有
Reader
或ReaderT
相比,它们的重量稍微轻一些。您确实必须更改函数的类型签名,但我认为可以编写这样的更改脚本。 (这对于 Haskell IDE 来说是一个很好的功能。)Use implicit parameters. They're slightly less heavyweight than making every function have
Reader
orReaderT
in its type. You do have to change the type signatures of your functions, but I think such a change can be scripted. (Would make a nice feature for a Haskell IDE.)不使用这种模式有一个重要原因。据我所知,
Haskell 不保证
options
只会被评估一次。由于option
的结果是一个纯值,它可以被记忆和重用,但它也可以为每次调用重新计算(即内联),并且程序的含义不得改变(与您的相反)案件)。如果您仍然决定使用此模式,请务必添加
{-# NOINLINE options #-}
,否则它可能会被内联,并且您的程序将失败! (这样我们就摆脱了语言和类型系统给出的保证,而仅仅依赖于特定编译器的实现。)这个主题已经被广泛讨论,并且在 Haskell Wiki 上很好地总结了可能的解决方案顶级可变状态。目前,如果没有一些额外的编译器支持,就不可能安全地抽象此模式。
There is an important reason for not using this pattern. As far as I know, in
Haskell gives no guarantees that
options
will be evaluated only once. Since the result ofoption
is a pure value, it can be memoized and reused, but it can also be recomputed for every call (i.e. inlined) and the meaning of the program must not change (contrary to your case).If you still decide to use this pattern, be sure to add
{-# NOINLINE options #-}
, otherwise it might get inlined and your program will fail! (And by this we're getting out of the guarantees given by the language and the type system and relying solely on the implementation of a particular compiler.)This topic has been widely discussed and possible solutions are nicely summarized on Haskell Wiki in Top level mutable state. Currently it's not possible to safely abstract this pattern without some additional compiler support.
阅读不同的代码。
听起来确实是 reader monad 完成的事情,只不过 reader monad 以安全的方式完成了它。不要迁就自己的懒惰,而应该编写实际的好代码。
Read different code.
Sounds like literally exactly what the reader monad accomplishes, except that the reader monad does it in a safe way. Instead of accommodating your own laziness, just write actual good code.