因为以下内容确实阻止:
do
x <- foo
y <- bar
return x + y
脱糖成以下形式:
foo >>= (x -> bar >>= (y -> return x + y))
x -> ...
和y -> ...
在这里不是真的延续吗?
我想知道是否有办法捕获bind
定义中的延续,但我无法正确获得类型。即:
data Pause a = Pause a | Stop
instance Monad Pause where
return x = Stop
m >>= k = Pause k -- this doesn't work of course
现在我尝试混淆这些类型:
data Pause a = Pause a (a -> Pause ???) | Stop
------- k ------
但这也行不通。难道没有办法捕捉这些隐含的延续吗?
顺便说一句,我知道Cont
monad,我只是在尝试和尝试东西。
好吧,我不太确定,但让我提出一些想法。我不太确定它是什么应该意味着捕捉延续。例如,您可以捕获整体 do
结构中的块:
{-# LANGUAGE ExistentialQuantification #-}
import Control.Monad
data MonadExp b = Return b | forall a. Bind (MonadExp a) (a -> MonadExp b)
instance Monad MonadExp where
return x = Return x
f >>= g = Bind f g
例如:
block :: MonadExp Int
block = do
x <- return 1
y <- return 2
return $ x + y
instance Show (MonadExp a) where
show (Return _) = "Return _"
show (Bind _ _) = "Bind _ _"
print block
>> Bind _ _
然后评估整个事情:
finish :: MonadExp a -> a
finish (Return x) = x
finish (Bind f g) = finish $ g (finish f)
print $ finish block
>> 3
或者逐步浏览它并查看零件
step :: MonadExp a -> MonadExp a
step (Return _) = error "At the end"
step (Bind f g) = g $ finish f
print $ step block
>> Bind _ _
print $ step $ step block
>> Return _
好吧,现在我想得更多了,这可能不是你要问的。但也许它会帮助你思考。
好吧,我不知道你的 lambda 是否是严格意义上的延续,但在我眼中它们看起来也与这个概念相似。
但请注意,如果它们是延续,那么脱糖的一元代码已经以延续传递样式 (CPS) 编写。 "捕获"延续的控制运算符的通常概念基于直接样式的程序;"捕获"的延续只隐含在直接样式的程序中,但 CPS 转换使其显式。
由于脱糖的一元代码已经在 CPS 或类似的东西中,好吧,也许一种构建您的问题的方法是 monadic 代码是否可以表达 CPS 代码可以表达的一些控制流技巧。 通常,这些技巧可以归结为这样一种想法:虽然在 CPS 制度下,函数通常通过调用其延续来完成,但函数可以选择用它选择的另一个函数替换其延续。 这个替换延续可以通过引用原始延续来构造,以便它可以反过来"恢复"原始延续(如果它选择)。 例如,协程被实现为相互"替换/恢复"循环。
从这个角度来看,我认为你的答案大多是否定的;CPS 要求在 foo >>= bar
中,foo
必须能够选择是否调用bar
,并且foo
必须能够提供 bar
的替代品,但(>>=)
本身并没有提供foo
执行此操作的机制,更重要的是,(>>=)
控制着执行流, 不是foo
. 一些特定的monad实现了部分或全部(例如,Maybe
monad允许foo
通过产生Nothing
结果来放弃执行bar
),但其他的则没有。
我能得到的最接近的是放弃(>>=)
并改用它:
-- | Execute action @foo@ with its "continuation" @bar@.
callCC :: Monad m => ((a -> m b) -> m b) -> (a -> m b) -> m b
foo `callCC` bar = foo bar
在这里,foo
可以选择是否使用bar
。 但请注意,这个callCC
真的只是($)
!