-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MonadTrans instance for Free violates monad transformer laws #3
Comments
So, it turns out there's a very simple and elegant solution. Define a free monad transformer: data FreeT f m r = FreeT { runFreeT :: m (Either r (f (FreeT f m r))) }
instance (Functor f, Monad m) => Monad (FreeT f m) where
return = FreeT . return . Left
m >>= f = FreeT $ runFreeT m >>= \x -> case x of
Left r -> runFreeT $ f r
Right a -> return $ Right $ fmap (>>= f) a
instance MonadTrans (FreeT f) where
lift = FreeT . liftM Left The above solution is a common idiom among coroutine libraries that implement |
Great catch. I've actually built the free monad transformer for a related reason in scala, but had overlooked the actual law violation taking place here. This is awkward because the existing MonadTrans is actually quite useful but the existence of liftF kind of takes the edge off. This is definitely a "SHOULD FIX" issue. =) I'll push a version with it when I can get a better grasp of what downstream packages will be broken, since a major version bump requires me to push about a dozen other packages of my own and I should audit any third party dependencies as well. |
It's not urgent to fix (for me, at least). I only brought it up because I discovered the exact same violation in my own In a future release I may also use the free monad transformer approach to fix the issue in pipes and if I do that I'd like to use this library as the dependency for it. So from my point of view the existence of the wrong |
On Sun, Mar 25, 2012 at 11:25 AM, Gabriel439 <
We seem to have invented basically the same construction. At ClariFi have an in-house iteratee-like library (in scala) that looks a In a future release I may also use the free monad transformer approach to
I can definitely see the utility of it. Hrmm. The worry that I have is that folks will then pop up to pressure me Er that was a bit rambly but I hope you get the idea. =) -Edward |
I got the idea. I think a separate type is fine. Regarding bidirectional channels, if you are using the base monad to signal back to upstream, you can implement the same behavior with unidirectional channels by having upstream yield a monadic action alongside its normal yielded value which signals to downstream its desired behavior up until the next time it is requested. This is the approach I'm currently using to safely intercept downstream termination without violating the category laws for pipes. The reason is that I can never get bidirectional channels to satisfy the identity law. |
That FreeT type is exactly the same as the FreeT type from control-monad-free: http://hackage.haskell.org/packages/archive/control-monad-free/0.5.3/doc/html/Control-Monad-Free.html#g:3 |
Thanks for the tip! I can use that for now instead of rolling my own. |
I've added FreeT and CofreeT. I've kept the illegal MonadTrans, Alternative and MonadPlus instances, but I've marked them as illegal in the comments. |
Like the title says, the
MonadTrans
instance forFree
violates both monad transformer laws. I'll begin with the first law:This is because the left-hand side creates a
Pure
embedded within aFree
:... but the right-hand side creates just a naked
Pure
:The
MonadTrans
instance forFree
also violates the second monad transformer law:The left-hand side comes out to:
... which gives a
Pure
embedded within a singleFree
, whereas the right-hand side:... gives a
Pure
embedded within twoFree
s.The laws are only true if you apply
retract
to both sides of the equation, butretract
is not a monomorphism.The text was updated successfully, but these errors were encountered: