Используйте MonadRef для реализации MonadCont
Существует хорошо известная проблема, которую мы не можем использовать forall
вводит в Cont
тип возврата.
Однако должно быть хорошо иметь следующее определение:
class Monad m => MonadCont' m where
callCC' :: ((a -> forall b. m b) -> m a) -> m a
shift :: (forall r.(a -> m r) -> m r) -> m a
reset :: m a -> m a
а затем найти экземпляр, который имеет смысл. В этой статье автор утверждал, что мы можем реализовать MonadFix
на вершине ContT r m
при условии что m
реализованы MonadFix
а также MonadRef
, Но я думаю, что если у нас есть MonadRef
мы можем реально реализовать callCC'
выше, как следующее:
--satisfy law: mzero >>= f === mzero
class Monad m => MonadZero m where
mzero :: m a
instance (MonadZero m, MonadRef r m) => MonadCont' m where
callCC' k = do
ref <- newRef Nothing
v <- k (\a -> writeRef ref (Just a) >> mzero)
r <- readRef ref
return $ maybe v id r
shift = ...
reset = ...
(К сожалению, я не знаком с семантикой shift
а также reset
поэтому я не предоставил реализации для них)
Эта реализация, кажется, хорошо для меня. Интуитивно, когда callCC'
будучи призванным, мы кормим k
что функция, что ее собственный эффект всегда терпит неудачу (хотя мы не можем предоставить значение произвольного типа b
, но мы всегда можем предоставить mzero
типа m b
и в соответствии с законом он должен эффективно прекратить вычисление всех дальнейших эффектов), и он получает полученное значение как конечный результат callCC'
,
Итак, мой вопрос:
Является ли эта реализация работает, как ожидается, для идеального callCC
? Можем ли мы реализовать shift
а также reset
с правильной семантикой, а?
В дополнение к вышесказанному, я хочу знать:
Чтобы обеспечить правильное поведение, мы должны принять некоторые свойства MonadRef
, Так что бы законы MonadRef
иметь для того, чтобы заставить вышеупомянутую реализацию вести себя как ожидалось?
ОБНОВИТЬ
Оказывается, вышеприведенная наивная реализация недостаточно хороша. Чтобы оно удовлетворяло "Ток продолжения"
callCC $\k -> k m === callCC $ const m === m
Мы должны приспособить реализацию к
instance (MonadPlus m, MonadRef r m) => MonadCont' m where
callCC' k = do
ref <- newRef mzero
mplus (k $ \a -> writeRef ref (return a) >> mzero) (join (readRef ref))
Другими словами, оригинал MonadZero
недостаточно, мы должны быть в состоянии объединить mzero
значение с нормальным вычислением без отмены всего вычисления.
Вышеизложенное не отвечает на вопрос, оно просто скорректировано, поскольку первоначальная попытка была сфальсифицирована, чтобы быть кандидатом. Но для обновленной версии оригинальные вопросы все еще остаются вопросами. Особенно, reset
а также shift
еще предстоит реализовать.
1 ответ
(Это еще не ответ, но только некоторые подсказки возникли у меня в голове. Надеюсь, что это приведет к реальному ответу, я или кто-то еще.)
Call-by-Value - это двойственный вызов по имени - Philip Wadler
В вышеприведенной статье автор представил "Двойное исчисление", типизированное исчисление, соответствующее классической логике. В последнем разделе есть сегмент говорит
Стратегия, двойная по требованию, могла бы избежать этой неэффективности, перезаписывая coterm его covalue при первой оценке.
Как указано в статье Вадлера, вызов по имени оценивает продолжения с нетерпением (он возвращается до оценки всех значений), в то время как вызов по значению оценивает продолжения лениво (возвращается только после оценки всех значений).
Теперь взгляните на callCC'
выше, я полагаю, что это пример двойного вызова по требованию на стороне продолжения. Стратегия оценки заключается в том, чтобы обеспечить поддельное "продолжение" данной функции, но кешировать состояние в этой точке, чтобы позже вызвать "истинное" продолжение. Это похоже на создание кэша продолжения, и поэтому, как только вычисления завершатся, мы восстановим это продолжение. Но кэшируйте оцененное значение, как оно подразумевается по требованию.
В общем, я подозреваю, что состояние (вычисление до текущего момента времени) двойственно продолжению (будущее вычисление). Это объяснит несколько явлений. Если это правда, то неудивительно, что MonadRef
(соответствуют глобальному и полиморфному состоянию) двойственны MoncadCont
(соответствуют глобальным и полиморфным продолжениям), и поэтому их можно использовать для реализации друг друга.