ekmett / kan-extensions

Kan extensions, Kan lifts, the Yoneda lemma, and (co)monads generated by a functor
Other
78 stars 33 forks source link

Laws hold? MonadFix (Codensity m) #64

Open Icelandjack opened 3 years ago

Icelandjack commented 3 years ago

The library mmtl has a MonadFix (Codensity m) instance

-- still need to prove that MonadFix laws hold
instance MonadFix m => MonadFix (Codensity m) where
    mfix :: (a -> Codensity m a) -> Codensity m a
    mfix f = lift (mfix (lowerCodensity . f))

https://hackage.haskell.org/package/mmtl-0.1/docs/Control-Monad-Codensity.html

paf31 commented 3 years ago

I'm using Codensity to ensure timely resource cleanup (like a transformer version of the managed library), in which case this is not going to be the instance you want, since it will run your finalizers early, as you exit the mdo block.

Another option is to use a lazily-evaluated promise for the result, mirroring the approach in fixIO:

fixCodensity :: MonadIO m => (a -> Codensity m a) -> Codensity m a
fixCodensity f = Codensity \k -> do
  promise <- liftIO $ IORef.newIORef (error "result eval'd too early")
  ans <- liftIO $ unsafeDupableInterleaveIO (IORef.readIORef promise)
  runCodensity (f ans) \a -> do
    liftIO $ IORef.writeIORef promise a
    k a

This is also probably not the instance you'd want in a general-purpose library, but I figured I'd put it here for reference.

Mathnerd314 commented 3 years ago

There's also an implementation here: https://stackoverflow.com/questions/25827227/why-cant-there-be-an-instance-of-monadfix-for-the-continuation-monad

instance MonadFix m => MonadFix (Codensity m) where
  mfix f = Codensity (\ka -> mfixing (\a -> runCodensity (f a) ka<&>(,a)))
    where mfixing f = fst <$> mfix (\ ~(_,a) -> f a )