对 IORefs 进行混淆以制作计数器

发布于 2024-10-08 02:55:36 字数 545 浏览 9 评论 0原文

我找到了一些示例代码,并对其进行了一些更改,

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

∞琼窗梦回ˉ 2024-10-15 02:55:36

在你的第二个版本中 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 access newNode.

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.

旧伤还要旧人安 2024-10-15 02:55:36

澄清一下:对于像您正在构建的应用程序一样,使用 unsafePerformIO 创建 IORef 是很正常的,并且允许 Haskell 最接近程序中的全局变量语言。在你的行动中使用它可能是不明智的。例如,newNode 可能最好写为:

newNode = do i <- readIORef counter
             writeIORef counter (i+1)
             return i

然后您打算调用 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:

newNode = do i <- readIORef counter
             writeIORef counter (i+1)
             return i

And then any place you intend to call newNode should be an IO action itself.

One other little detail: counter will have the type IORef Integer by default unless you change this using default. Don't try to give it a generic type like Num a => IORef a: that way danger lies.

奢欲 2024-10-15 02:55:36

您不应该在正常编程中使用此类构造,因为编译器可能会应用各种优化来消除所需的效果或使其变得不可预测。如果可能,请改用 State monad 之类的东西。它更干净,并且始终如您所愿。给你:

-- This function lets you escape from the monad, it wraps the computation into a monad to make it, well, countable
counter :: (State Int x) -> x
counter = flip evalState 0

-- Increments the counter and returns the new counter
newNode :: State Int Int
newNode = modify (+1) >>= get

请看看 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:

-- This function lets you escape from the monad, it wraps the computation into a monad to make it, well, countable
counter :: (State Int x) -> x
counter = flip evalState 0

-- Increments the counter and returns the new counter
newNode :: State Int Int
newNode = modify (+1) >>= get

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文