对 IORefs 进行混淆以制作计数器
我找到了一些示例代码,并对其进行了一些更改,
counter = unsafePerform $ newIORef 0
newNode _ = unsafePerformIO $
do
i <- readIORef counter
writeIORef counter (i+1)
return i
每次运行时都会返回 1 然后 2 然后 3 然后 3 等。
但是当我将其更改为
newNode = unsafePerformIO $
do
i <- readIORef counter
writeIORef counter (i+1)
return i
然后每次运行时我都会得到 0 。
为什么会发生这种情况?我可以采取什么措施来解决这个问题?
I found some sample code, and changed it a little
counter = unsafePerform $ newIORef 0
newNode _ = unsafePerformIO $
do
i <- readIORef counter
writeIORef counter (i+1)
return i
Which returns 1 then 2 then 3 then 3 etc each time it's run.
But when I change it to
newNode = unsafePerformIO $
do
i <- readIORef counter
writeIORef counter (i+1)
return i
then I get 0 every time I run it.
Why is this happening, and what can I do to fix it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在你的第二个版本中
newNode
是一个简单的值,而不是一个函数。因此,haskell 只会对它求值一次,然后每当您访问 newNode 时都会给出该求值的结果。警告:在您知道引用透明的 IO 操作以外的任何操作上使用
unsafePerformIO
都是危险的。它可能会与某些优化产生不良交互,并且通常不会像您期望的那样运行。它的名字中含有“不安全”一词是有原因的。作为使用
unsafePerformIO
的一种方式,您的代码很好,但如果您想在实际代码中使用类似的东西,我强烈建议您重新考虑。In your second version
newNode
is a simple value, not a function. So haskell evaluates it exactly once and then gives you the result of that evaluation whenever you accessnewNode
.A word of warning: Using
unsafePerformIO
on anything other than an IO action which you know to be referentially transparent is dangerous. It might interact badly with some optimizations and just generally not behave like you expect. There's a reason it's got the word "unsafe" in its name.As a way to play around with
unsafePerformIO
your code is fine, but if you ever want to use something like it in real code, I'd strongly encourage you to reconsider.澄清一下:对于像您正在构建的应用程序一样,使用
unsafePerformIO
创建 IORef 是很正常的,并且允许 Haskell 最接近程序中的全局变量语言。在你的行动中使用它可能是不明智的。例如,newNode 可能最好写为:然后您打算调用 newNode 的任何地方本身都应该是一个 IO 操作。
另一个小细节:默认情况下,
counter
的类型为IORef Integer
,除非您使用default
更改此类型。不要尝试给它一个通用类型,如Num a =>; IORef a
:这样就有危险了。Just as a clarification: for an application like the one you seem to be building, creating an IORef using
unsafePerformIO
is quite normal, and allows Haskell's closest approximation to global variables in procedural languages. What is probably not wise is using it in your actions. For example, newNode is probably best written as:And then any place you intend to call
newNode
should be an IO action itself.One other little detail:
counter
will have the typeIORef Integer
by default unless you change this usingdefault
. Don't try to give it a generic type likeNum a => IORef a
: that way danger lies.您不应该在正常编程中使用此类构造,因为编译器可能会应用各种优化来消除所需的效果或使其变得不可预测。如果可能,请改用
State
monad 之类的东西。它更干净,并且始终如您所愿。给你:请看看 sepp2k 为你的问题的答案感到难过。如果您有一些全局可用的东西(例如配置),不太可能更改,并且必须几乎可以从任何地方获得,那么您所解释的内容特别有用。明智地使用它,因为它违反了纯度的基本原则。如果可能的话,请使用 monad(就像我解释的那样)。
Yous shouldn't use such constructs in a normal programming, as the compiler may apply various optimizations that kill the desired effect or make it unpredictable. If possible, use something like the
State
monad instead. It's cleaner and will always behave like you want. Here you go:Please see what sepp2k sad for the answer to your question. What you explain is particularly useful, if you have something global available (like configs), unlikely to change, which must be available from nearly everywhere. Use it wisely, as it is against the basic principle of purity. If it's possible, use monads instead (like I explained).