在Haskell中,如何在单子函数定义中区分可变/不可变变量

问题描述 投票:1回答:1

假设您想在Haskell中编写一些有状态的函数。您必须使用这样的单子风格:(使用任何状态的单子)

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

因此,这实际上意味着该函数需要一些输入xk,可能会使用和/或修改世界以计算返回值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中保持不变?

haskell immutability state-monad
1个回答
0
投票

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的字符串。

© www.soinside.com 2019 - 2024. All rights reserved.