假设您想在Haskell中编写一些有状态的函数。您必须使用这样的单子风格:(使用任何状态的单子)
f :: x -> k -> (ST s) r
因此,这实际上意味着该函数需要一些输入x
和k
,可能会使用和/或修改世界以计算返回值r
。
假设x
是一个有状态的结构,可以通过f
进行修改。假设k
只是一种简单的键类型,例如用于访问x
中的内容。稍后将为k
本身分配一个简单的数字类型,但我们现在不必决定其类型。
所以从本质上讲,我知道x
是可变的,k
是不可变的。问题只是看f
的签名,我们不能说清楚,因此,如果f
出现在某些更复杂的单子代码的正文中,我们就无法很好地推断出这些变量。
示例:
g :: x -> k -> (ST s) r
g a i = do
...
f a i -- I don't know if i :: k depends on state
... --- I don't know if i was changed by f
我的意思是,如果给我一个未知类型为i
的变量k
,我不知道它是否取决于s
以及它的值是否会受到f
的调用的影响]。编写纯函数时当然不会出现此问题,因为一切都是不可变的。
是否有一种方便的注释方法,更重要的是,在调用k
时静态地强制f
在ST monad中保持不变?
在ST
内部,您绝对可以分辨出什么是可变的:Int
始终是不可变的整数,而STRef s Int
是对可变的Int
的(不可变的)引用。
因此,
f :: STRef s Int -> String -> (ST s) Bool
可以(读取和)修改指向第一个参数的Int
,但是只能读取作为第二个参数传递的不可变字符串。
最重要的是,f
可能会创建(并变异)对新分配值的新STRef s
引用。如果f
是使用对此类值的引用定义的,则它也可以修改其他值。例如。在
bar :: forall s . ST s ()
bar = do
ref <- newSTRef "hello"
let f :: STRef s String -> String -> ST s ()
f str_ref str2 = do
str <- readSTRef str_ref
writeSTRef ref str
writeSTRef str_ref (str ++ " change " ++ str2)
...
调用f
将同时更改最初设置为"hello"
的字符串和将其引用传递给f
的字符串。