
On Tue, 2009-01-13 at 19:19 -0500, Dan Doel wrote:
On Tuesday 13 January 2009 5:51:09 pm Luke Palmer wrote:
On Tue, Jan 13, 2009 at 11:21 AM, Tim Newsham
wrote: I have seen several libraries where all functions of a monad have the
monadic result (), e.g. Binary.Put and other writing functions. This is a clear indicator, that the Monad instance is artificial and was only chosen because of the 'do' notation.
Maybe that was the initial reason, but I've actually found the Binary.Put.PutM (where Put = PutM ()) to be useful. Sometimes your putter does need to propogate a result...
But that's the whole point of Writer! Take a monoid, make it into a monad. Put as a monad is silly.
You mean it should be Writer instead?
When GHC starts optimizing (Writer Builder) as well as it optimizes PutM, then that will be a cogent argument.
In that case it's a cogent argument now. :-) You may be interested to note that PutM really is implemented as a writer monad over the Builder monoid: -- | The PutM type. A Writer monad over the efficient Builder monoid. newtype PutM a = Put { unPut :: PairS a } data PairS a = PairS a {-# UNPACK #-}!Builder -- | Put merely lifts Builder into a Writer monad, applied to (). type Put = PutM ()
Until then, one might argue that it misses "the whole point of Put".
Back when we were first writing the binary library, Ross converted our original Put to be a monoid called Builder with Put left as a Writer. GHC optimises it perfectly, we checked. The reason we provide Put as well as Builder is purely for symmetry with code written using Get. Also `mappend` is not so pretty. Another argument for redefining (++) == mappend :-) Get doesn't need to be a Monad either, it only needs to be an applicative functor. Indeed the rules to eliminate adjacent bounds checks only fire if it is used in this way (using >> also works). Duncan