Use MonadRef para implementar MonadCont
Hay un problema bien conocido queno podemos usarforall
tipos en elCont
tipo de retorno.
Sin embargo, debería estar bien tener la siguiente definición:
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
y luego encuentre una instancia que tenga sentido. Eneste papel el autor afirmó que podemos implementarMonadFix
encima deContT r m
siempre quem
implementadoMonadFix
yMonadRef
. Pero creo que si tenemos unMonadRef
en realidad podemos implementarcallCC'
arriba como el siguiente:
--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 = ...
(Desafortunadamente no estoy familiarizado con la semántica deshift
yreset
así que no proporcioné implementaciones para ellos)
Esta implementación me parece bien. Intuitivamente, cuandocallCC'
siendo llamado, alimentamosk
que una función que su propio efecto siempre falla (aunque no podemos proporcionar un valor de tipo arbitrariob
, pero siempre podemos proporcionarmzero
de tipom b
y de acuerdo con la ley, debería detener efectivamente todos los efectos adicionales que se calculan), y captura el valor recibido como resultado final decallCC'
.
Entonces mi pregunta es:
¿Esta implementación funciona como se espera para un idealcallCC
? Podemos implementarshift
yreset
con la semántica adecuada también?
Además de lo anterior, quiero saber:
Para garantizar el comportamiento adecuado, debemos asumir alguna propiedad deMonadRef
. Entonces, ¿cuáles serían las leyes unMonadRef
tener para que la implementación anterior se comporte como se esperaba?
ACTUALIZAR
Resulta que la implementación ingenua anterior no es lo suficientemente buena. Para que satisfaga la "corriente de continuación"
callCC $\k -> k m === callCC $ const m === m
Tenemos que ajustar la implementación a
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))
En otras palabras, el originalMonadZero
no es suficiente, tenemos que poder combinar unmzero
valor con un cálculo normal sin cancelar todo el cálculo.
Lo anterior no responde a la pregunta, solo se ajusta ya que el intento original fue falsificado para ser candidato. Pero para la versión actualizada, las preguntas originales siguen siendo preguntas. Especialmente,reset
yshift
aún están por implementarse.