In Haskell, we can use this useful idiom to get from a list that of indexed elements in it:
indexify :: (Num i) => [a] -> [(i,a)]
indexify = zip [0..]
However, according to the implementation of zip in GHC.List as of base-4.9.1.0, this will not completely perform list fusion, i.e. this will not actually generate the list [0..], but the argument list to indexify will be constructed.
Of course, there is a definition that allows appropriate list fusion:
indexify' :: (Num i) => [a] -> [(i,a)]
indexify' xs = build $ \c n ->
foldr (\x r !i -> (i,x) `c` r (i+1)) (const n) xs 0
Do we need to import GHC.Prim (build) to do this? Or is there another implementation that simplifies to indexify'?
This already exists in the ilist package, as indexed. The relevant source code snippets are
import GHC.Exts -- exports `build`
indexed :: [a] -> [(Int, a)]
indexed xs = go 0# xs
where
go i (a:as) = (I# i, a) : go (i +# 1#) as
go _ _ = []
{-# NOINLINE [1] indexed #-}
indexedFB :: ((Int, a) -> t -> t) -> a -> (Int# -> t) -> Int# -> t
indexedFB c = \x cont i -> (I# i, x) `c` cont (i +# 1#)
{-# INLINE [0] indexedFB #-}
{-# RULES
"indexed" [~1] forall xs. indexed xs = build (\c n -> foldr (indexedFB c) (\_ -> n) xs 0#)
"indexedList" [1] forall xs. foldr (indexedFB (:)) (\_ -> []) xs 0# = indexed xs
#-}
As you'll probably notice, the rewrite rule makes use of pretty much the same definition you have, so that probably is the best way to do it. Also, GHC.Exts also exports build, so you don't need to import GHC.Prim.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With