Есть ли способ зафиксировать продолжения в нотации do?

Поскольку следующий блок do:

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

desugared к следующей форме:

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 Монада, я просто экспериментирую и пробую вещи.

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 _

Ну, теперь, когда я больше об этом думаю, вы, вероятно, не об этом. Но, возможно, это поможет тебе думать.

Ну, я не знаю, являются ли ваши лямбды продолжениями в самом строгом смысле этого слова, но они также выглядят похожими на эту концепцию на мой взгляд.

Но обратите внимание, что если они являются продолжениями, то десагаредированный монадический код уже написан в стиле передачи продолжения (CPS). Обычное понятие управляющего оператора, который "захватывает" продолжение, основано на программах прямого стиля; "захваченное" продолжение является неявным только в программе прямого стиля, но преобразование CPS делает его явным.

Поскольку десагаредированный монадический код уже находится в CPS или что-то в этом роде, возможно, способ сформулировать ваш вопрос состоит в том, может ли монадический код выражать некоторые приемы потока управления, которые может делать код CPS. Как правило, эти уловки сводятся к идее, что, хотя в режиме CPS функция завершает работу, вызывая свое продолжение, функция может выбрать заменить свое продолжение другим по своему выбору. Это продолжение замены может быть построено со ссылкой на исходное продолжение, так что оно может, в свою очередь, "восстановить" это исходное, если оно выберет. Так, например, сопрограммы реализованы как взаимный цикл "заменить / восстановить".

И, глядя на это в свете, я думаю, что ваш ответ в основном нет; CPS требует, чтобы в foo >>= bar, foo должен быть в состоянии выбрать, bar будет вызван на всех, и foo должен быть в состоянии предоставить замену bar, но (>>=) сам по себе не предлагает механизм для foo чтобы сделать это, и что более важно, (>>=) контролирует поток выполнения, а не foo, Некоторые конкретные монады реализуют части или все это (например, Maybe монада позволяет foo отказаться от казни bar производя Nothing результат), а другие нет.

Самое близкое, что я мог получить, это отказаться (>>=) и используйте это вместо:

-- | 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 действительно просто ($)!

Другие вопросы по тегам