Sunday, March 29, 2009

anaMap and accumMapL

There has been a very long thread recently on Haskell-Cafe -
- 'about Haskell code written to be "too smart"':
http://www.haskell.org/pipermail/haskell-cafe/2009-March/058475.html

As the thread was running the idea of partitions (aka takeList) seemed like an unfold to me, though I couldn't work out a nice formulation. Here is the best 'natural' unfold I could get. It's messier than a usual anamorphism because the state has to be doubled - the state is both the list of lengths and the remaining stream:


anapart :: [Int] -> [a] -> [[a]]
anapart = (unfoldr phi .) . (,) where
phi ([],_) = Nothing
phi (i:is,xs) = let (l,r) = splitAt i xs in Just (l,(is,r))


Prompted by the suggesting that mapAccumL was the natural way to do it, Claus Reinke noted that that the arguments to mapAccumL were the wrong way round:
http://www.haskell.org/pipermail/haskell-cafe/2009-March/058689.html


Here is a version accumMapL which puts the state second in the result tuple - which is the way unfoldr (anamorphism) does it:


accumMapL :: (a -> st -> (b, st)) -> st -> [a] -> ([b], st)
accumMapL _ s [] = ([],s)
accumMapL f s (x:xs) = (y:ys,s'')
where (y, s') = f x s
(ys,s'') = accumMapL f s' xs



Since mapAccumL (accumMapL) is like a fusion of map and foldl, it made me wonder what a a combination of map and unfoldr would give me as I was still keen on my anamorphism, maybe anaMap isn't the best name, but I think this function is the analogue:


-- anaMap is to unfoldr, what accumMapL is to foldl
-- Note - we can signal exhaustion early by the Maybe type.

anaMap :: (a -> st -> Maybe (b,st)) -> st -> [a] -> ([b],st)
anaMap f s [] = ([],s)
anaMap f s (x:xs) = case (f x s) of
Nothing -> ([],s)
Just (a,st) -> (a:as,st') where (as,st') = anaMap f st xs


And here is takeList:


takeListAM :: [Int] -> [t] -> [[t]]
takeListAM ns xs = fst $ anaMap fn xs ns where
fn _ [] = Nothing
fn i ys = Just $ splitAt i ys


Finally here's the GHC transcript, it passes the Hartman test:


*Main> main
[[1,2,3],[4,5,6,7,8,9,10]]
[[1,2,3],[4,5,6,7,8,9,10],[11,12,13,14,15,16,17,18,19,20,21],[22,23,24,25,26,27,28,29,30,31,32,33,34
,35,36]]
1000000
*Main> null $ takeListAM [1] undefined
*** Exception: Prelude.undefined
*Main> takeListAM [0] []
[]
*Main>


By the way, I do like the 'specs' (aka glasses) combinator to compose a binary function and a ternary one. The function anapart above, used the double composition idiom which now seems very common - here's the line in question:


anapart = (unfoldr phi .) . (,) where
...


Here is is with 'specs':


anapart = unfoldr phi `oo` (,) where
...


Specs has a nod to ML which uses letter o for function composition, plus the '2 o's' seem like a nice visual pun to suggest doubling going on in the composition:


oo :: (c -> d) -> (a -> b -> c) -> a -> b -> d
oo f g = (f .) . g


Of course higher arities can also be accommodated:


ooo :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
ooo f g = ((f .) .) . g


etc.

Blog Archive

About Me

My photo
Disambiguating biog as there are a few Stephen Tetley's in the world. I'm neither a cage fighter or yachtsman. I studied Fine Art in the nineties (foundation Bradford 1992, degree Cheltenham 1992 - 95) then Computing part-time at Leeds Met graduating in 2003. I'm the Stephen Tetley on Haskell Cafe and Stackoverflow.