Haskell snippet - getPrefixMap
Here's a little snippet I worked on yesterday, while preparing my talk for the London Perl Workshop.
It takes a list of tuples ("string", whatever) and maps all the prefixes of the string
("string", "strin", "stri", etc.) to the whatever.
I was quite impressed at how easily this came together. The functional composition (pipelines connected with ".") is coming a bit more easily, but the heavy lifting is all done really by the "inits" function from Data.List.
Some of this is made "prettier" (but harder to understand) by various features. "uncurry" changes a function that takes a tuple (a,b) into one that takes 2 arguments "a b". The (flip (,) r) is actually harder to read than the equivalent lambda, (\s -> (s,r)). (Annoyingly, you can't section the comma operator to do "(,r)" because tuples are "special"...)
--
import Data.List
import qualified Data.Map as M
-- can contain duplicates!
getPrefixes :: [([a], t)] -> [([a], t)]
getPrefixes = concatMap (uncurry gps)
where gps s r = map (flip (,) r) -- make a tuple (s',r)
. reverse -- shortest prefixes last
. tail -- ignore the empty prefix ""
. inits -- get all the prefixes
$ s
getPrefixMap :: (Ord a) => [([a], t)] -> M.Map [a] t
getPrefixMap = M.fromList . getPrefixes
-- without the type, we'd have to write like so (because of the
-- monomorphism restriction)
-- > getPrefixMap xs = M.fromList $ getPrefixes xs
Update: after Joao's comments below, I got the version
> getPrefixes :: [([a], t)] -> [([a], t)]
> getPrefixes = concatMap gps
> where gps (s, r) = let ss = tail . inits $ s
> rs = repeat $ r
> in zip ss rs
Joao meanwhile mentioned the cross product, which I didn't
immediately get the point of.
> infix 1 ><
> (><) f g a = (f a, g a)
As you can see, this applies the remaining parameter "a"
to two functions. So it's useful in this case to curry
the "(s,r)" parameter. doserj pointed out that this is
spelt (&&&) so that we just need to:
> import Control.Arrow
> getPrefixes = concatMap $ uncurry zip .
> (tail . inits . fst &&& repeat . snd)
As often happens in Haskell you could argue whether this is
actually any clearer, but it's cute!

Comments
infix 1 ><
(><) f g a = (f a, g a)
getPrefixes1 :: [([a], t)] -> [([a], t)]
getPrefixes1 = concatMap ((uncurry zip) . (tail.inits.fst >< slist))
where slist (f,s) = replicate (length f) s
I am not sure if the categorical product (><) is defined in Haskell, but, even if it isn't, it is very simple.
Good luck with the talk,
Joao
Good point about zip! I could just use (repeat s) along with that, will have a play with it.
Third version:
getPrefixes = concatMap ((uncurry zip) . (tail.inits.fst >< repeat.snd))
And it is done :)
Thanks, I've updated the main post.
If you are interested, more details in page 12 of:
http://www.di.uminho.pt/~jno/ps/pdbc02_new.pdf
Joao
f *** g = \(x,y) -> (f x, g y)
i.e., it applies f and g to a pair "in parallel".
So, you can write
getPrefixes = concatMap $ uncurry zip . (tail . inits *** repeat)
even shorter! =)