是否可以在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
答案 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 Int
和IORef [Int]
都可以,IORef [a]
不是),这不会破坏Haskell的主要功能,即类型安全和参考透明度。它仍然被某种方式视为一种小黑客,因为它使用了禁用函数unsafePerformIO
,但至少这是一个众所周知的安全用途。
为什么允许IO引用而ST引用不是?因为ST
monad具有转义函数runST
,因此可以在纯代码中使用monadic值(例如在test1
和test2
中)。相反,IO
没有类似的runIO
转义函数。