以下是使用分隔接续(reset/shift)的简单范例:
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Cont
test :: Integer
test = evalCont . reset $ do
r <- shift $ \k -> do
return $ k 10
return $ 1 + r
λ> test1
11
它工作得很好。
但是,我想将continuation k
提取为一个纯函数以备将来使用,而不是在shift中调用它。
例如,我希望test2
可以返回k
:
test2 :: Integer -> Integer
test2 = evalCont . reset $ do
r <- shift $ \k -> do
return $ k
return $ 1 + r
但GHC抱怨道:
? Couldn't match type 'Integer -> Integer' with 'Integer'
Expected type: Cont (Integer -> Integer) (Integer -> Integer)
Actual type: ContT
(Integer -> Integer)
Data.Functor.Identity.Identity
((Integer -> Integer) -> Integer -> Integer)
? In a stmt of a 'do' block: return $ k
In the expression: do return $ k
In the second argument of '($)', namely '\ k -> do return $ k'
|
88 | return $ k
| ^^^^^^^^^^
有人能帮我解决这个问题吗?
- 谢谢-谢谢
2条答案
按热度按时间6yt4nkrj1#
标准的
Cont
是不完全通用的。使用标准
SadCont
是因为它支持常用类型的>>=
和return
(因此它可以是Monad
)。但是Cont
内部的“真实的”定界延续允许每个shift
从一种类型的延续中取值,并将它们向上发送到前一个不同类型的shift
/reset
。在本例中,您只是将 entire continuation作为函数从shift
传递到reset
。在您的示例中
TL;DR
Cont
被故意“破坏”了,所以它失去了不同输入和输出类型的通用性,但却获得了Monad
的特性。你可以通过将输入和输出类型放入一个(递归)和中来解决这个问题。或者(这个答案)你可以定义并使用“真实的”Cont
。drkbr07n2#
受到@BenjaminHodgson评论的启发,以下是临时解决方案:
免责声明:我不确定递归类型
Ret
是否必要。如果有人能提供更好的解决方案或解释,我将不胜感激。谢谢。