有没有办法在do符号中捕获延续



因为以下内容确实阻止:

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真的只是($)

最新更新