这个问题显然与讨论过的问题here和here有关。不幸的是,我的要求与这些问题略有不同,所给出的答案并不适用于我。我也不太明白为什么 runST
无法在这些情况下键入检查,这无济于事。
我的问题是,我有一段代码使用一个monad堆栈,或者更确切地说是一个monad:
import Control.Monad.Except
type KErr a = Except KindError a
另一部分代码需要与此集成,并将其包含在STT monad:
中type RunM s a = STT s (Except KindError) a
在这些部分之间的界面,我显然需要包裹和展开外层。我有以下功能可以在KErr
- >中使用RunM
方向:
kerrToRun :: KErr a -> RunM s a
kerrToRun e = either throwError return $ runExcept e
但出于某种原因,我无法反复进行类型检查:
runToKErr :: RunM s a -> KErr a
runToKErr r = runST r
我正在假设由于RunM
的内部monad与KErr
具有相同的结构,我可以在我打开STT
图层后返回它,但我似乎无法做到这一点,因为runST
抱怨它的类型参数:
src/KindLang/Runtime/Eval.hs:18:21:
Couldn't match type ‘s’ with ‘s1’
‘s’ is a rigid type variable bound by
the type signature for runToKErr :: RunM s a -> KErr a
at src/KindLang/Runtime/Eval.hs:17:14
‘s1’ is a rigid type variable bound by
a type expected by the context:
STT s1 (ExceptT KindError Data.Functor.Identity.Identity) a
at src/KindLang/Runtime/Eval.hs:18:15
Expected type: STT
s1 (ExceptT KindError Data.Functor.Identity.Identity) a
Actual type: RunM s a
我也试过了:
runToKErr r = either throwError return $ runExcept $ runST r
为了更强烈地将runST
与其预期的返回类型隔离,如果是问题的原因,但结果是相同的。
此s1
类型来自何处,以及如何说服ghc与s
的类型相同?
答案 0 :(得分:5)
(下面讲的是ST s a
,但与STT s m a
的内容相同;我只是避免了下面谈论变压器版本的不必要的复杂情况。
您看到的问题是,runST
具有类型(forall s. ST s a) -> a
,以隔离来自外部纯净世界的任何潜在(STRef
- 更改)计算效果。所有s
计算,ST
s等标记的STRef
幻像类型的整个点是跟踪它们属于哪个“ST
- 域” ;并且runST
的类型确保域之间无法传递任何内容。
您可以通过强制执行相同的不变量来编写runToKErr
:
{-# language Rank2Types #-}
runToKErr :: (forall s. RunM s a) -> KErr a
runToKErr = runST
(当然,你可能会意识到这个限制对你希望写的程序来说太强了;那时你需要失去希望,对不起,我的意思是你需要重新设计你的程序。)
至于错误消息,您无法“说服类型检查器s1
和s
属于同一类型”的原因是因为如果我通过了ST s a
给定的s
和a
选项与给您一些允许您选择自己的s
的内容不同。 GHC选择s1
(Skolemized变量)作为s
,因此尝试将ST s a
与ST s1 a
统一起来