在 Haskell 中,如何区分单子函数定义中的可变引用与常规变量
In Haskell, how to distinguish mutable references vs regular variables in monadic functions definitions
假设您想在 Haskell 中编写一些有状态函数。
你必须使用这样的 monadic 样式:(使用任何状态 monad)
f :: x -> k -> (ST s) r
所以这意味着本质上函数需要一些输入 x
和 k
,可能会使用 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
。
假设您想在 Haskell 中编写一些有状态函数。 你必须使用这样的 monadic 样式:(使用任何状态 monad)
f :: x -> k -> (ST s) r
所以这意味着本质上函数需要一些输入 x
和 k
,可能会使用 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
。