There is a well known issue that we cannot use forall
types in the Cont
return type.
However it should be OK to have the following definition:
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
and then find an instance that makes sense. In this paper the author claimed that we can implement MonadFix
on top of ContT r m
providing that m
implemented MonadFix
and MonadRef
. But I think if we do have a MonadRef
we can actually implement callCC'
above like the following:
--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 = ...
(Unfortunately I am not familiar with the semantic of shift
and reset
so I didn't provide implementations for them)
This implementation seems OK for me. Intuitively, when callCC'
being called, we feed k
which a function that its own effect is always fail (although we are not able to provide a value of arbitrary type b
, but we can always provide mzero
of type m b
and according to the law it should effectively stop all further effects being computed), and it captures the received value as the final result of callCC'
.
So my question is:
Is this implementation works as expected for an ideal callCC
? Can we implement shift
and reset
with proper semantic as well?
In addition to the above, I want to know:
To ensure the proper behaviour we have to assume some property of MonadRef
. So what would the laws a MonadRef
to have in order to make the above implementation behave as expected?
UPDATE
It turn out that the above naive implementation is not good enough. To make it satisfy "Continuation current"
callCC $\k -> k m === callCC $ const m === m
We have to adjust the implementation to
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))
In other words, the original MonadZero
is not enough, we have to be able to combind a mzero
value with a normal computation without cancelling the whole computation.
The above does not answer the question, it is just adjusted as the original attempt was falsified to be a candidate. But for the updated version, the original questions are still questions. Especially, reset
and shift
are still up to be implemented.