Reputation: 23135
With just putChar
I can do stuff like this:
f =
do
putChar 'a'
putChar 'b'
g
putChar 'f'
putChar 'g'
g =
do
putChar 'c'
putChar 'd'
putChar 'e'
And what I will find when I "run" this (by saying main = f
) it'll just print the characters out in order.
What I'm looking for is a non-IO
version. Something that works a bit like this:
f =
do
append 'a'
append 'b'
g
append 'f'
append 'g'
g =
do
append 'c'
append 'd'
append 'e'
And a function, say runAppend :: t a -> [a]
Such that runAppend f
= ['a','b','c','d','e','f','g']
Naturally I'd want this to run in linear time (i.e. not suffer issues like ++
does when you do the concatenations in a bad order).
This seems like a fairly common use case so I'm guessing it exists, if someone could point me to it that would be good, I didn't want to reinvent the wheel.
Upvotes: 0
Views: 441
Reputation: 27636
You can use a Writer
monad for this:
import Control.Monad.Writer
import Data.DList (DList)
import qualified Data.DList as DList
type Append c = Writer (DList c)
append :: c -> Append c ()
append = tell . pure
runAppend :: Append c () -> [c]
runAppend = DList.toList . execWriter
f, g :: Append Char ()
This is a bit more complicated than necessary because it uses a DList Char
instead of a String
. DList
gives you an efficient Monoid
instance for this case where you keep appending to the end.
Upvotes: 7
Reputation: 153162
The Writer monad does this:
> let g = tell "b" >> tell "c" >> tell "d"
> runWriter (tell "a" >> g >> tell "e")
((),"abcde")
Upvotes: 9