Sunday, April 17, 2011

The Monad instance for Iteratees

I'd like to walk through the Monad instance for Iteratees. Understanding the monad instance makes it easier to understand whats happening when you try and write a moderately complex iteratee


>instance Monad m => Monad (Iteratee a m) where
> return x = yield x (Chunks [])


The definition of return should be straightforward. Return creates an iteratee that is immediately ready to yield x without any accepting any data, and without adding any extra data.


($ m0) $ applies m0 to the giant function that follows.
fix $ \bind m creates a recursive function named bind, with m0 passed as m initially. note the type for bind:

bind :: Iteratee a m b -> Iteratee a m c, this will be useful later


> m0 >>= f = ($ m0) $ fix $



Iteratee $ runIteratee m is standard unwrapping. The >>= is for the inner Monad.



> \bind m -> Iteratee $ runIteratee m >>= \r1 ->
> case r1 of


Here the iteratee hasn't returned yet, so we compose the continuation with bind, creating essentially a while loop. While the first iteratee hasn't returned yet, keep asking for data, and checking its result. Also note the types:
>>= :: Iteratee a m b -> (b -> Iteratee a m c) -> Iteratee a m c
k :: a -> Iteratee a m b
(bind . k) :: a -> Iteratee a m c


> Continue k -> return (Continue (bind . k))
> Error err -> return (Error err)


If the source Iteratee finally yields, with no extra data then (f x) is the iteratee we want to return.


> Yield x (Chunks []) -> runIteratee (f x)


If there's any extra data, we first grab the step from the inner Monad. If it's a Continuation, we feed it the extra before returning. Otherwise, we pass along the extra.


> Yield x extra -> runIteratee (f x) >>= \r2 ->
> case r2 of
> Continue k -> runIteratee (k extra)
> Error err -> return (Error err)
> Yield x' _ -> return (Yield x' extra)


An important thing to note is what happens in the case of EOF. The EOF would be on returned along with the Yield, and then fed to the second Iteratee. It's possible that a chain of Iteratees could be resolved like this.


Another important thing to note is that the type a remains fixed. The monad instance of an Iteratee allows you to compose functions that read from the same stream. It's also possible to transform streams, but that's not  what's going on here.

No comments:

Post a Comment