[Haskell-beginners] Rewriting using State and/or Reader?

Brent Yorgey byorgey at seas.upenn.edu
Fri Nov 12 17:12:33 EST 2010


On Fri, Nov 12, 2010 at 09:38:35PM +0000, Paul Sargent wrote:
> Hi,
> 
> My Haskell project has quite a lot of code in it which either:
> 
> a) Uses a set of common values which are stored in an "environment" data type.
> b) Takes a set of values, and updates them repetitively.
> 
> Often the same function will do both.
> 
> Up to now I've tended to avoid monad based code (except List and Maybe), but I'm thinking that maybe I can simplify some of my code.
> 
> The code for case a) might look something like:
> 
>     data Env = Env {rateA    :: Double,
>                     valueB   :: Double} deriving (Show)
> 
>     f :: Env -> Double -> Double -> Double
>     f env a b = a * ra + b * g' b
>         where ra = rateA env
>               g' = g env
> 
>     g :: Env -> Double -> Double
>     g env b = (valueB env) + b
> 
> Basically I'm threading the Env parameter through all the function
> calls that need it. Am I right in saying this sort of stuff is a
> good candidate for the Reader monad? What do I gain if I do it?

Yes, this is a good candidate for Reader.  You gain not having to
thread the environment around everywhere.  Unfortunately, you lose a
bit in syntax.  These particular functions
could be written like this:

  f :: Double -> Double -> Reader Env Double
  f a b = do ra <- asks rateA
             g' <- g b
             return a * ra + b * g'

  g :: Double -> Reader Env Double
  g b = asks valueB >>= \b' -> return (b + b')

You can make this a little better with Applicative syntax, especially
if you use InfixApplicative
(http://hackage.haskell.org/package/InfixApplicative):

  f a b = (pure a <^(*)^> asks rateA) <^(+)^> (pure b <^(*)^> g b)

well... I guess you can decide whether you think that's any better.
At least it's closer to the original.

> Similarly am I right in saying that this type of code is a good candidate for State (called repeatedly from some loop somewhere)?
> 
>     updateList :: Double -> [Item] -> [Item]
>     updateList t xs = map (updateItemToTime t) xs
> 
>     updateItemToTime :: Double -> Item -> Item
>     updateItemToTime = some function which does an incremental calculation for a time slice
> 
> (obviously the code is nonsense, I'm just trying to give a feel of
> the structures I'm using)

Yes, this could make use of State.

> 
> Then, if I did move the code to these monads, how well do the live
> together? ...and how about functions which use functions in multiple
> different state or reader monads?

There are actually nice ways to make different monads live together,
but it can require some rather complex and abstract machinery.  See
e.g. the Monatron package
(http://hackage.haskell.org/package/Monatron) and this paper:

http://people.cs.kuleuven.be/~tom.schrijvers/Research/papers/monad_zipper_draft.pdf

> Basically, I haven't seen the advantage of using the monadic way for
> this code, so what am I missing?

Not too much, necessarily.  For the sorts of simple things it seems
like you're doing, I think the syntactic overhead outweighs the
benefits.

-Brent


More information about the Beginners mailing list