Существует известная проблема: мы не можем использовать типы 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
должны быть реализованы.