An additional thought: I'd say 'contained' is sort of inverse to 'writer': writer <=< contained = id contained . writer = return
Petr Pudlak 2012/12/9 Petr P <petr....@gmail.com> > Hi all, > > I'd say that a type class declares functions and specifies laws (in the > docs) > what its implementations must adhere to. It's not the job of a type class > to > fulfill the laws, it's the job of its implementations. So the reason for > 'Monoid w' in 'MonadWriter' cannot be that then 'MonadWriter' wouldn't be a > monad. Such constraints should be required only by implementations. > > It is true that any Writer has an implicit underlying monoid, and we can > even "extract" the operations from it as follows. The empty element can be > extracted as > > empty = liftM snd (listen (return ())) :: m w > > Having this 'empty', we can give 'const empty' to 'pass' to discard output > of > an action, so we can construct: > > -- | @contained m@ executes the action @m@ in a contained environment > and > -- returns its value and its output. The current output is not > modified. > contained :: m a -> m (a, w) > contained k = do > -- we can retrieve mempty even if we don't have the monoid > constraint: > ~(_, empty) <- listen (return ()) > -- listen what @k@ does, get its result and ignore its output > change: > pass (listen k >>= \x -> return (x, const empty)) > > This generalizes 'listen' and 'pass' (both can be easily defined from it) > and I find this function much easier to understand. In a way, it is also a > generalization of WriterT's runWriterT, because for WriterT we have > 'contained = lift . runWriterT'. > > [I implemented 'contained' in a fork of the mtl library, if anybody is > interested: https://github.com/ppetr/mtl ] > > With that, we can do > > -- Doesn't produce any output, only returns the combination > -- of the arguments. > append x y = liftM snd $ contained (tell x >> tell y) :: w -> w -> m w > > I didn't check the monoid laws, but it seems obvious that they follow from > the > monad laws and (a bit vague) specification of 'listen' and 'pass'. > > Personally, I'd find it better if `MonadWriter` would be split into two > levels: > One with just 'tell' and 'writer' and the next level extending it with > 'listen'/'pass'/'contained'. The first level would allow things like > logging to > a file, without any monoidal structure. But this would break a lot of stuff > (until we agree on and develop something like > http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances). > > Best regards, > Petr > > > > 2012/12/9 Roman Cheplyaka <r...@ro-che.info> > >> * Edward Z. Yang <ezy...@mit.edu> [2012-12-08 15:45:54-0800] >> > > Second, even *if* the above holds (two tells are equivalent to one >> > > tell), then there is *some* function f such that >> > > >> > > tell w1 >> tell w2 == tell (f w1 w2) >> > > >> > > It isn't necessary that f coincides with mappend, or even that the >> type >> > > w is declared as a Monoid at all. The only thing we can tell from the >> > > Monad laws is that that function f should be associative. >> > >> > Well, the function is associative: that's half of the way there to >> > a monoid; all you need is the identity! But we have those too: >> > whatever the value of the execWriter (return ()) is... >> >> Let me repeat: >> >> It isn't necessary that f coincides with mappend, or even that the >> type w is declared as a Monoid at all. >> >> Let me illustrate this with an example. >> >> data MyWriter a = MyWriter Integer a >> >> instance Monad MyWriter where >> return = MyWriter 0 >> MyWriter n x >>= k = >> let MyWriter n' y = k x >> in MyWriter (n+n') y >> >> instance MonadWriter Integer MyWriter where >> tell n = MyWriter n () >> listen (MyWriter n x) = return (x,n) >> pass (MyWriter n (a,f)) = MyWriter (f n) a >> >> Yes, integers do form a monoid when equipped with 0 and (+). However, we >> know well why they are not an instance of Monoid — namely, there's more >> than one way they form a monoid. >> >> Even if something is in theory a monoid, there may be technical reasons >> not to declare it a Monoid. Likewise, imposing a (technical) superclass >> constraint on MonadWriter has nothing to do with whether the Monad will >> be well-behaved. >> >> This is true in both directions: even if the type is an instance of >> Monoid, nothing forces the Monad instance to use the Monoid instance. >> I.e. I can declare a MonadWriter on the Sum newtype whose bind, instead >> of adding, subtracts the numbers. >> >> Roman >> > >
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe