以下是使用分隔延续(重置/移位)的简单示例:
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
效果很好。
但是,我想提取延续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
| ^^^^^^^^^^
任何人都可以帮助我解决这个问题吗?
Thanks.
标准Cont
是不完全普遍的。 “真实的”Cont
看起来像这样
newtype Cont i o a = Cont { runCont :: (a -> i) -> o }
-- versus the standard
newtype SadCont r a = SadCont { sadCont :: (a -> r) -> r }
-- SadCont r a = Cont r r a
标准SadCont
使用是因为它支持>>=
and return
在他们通常的类型(所以它可以是Monad
)。但“真正的”内部有分隔的延续Cont
允许每个shift
从一种类型的延续中获取值并将它们向上发送到前一个类型shift
/reset
在不同的类型。在这种情况下,您只是通过entire作为函数连续shift
to reset
.
{-# LANGUAGE RebindableSyntax #-}
-- ^ placing this at the top of a file or passing -XRebindableSyntax to GHC allows do notation to use custom (>>=) and (>>)
-- not Monad operations!
return :: a -> Cont r r a
return x = Cont ($ x)
(>>=) :: Cont m o a -> (a -> Cont i m b) -> Cont i o b
Cont x >>= f = Cont $ \k -> x (($ k) . runCont . f)
(>>) :: Cont m o a -> Cont i m b -> Cont i o b -- RebindableSyntax also wants this
a >> b = a >>= const b
evalCont :: Cont a o a -> o
evalCont (Cont x) = x id
-- shift/reset are actually just
reset = evalCont
shift = Cont
-- note that the types of reset and shift differ significantly from transformers
-- reset returns a pure value here and shift requires a pure value from its function
-- I think my choices are more correct/standard, e.g. they line up with the old Scala shift/reset http://lampwww.epfl.ch/~hmiller/scaladoc/library/scala/util/continuations/package.html
在你的例子中
test2 :: Integer -> Integer
test2 = reset $ do
r <- shift $ \k -> k
return $ 1 + r
TL;DR Cont
被故意“破坏”,因此它失去了不同输入和输出类型的通用性,但获得了Monad
冰城。正如您所发现的,您可以通过将输入和输出类型放入(递归)总和中来解决它。或者(这个答案)您可以定义并使用“真实”Cont
.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)