在变压器堆栈中展开 STT monad?
Unwrapping the STT monad in a transformer stack?
这个问题显然与讨论的问题有关 and 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
具有相同的结构,我可以 return 打开它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
与其预期的 return 类型隔离开来,以防这是问题的原因,但结果是一样的。
这个 s1
类型来自哪里,我如何说服 ghc 它与 s
是同一类型?
(下面讨论 ST s a
但与 STT s m a
一样适用;我只是避免了下面讨论转换器版本的不必要的复杂化)
您看到的问题是 runST
具有类型 (forall s. ST s a) -> a
以将计算的任何潜在(STRef
变化)影响与外部纯净世界隔离开来。所有 ST
计算、STRef
s 等被标记的 s
幻影类型的全部要点是跟踪它们属于哪个“ST
-域” ; runST
的类型确保域之间不能传递任何内容。
您可以通过强制执行相同的不变量来编写 runToKErr
:
{-# language Rank2Types #-}
runToKErr :: (forall s. RunM s a) -> KErr a
runToKErr = runST
(当然,你可能会进一步意识到这个限制对于你希望编写的程序来说太强了;到那时你需要失去希望,抱歉,我的意思是你需要重新设计你的程序。)
至于错误信息,你不能"convince the type checker that s1
and s
are the same type"的原因是如果我给你一个ST s a
给定的选择s
和a
,这与给你一些让你自己选择的东西是不一样的 s
。 GHC 选择 s1
(一个 Skolemized 变量)作为 s
,因此试图统一 ST s a
和 ST s1 a
这个问题显然与讨论的问题有关 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
具有相同的结构,我可以 return 打开它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
与其预期的 return 类型隔离开来,以防这是问题的原因,但结果是一样的。
这个 s1
类型来自哪里,我如何说服 ghc 它与 s
是同一类型?
(下面讨论 ST s a
但与 STT s m a
一样适用;我只是避免了下面讨论转换器版本的不必要的复杂化)
您看到的问题是 runST
具有类型 (forall s. ST s a) -> a
以将计算的任何潜在(STRef
变化)影响与外部纯净世界隔离开来。所有 ST
计算、STRef
s 等被标记的 s
幻影类型的全部要点是跟踪它们属于哪个“ST
-域” ; runST
的类型确保域之间不能传递任何内容。
您可以通过强制执行相同的不变量来编写 runToKErr
:
{-# language Rank2Types #-}
runToKErr :: (forall s. RunM s a) -> KErr a
runToKErr = runST
(当然,你可能会进一步意识到这个限制对于你希望编写的程序来说太强了;到那时你需要失去希望,抱歉,我的意思是你需要重新设计你的程序。)
至于错误信息,你不能"convince the type checker that s1
and s
are the same type"的原因是如果我给你一个ST s a
给定的选择s
和a
,这与给你一些让你自己选择的东西是不一样的 s
。 GHC 选择 s1
(一个 Skolemized 变量)作为 s
,因此试图统一 ST s a
和 ST s1 a