我在处理forall
类型中的ST
量词时遇到了一些问题。以下(简化)代码示例工作正常,产生预期结果。
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
)
答案 0 :(得分:4)
您需要做的就是将.
替换为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了解错误背后的理论。