在 Haskell 中,如何区分单子函数定义中的可变引用与常规变量

In Haskell, how to distinguish mutable references vs regular variables in monadic functions definitions

假设您想在 Haskell 中编写一些有状态函数。 你必须使用这样的 monadic 样式:(使用任何状态 monad)

f :: x -> k -> (ST s) r

所以这意味着本质上函数需要一些输入 xk,可能会使用 and/or 修改世界来计算 return 值 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

我的意思是,如果给我一个未知类型k的变量i,我不知道它是否依赖于s以及它的值是否可能会受到调用 f 的影响。 写纯函数当然不存在这个问题,因为一切都是不可变的。

有没有一种方法可以方便地注释,更重要的是,静态强制调用 f 时,k 将在 ST monad 中保持不变?

ST 中,您可以明确地分辨出什么是可变的:Int 始终是不可变整数,而 STRef s Int 是对可变 [= 的(不可变)引用14=].

因此,

f :: STRef s Int -> String -> (ST s) Bool

可以(读取和)修改指向第一个参数的 Int,但只能读取作为第二个参数传递的不可变字符串。

最重要的是,f 可能会创建(并改变)新的 STRef s 对新分配值的引用。如果 f 是使用对此类值的引用定义的,它还可以修改其他值。例如。在

bar :: forall s . ST s ()
bar = do
   x_ref <- newSTRef "hello"
   let f :: STRef s String -> String -> ST s ()
       f y_ref str = do
         y <- readSTRef y_ref
         writeSTRef x_ref y
         writeSTRef y_ref (y ++ " change " ++ str)
   ...

调用 f 将更改最初设置为 "hello" 的字符串和其引用传递给 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 不是引用,它仍然具有相同的值。如果它是引用,则引用的值可能已更改为 f a i