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
finallyandforkIOthe 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 agets 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 nextto 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.
mplusis in particular confusing: a naive encoding asdata F b = MZero | MPlus b bdistributes over
>>=and a suggested better implementation is more complicated. And also a native implementation of a freeMonadPluswas removed from free.In freer it's implemented by adding
data NonDetEff a where MZero :: NonDetEff a MPlus :: NonDetEff BoolWhy is
MPlusNonDetEff Boolinstead 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
forkExecI have no idea how to proceed at all.