Haskell 中 `do` 之外的 ST Monad 用法?

ST Monad usage outside of `do` in Haskell?

我在 do 符号的上下文中看到了很多 ST monad 的例子;然而,由于这不适合我的目的,我尝试在 do 线程之外使用 ST monad 例如 newSTRefmodifySTRef

类型由VSCode的HLS自动添加。

ref :: ST s (STRef s Integer)
ref = newSTRef (1)

x :: ST s ()
x = modifySTRef ref 2

错误:最后一行ref

Couldn't match expected type ‘STRef s a0’
            with actual type ‘ST s0 (STRef s0 Integer)’

好吧,我当然可以看到类型不匹配的错误,但我不知道如何修复它。

ST Monad 在 do 之外的正确用法是什么?

编辑:

没有do我的目的是

  1. 就像上面提到的那样,do 只是一种语法糖,通常我想避免它以便以更直接的功能方式编写代码。

  2. 获取可变对象的目的是为了开发FRP,单do线程中ref的顺序定义和更新没有用处。

这里的问题是 modifySTRef 需要一个 STRef s a,而不是 ST s (STRef s a)ST 旨在利用 monad 属性来保护可变操作不被滥用,因此所有修改只能在 ST 上下文中发生。因此,您需要使用上下文的力量来提取纯 STRef 引用。

通常你会do这样:

x :: ST s ()
x = do
  refv <- ref
  modifySTRef refv 2

但是既然你想避免这种糖,你可以做正确的脱糖:

x :: ST s ()
x = refv >>= \refv -> modifySTRef refv 2

您可以阅读有关 do 表示法及其解析方式的更多信息 here


评论中的评论:请注意,您的代码 没有 这样的全局变量。 newSTRef 每次在 ST context 中调用时分配一块新的 RAM 。因此,此示例中的 x 实际上什么也没做(除了浪费一些内存和 GC 时间)。

如果您打算保留参考文献,则必须将其“拿在手中”,例如放在 ReaderT 包装纸中。