Most monadic functions take pure arguments and return a monadic value. But there are a few that need also monadic arguments, for example:
mplus :: (MonadPlus m) => m a -> m a -> m a
finally :: IO a -> IO b -> IO a
forkIO :: m () -> m ThreadId
-- | From Control.Monad.Parallel
forkExec :: m a -> m (m a)
Each of them seems to bring up a different problem and I can't get a grasp of a generic way how to encode such actions using free monads.
In both
finally
andforkIO
the problem is that the monadic argument is of a different type than the result. But for free one would need them to be of the same type, asIO a
gets replaced by the type variable of the encoding type, likedata MyFunctor x = Finally x x x
, which would only encodeIO a -> IO a -> IO a
.In From zero to cooperative threads in 33 lines of Haskell code the author uses
Fork next next
to fist implementcFork :: (Monad m) => Thread m Bool cFork = liftF (Fork False True)
and then uses it to implement
fork :: (Monad m) => Thread m a -> Thread m ()
where the input and output have different types. But I don't understand if this was derived using some process or just an ad-hoc idea that works for this particular purpose.
mplus
is in particular confusing: a naive encoding asdata F b = MZero | MPlus b b
distributes over
>>=
and a suggested better implementation is more complicated. And also a native implementation of a freeMonadPlus
was removed from free.In freer it's implemented by adding
data NonDetEff a where MZero :: NonDetEff a MPlus :: NonDetEff Bool
Why is
MPlus
NonDetEff Bool
instead ofNonDetEff a a
? And is there a way how to make it work withFree
, where we need the data type to be a functor, other than using the CoYoneda functor?- For
forkExec
I have no idea how to proceed at all.