在变压器堆栈中展开STT monad?

时间:2016-04-25 01:06:02

标签: haskell polymorphism higher-rank-types

这个问题显然与讨论过的问题herehere有关。不幸的是,我的要求与这些问题略有不同,所给出的答案并不适用于我。我也不太明白为什么 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的类型相同?

1 个答案:

答案 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

(当然,你可能会意识到这个限制对你希望写的程序来说太强了;那时你需要失去希望,对不起,我的意思是你需要重新设计你的程序。)

至于错误消息,您无法“说服类型检查器s1s属于同一类型”的原因是因为如果我通过了ST s a给定的sa选项与给您一些允许您选择自己的s的内容不同。 GHC选择s1Skolemized变量)作为s,因此尝试将ST s aST s1 a统一起来