对IORefs制造反击的困惑

Squ*_*dly 4 monads haskell unsafe-perform-io ioref

我找到了一些示例代码,并稍微改了一下

counter = unsafePerform $ newIORef 0

newNode _ = unsafePerformIO $
              do
                i <- readIORef counter
                writeIORef counter (i+1)
                return i
Run Code Online (Sandbox Code Playgroud)

每次运行时返回1然后2然后3然后3等.

但是当我改变它

newNode = unsafePerformIO $
              do
                i <- readIORef counter
                writeIORef counter (i+1)
                return i
Run Code Online (Sandbox Code Playgroud)

然后我每次运行都得0.

为什么会发生这种情况,我该怎么做才能解决这个问题?

sep*_*p2k 10

在你的第二个版本newNode是一个简单的值,而不是一个函数.因此,haskell只对其进行一次评估,然后在您访问时为您提供评估结果newNode.

警告:使用unsafePerformIO除了IO操作之外的任何其他任何您认为是引用透明的操作都是危险的.它可能与某些优化相互影响很严重,而且通常不像您期望的那样.有一个原因是它的名字中有"不安全"这个词.

作为一种使用unsafePerformIO代码的方法很好,但如果您想在实际代码中使用类似的东西,我强烈建议您重新考虑.

  • @MrBones:因为这种行为几乎永远不会是人们想要的.如果我写'x = veryExpensiveFunction foobar`然后再写'y = x*x + x`,我想要`veryExpensiveFunction foobar`来评估一次,而不是三次.而这正是Haskell所做的(除非`x`的类型是多态的).在这种情况下,你希望它表现得像一个nullary函数的唯一原因是评估表达式会产生副作用,即使没有不安全的操作也不会发生这种情况. (6认同)
  • 独立于一个值是否可以被认为是一个0参数函数(IMO,松散它可以 - 就像'x - > y - > z'可以被认为是二元函数的类型),纯度意味着这样函数必须始终返回相同的值.它总是使用相同的参数集(无)调用,因此必须始终返回相同的值,或者根据定义它不是纯粹的,并使用'unsafePerformIO'断言它是不正确的. (3认同)
  • 目前(GHC 7.6.3)`newNode()`肯定会与优化交互不良:当用`-O`编译时它只运行一次!例如,`newNode()+ newNode()`的计算结果为'0`. (2认同)