变压器组中 ST 类型的问题

Trouble with ST's type in a transformer stack

我在处理 ST 类型中的 forall 量词时遇到了一些问题。以下(简化的)代码示例工作正常,产生了预期的结果。

import Control.Monad
import Control.Monad.ST

return' :: a -> ST s a
return' = return

function :: a -> a
function x = runST $ do
               -- Some complicated expression eventually producing a result
               return' x

这很好,如果我只想拥有 ST。但是在我的计算中,我想要一个可能也会失败的状态转换器计算,所以我尝试将 ExceptT 添加到堆栈中。

import Control.Monad
import Control.Monad.ST
import Control.Monad.Except

return' :: a -> ExceptT String (ST s) a
return' = return

function :: a -> Either String a
function x = runST . runExceptT $ do
               -- Some complicated expression that might fail
               return' x

不幸的是,我收到一条相当奇怪的错误消息。

example.hs:9:14:
    Couldn't match type `ST s0 (Either String a)'
                   with `forall s. ST s (Either String a)'
    Expected type: ST s0 (Either String a) -> Either String a
      Actual type: (forall s. ST s (Either String a))
                   -> Either String a
    Relevant bindings include
      x :: a (bound at example.hs:9:10)
      function :: a -> Either String a (bound at example.hs:9:1)
    In the first argument of `(.)', namely `runST'
    In the expression: runST . runExceptT

我对 ST 使用的 rank-2 类型只有一个模糊的理解,所以我不太确定如何处理这个错误。我怎样才能安全地将 ST 放在 monad 转换器堆栈的底部而不会出现 forall 的问题?

(对于那些好奇的人,我使用 ST 因为我希望我的计算使用可变数组然后在最后冻结它。这不应该与这个问题相关,但这是为什么我不能简单地使用 State 来代替)

只需要将runST后面的.换成$,就可以用普通的return代替return'了嗯。

import Control.Monad
import Control.Monad.ST
import Control.Monad.Except

function :: a -> Either String a
function x = runST $ runExceptT $ do
               return x

main = return ()

(至少对我来说这个编译没有问题。)

有关错误背后的理论,请参阅 this answer