[Haskell-cafe] newbie "concatenating" monad question

Paul Johnson paul at cogito.org.uk
Sat Mar 24 16:05:25 EDT 2007


Leandro Penz wrote:
> I have some functions that build big strings by calling other 
> functions and appending the result, like
>
> buildStuff =
>   func1 ++ func2 ++ func3 ++ func4
The usual idiom is something like

   buildStuff = concat [func1, func2, func3, func4]

Put the list elements on separate lines if it makes life easier.

> My idea is to have a monad with a concatenating >>, so that I can:
>
> bulidStuff = do
>   func1
>   func2
>   func3
>   func4
>
> I am thinking about using the writer monad, but I would still have to 
> use "tell". The list monad is not an option either, as it does not 
> concatenate.
>
> Is there a proper way of doing something like this (efficiently) ?
The writer monad is a more complicated way of doing this, and might be 
appropriate if you want to build up a string over a lot of different 
functions.  But its probably easier to use concat.

One thing: beware of the copying overhead.  The naive use of the Writer 
monad is to use ++, but every time you "tell" it a new string the 
accumulated string so far gets copied to have the new one appended.  The 
way around this is to use StringS (which you will find in the Prelude).  
StringS has the type "String -> String", which seems really wierd.  An 
StringS takes a string, prepends something on to it, and then returns 
the resulting string.  The clever thing is that you can take two StringS 
and concatenate them using (.)  function composition in constant time.  
So I can write:

   foo, bar, stop, foobar :: StringS
   foo = ("Hello " ++)  -- A function from a string to a string.
   bar = ("world" ++)
   stop = ('.' :)  -- Also a function from a string to a string: work it 
out.
   foobar = foo . bar . stop -- Function composition: very efficient.
   main = do
      putStrLn (foobar "")  -- Get the final result by applying the 
function to an empty string.

You can do this inside the Writer monad as well because functions, like 
strings, are instances of the Monoid class (i.e. they implement mplus in 
the way you would expect).  You just have to wrap a function around 
"tell" like

   say str = tell (str ++)

and remember that runWriter will then hand you a StringS.

Hope this helps,

Paul.


More information about the Haskell-Cafe mailing list