There is a known problem: we cannot use forall types in the Cont return type .
However, it should be good to have the following definition:
class Monad m => MonadCont' m where callCC' :: ((a -> forall b. mb) -> ma) -> ma shift :: (forall r.(a -> mr) -> mr) -> ma reset :: ma -> ma
and then find the instance that makes sense. In this article, the author claimed that we can implement MonadFix on top of ContT rm by providing m implemented MonadFix and MonadRef . But I think that if we have MonadRef , we can actually implement callCC' above, as shown below:
--satisfy law: mzero >>= f === mzero class Monad m => MonadZero m where mzero :: ma instance (MonadZero m, MonadRef rm) => 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 = ...
(Unfortunately, I am not familiar with the semantics of shift and reset , so I did not provide them with an implementation)
This implementation seems appropriate to me. Intuitively, when callCC' is callCC' , we are root k , which a function that has its own effect always fails (although we cannot provide a value of arbitrary type b , but we can always provide mzero of type mb and, in accordance with the law, it must effectively stop computing all subsequent effects), and it captures the resulting value as the final result of callCC' .
So my question is:
callCC this implementation work as expected for a perfect callCC ? Can we implement shift and reset with the correct semantics?
In addition to the above, I want to know:
To ensure proper behavior, we need to take some MonadRef property. So, what could the laws of a MonadRef do to make the implementation described above as expected?
UPDATE
It turns out that the naive implementation given above is not good enough. To satisfy the Continuation Current
callCC $\k -> km === callCC $ const m === m
We must configure the implementation to
instance (MonadPlus m, MonadRef rm) => MonadCont' m where callCC' k = do ref <- newRef mzero mplus (k $ \a -> writeRef ref (return a) >> mzero) (join (readRef ref))
In other words, the original MonadZero not enough, we should be able to combine the mzero value with a normal calculation without canceling the whole calculation.
The above does not answer the question, it is simply adjusted because the original attempt was falsified as a candidate. But for the updated version, the original questions remain questions. Especially, reset and shift should be implemented.