在Haskell的函数中使用全局变量但不作为参考

时间:2014-12-02 20:57:16

标签: haskell

是否可以在Haskell中重写此源以使用函数增加而不使用引用 x 作为参数?

increase 0 _ = return ()
increase n x = do
    modifySTRef x (+n)
    increase (n - 1) x

calculation n = runST $ do
    x <- newSTRef 0
    increase n x
    readSTRef x

2 个答案:

答案 0 :(得分:4)

没有[0]方法在源文件的顶层实现你喜欢的东西:根本不能保证像这样的全局变量的“执行”顺序。事实上,根本没有执行(因此先前的恐慌报价)。您必须ST个动作组合在一起执行runST下的执行,以使其中任何一个都发生。

这意味着不纯的绑定 - 就像你第一次声明全局变量的名字一样 - 必须在你的runST块中完成,因此它们生活在模块中顶层绑定之下的级别

但是除了它们被导出之外,这些顶级绑定没有什么特别之处。您可以在runST内建立一个新的“顶级”:

stThread :: ST s Int
stThread = do
  x <- newSTRef

  let increase :: Int -> ST s ()
      increase 0 = return ()
      increase n = do
        modifySTRef x (+n)
        increase (n - 1) x

  increase 10
  increase 20

  readSTRef x

calculation :: Int
calculation = runST stThread

[0]嗯,没有犹太人的方式不需要破坏Haskell语义。有时候人们仍在欺骗,但我不能以任何善意推荐它。

答案 1 :(得分:0)

您不能拥有顶级ST引用。这会破坏参考透明度。考虑一下:

x :: STRef s Int
x = magicallyCreateSTRef 0

test1 :: (Int, Int)
test1 = ( runST (modifySTRef x (+1) >> readSTRef x)
        , runST (modifySTRef x (+1) >> readSTRef x) )

test2 :: (Int, Int)
test2 = (y, y)
   where y = runST (modifySTRef x (+1) >> readSTRef x)

现在test1的价值是多少?根据评估顺序,它可以是(0,1)(1,0)。这已经是Haskell被打破的一个非常糟糕的迹象。为了更清楚地说明问题,test2应该明确等同于test1,这要归功于引用透明度。但是,test2无法生成内部具有不同值的对。

因此,您无法拥有顶级STRef。最多可以有顶级IO引用,例如

x :: IORef Int
x = unsafePerformIO (newIORef 0)

只要上面的引用是单态的(IORef IntIORef [Int]都可以,IORef [a]不是),这不会破坏Haskell的主要功能,即类型安全和参考透明度。它仍然被某种方式视为一种小黑客,因为它使用了禁用函数unsafePerformIO,但至少这是一个众所周知的安全用途。

为什么允许IO引用而ST引用不是?因为ST monad具有转义函数runST,因此可以在纯代码中使用monadic值(例如在test1test2中)。相反,IO没有类似的runIO转义函数。