【问题标题】:Is there a way to capture the continuations in a do notation?有没有办法在 do 表示法中捕获延续?
【发布时间】:2012-07-06 07:56:12
【问题描述】:

由于下面的do块:

do
  x <- foo
  y <- bar
  return x + y

脱糖为以下形式:

foo >>= (\x -> bar >>= (\y -> return x + y))

\x -&gt; ...y -&gt; ... 实际上不是在这里延续吗?

我想知道是否有办法捕获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,我只是在试验和尝试一些东西。

【问题讨论】:

  • 我不确定,但也许您需要更加仔细地思考“捕获延续”的含义。看看你对&gt;&gt;= 的定义:它抛弃了第一个参数......那么第一个参数在那里做什么?

标签: haskell types continuations coroutine continuation-passing


【解决方案1】:

好吧,我不知道您的 lambda 是否是严格意义上的延续,但在我看来它们也与这个概念相似。

但请注意,如果它们是延续,那么脱糖的一元代码已经以延续传递风格 (CPS) 编写。 “捕获”延续的控制操作符的通常概念是基于直接式程序; “捕获的”延续仅隐含在直接式程序中,但 CPS 转换使其显式。

由于去糖的单子代码已经在 CPS 或类似的东西中,好吧,也许一种解决您问题的方法是单子代码是否可以表达 CPS 代码可以表达的一些控制流技巧。通常,这些技巧归结为这样一种想法,即在 CPS 机制下,一个函数通过调用其延续来完成是常规的,一个函数可以选择用它选择的另一个延续来替换它的延续。可以使用对原始延续的引用来构造此替换延续,以便它可以反过来“恢复”原始延续,如果它选择的话。例如,协程被实现为相互的“替换/恢复”循环。

从这个角度来看,我认为你的答案大部分是否定的; CPS 要求在foo &gt;&gt;= bar 中,foo 必须能够选择是否调用bar,并且foo 必须能够提供bar 的替代品,但(&gt;&gt;=) 本身不能为foo 提供一种机制来执行此操作,更重要的是,(&gt;&gt;=) 控制着执行流程,而不是foo。一些特定的 monad 实现了部分或全部(例如,Maybe monad 允许 foo 通过产生 Nothing 结果来放弃对 bar 的执行),但其他人则不这样做。

我能得到的最接近的是放弃 (&gt;&gt;=) 并改用它:

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

【讨论】:

    【解决方案2】:

    好的,我不太确定,但让我提出一些想法。我不太确定它是什么 应该意味着捕捉延续。例如,您可以捕获整个 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 _
    

    好吧,现在我想多了,这可能不是你要问的。但 也许它会帮助你思考。

    【讨论】:

    • 这正是我想要做的——存在类型是缺失的环节。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-10
    • 2022-11-04
    • 1970-01-01
    • 2019-08-25
    相关资源
    最近更新 更多