Использование 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 of type m b и в соответствии с законом он должен эффективно останавливать вычисления всех последующих эффектов), и он фиксирует полученное значение как окончательный результат callCC'.

Итак, мой вопрос:

Эта реализация работает как ожидалось для идеального callCC? Можем ли мы реализовать shift и reset с правильной семантикой?

В дополнение к вышесказанному, я хочу знать:

Чтобы обеспечить правильное поведение, мы должны взять некоторое свойство MonadRef. Итак, что могли бы сделать законы a MonadRef, чтобы сделать описанную выше реализацию так, как ожидалось?

UPDATE

Получается, что приведенная выше наивная реализация недостаточно хороша. Чтобы он удовлетворял "Току продолжения"

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 - Dual to Call-by-Name - Philip Wadler

В приведенной выше статье автор представил "двойное исчисление", типизированное исчисление, соответствующее классической логике. В последнем разделе есть сегмент:

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

Как указано в бумаге Вадлера, вызов по имени оценивает продолжения с нетерпением (он возвращается до того, как все оцениваемые значения), в то время как по умолчанию оценивает продолжения лениво (он возвращается только после оценки всех значений).

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

В общем, я подозреваю, что состояние (вычисление до текущего момента времени) двойственно продолжению (будущему вычислению). Это объяснит несколько феноменов. Если это так, не удивительно, что MonadRef (соответствует глобальному и полиморфному состоянию) является двойственным к MoncadCont (соответствует глобальным и полиморфным продолжениям), и поэтому их можно использовать для реализации друг друга.