[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