Я не думаю, что это возможно. Для справки вот значение ContT
:
ContT r m a = (a -> m r) -> m r
Вот моя отправная точка для listen
:
listen m = ContT $ \c ->
runCont m (\x -> c (x,w))
Вопрос в том, где мы получаем w
? w
будет получено из вычисления, которое runCont m
выполняет до того, как вызовет нашу функцию \x -> c (x,w)
с возвращаемым значением x
. То есть информация, которую мы должны передать c
, взята из runCont
, поэтому нам нужно сделать что-то вроде этого:
listen m = ContT $ \c -> do
rec (r,w) <- listen . runContT m $ \x -> c (x,w)
return r
(необходимо LANGUAGE DoRec
и MonadFix m
в контексте)
Хотя этот тип проверяет, это не правильно. w
теперь является значением, записанным вычислением whole , а не только частью перед вызовом нашего продолжения \x -> c (x,w)
.
Вы видите, что вам нужно будет сделать? Я знаю, что мой ответ по сути «я думаю, что это невозможно, потому что я не могу придумать, как это сделать» (то, что Конал Эллиотт называет «доказательством недостатка воображения»), но я думаю, что мое отсутствие воображения на этот раз правильно. Информация, в которой мы нуждаемся, уничтожается, прежде чем мы успеваем взглянуть на нее.
Я полагаю, что этот случай возможен с монадным трансформатором Codensity :
newtype CodensityT m a = CodensityT { runCodensityT :: forall r. (a -> m r) -> m r }
, который дает те же улучшения производительности, что и Cont
в тех случаях, когда он это делает, но не поддерживает callCC
. Это потому, что вы можете runCodensityT
в середине вычисления с любым r
, который вы хотите.
listen m = CodensityT $ \c -> listen (runCodensityT m return) >>= c
Может быть, callCC
это проблема. Я не удивлюсь, если вы приведете пример, сочетающий listen
и callCC
, который может создать парадокс.