4

由于以下做块:

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

脱糖为以下形式:

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

这里不是\x -> ...而且y -> ...实际上是延续吗?

我想知道是否有一种方法可以捕获 定义中的延续bind,但我无法获得正确的类型。IE:

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单子,我只是在试验和尝试一些东西。

4

2 回答 2

1

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

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

于 2012-07-05T23:27:16.733 回答
1

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

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

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

从这个角度来看,我认为您的答案大多是否定的;CPS 要求 in foo >>= bar,foo必须能够选择是否bar将被调用,并且foo必须能够为 提供替代品bar,但(>>=)它本身并没有提供foo这样做的机制,更重要的(>>=)是,它可以控制执行流,不是foo。一些特定的 monad 实现了它的一部分或全部(例如,Maybemonad 允许通过产生结果foo来放弃执行),但其他的则不然。barNothing

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

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

于 2012-07-05T23:27:58.870 回答