Есть ли способ зафиксировать продолжения в нотации 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
действительно просто ($)
!