[Haskell-cafe] TraverseAccum: an effectful accumulating map.
Florent BALESTRIERI
fbalestrieri at cnsmdp.fr
Tue Apr 28 10:50:04 EDT 2009
Hello fellow haskellers. This message is a valid literate haskell
file. I ran it with ghc 6.10.1
> module TraverseAccum where
> import Control.Applicative
> import Data.Traversable
A year ago, I read "Why Attribute Grammars Matter". In it we found a
function on lists wich combined three traversals into one to compute the
differences to the average:
> avg_diff_list :: [Double] -> [Double]
> avg_diff_list xs =
> let nil = const (0,0.0,[])
> cons x f mean =
> let (n,s,ds) = f mean
> in (n+1,s+x, (x - mean) : ds)
> (n,s,ds) = foldr cons nil xs (s/n)
> in ds
Then I found "Applicative programming with effects" which abstracted the
notions of traversal and folding.
We can try and generalize avg_diff_list to work over any Traversable data
structure. Here is a possible version with two traversals:
> avg_diff' :: (Traversable t, Fractional f)
> => t f -> t f
> avg_diff' t =
> let start = (0,0.0)
> step (n,s) x = ((n+1, s+x), \mean -> x - mean)
> ((n,s), diffs) = mapAccumL step start t
> in sequenceA diffs (s/n)
But we can do better with only a single traversal. The idea is to
define a function traverseAccumL which does the work of mapAccumL but with
an effectful map. (mapM)
The definition of mapAccumL uses a StateL applicative functor whereas
traverseAccumL uses a StateL monad transformer.
First a few definitions.
> -- left-to-right state transformer
> newtype StateLT m s a = StateLT { runStateLT :: s -> m (s, a) }
> instance Functor m => Functor (StateLT m s) where
> fmap f (StateLT k) = StateLT $ \ s -> fmap f <$> k s
> instance (Functor m, Monad m) => Applicative (StateLT m s) where
> pure x = StateLT (\ s -> return (s, x))
> StateLT kf <*> StateLT kv = StateLT kfv
> where kfv s = do (s', f) <- kf s
> (s'', v) <- kv s'
> return (s'', f v)
Incidently, I always wondered why Monad wasn't a subclass of Functor.
> traverseAccumL :: ( Traversable t, Functor m, Monad m)
> => (a -> b -> m (a, c)) -> a -> t b -> m (a, t c)
> traverseAccumL f s t = runStateLT (traverse (StateLT . flip f) t) s
I could have done without StateLT but the signature would have been
different:
traverseAccumL :: (Traversable t, Monad m)
=> (a -> b -> m (c, a)) -> a -> t b -> m (t c, a)
traverseAccumL f s t = runStateT (mapM (StateT . flip f) t) s
Now the definition of avg_diff calls traverseAccumL with the reader monad
> avg_diff :: (Traversable t, Fractional f)
> => t f -> t f
> avg_diff t =
> let start = (0,0.0)
> step (n,s) x mean = ((n+1, s+x), x - mean)
> ((n,s), diffs) = traverseAccumL step start t (s/n)
> in diffs
The dual definition traverseAccumR is almost the same. The only
difference lies in the definition of <*>
StateRT kf <*> StateRT kv = StateRT kfv
where kfv s = do (s', v) <- kv s
(s'', f) <- kf s'
return (s'', f v)
> -- right-to-left state transformer
> newtype StateRT m s a = StateRT { runStateRT :: s -> m (s, a) }
>
> instance Functor f => Functor (StateRT f s) where
> fmap f (StateRT k) = StateRT $ \ s -> fmap f <$> k s
>
> instance (Functor m, Monad m) => Applicative (StateRT m s) where
> pure x = StateRT (\ s -> return (s, x))
> StateRT kf <*> StateRT kv = StateRT kfv
> where kfv s = do (s', v) <- kv s
> (s'', f) <- kf s'
> return (s'', f v)
>
> traverseAccumR :: ( Traversable t, Functor m, Monad m)
> => (a -> b -> m (a, c)) -> a -> t b -> m (a, t c)
> traverseAccumR f s t = runStateRT (traverse (StateRT . flip f) t) s
-- Florent B.
More information about the Haskell-Cafe
mailing list