Clinton
Clinton

Reputation: 23135

Appending monad

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

Answers (2)

Cactus
Cactus

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

Daniel Wagner
Daniel Wagner

Reputation: 153162

The Writer monad does this:

> let g = tell "b" >> tell "c" >> tell "d"
> runWriter (tell "a" >> g >> tell "e")
((),"abcde")

Upvotes: 9

Related Questions