The following ideas come from The Beautiful Folding .
When you have two folding operations on a list, you can always perform them right away, folding, saving both states. Let me put it in Haskell. First we need to fix what a bending operation is:
{-
The folding operation has a folding function, an initial value and a function that gives a result from the final state. Using existential quantification, we can hide the type of state that is needed to combine folds with different states.
Applying Foldr to a list is just calling Foldr with the appropriate arguments:
fold :: Foldr ab -> [a] -> b fold (Foldr fsg) = g . foldr fs
Naturally, Foldr is a functor, we can always add a function to the final one:
instance Functor (Foldr a) where fmap f (Foldr ksr) = Foldr ks (f . r)
More interestingly, it is also Applicative functor. The implementation of pure is simple, we just return the given value and do not reset anything. The most interesting part is <*> . He creates a new fold that holds the states as giving folds, and at the end, combines the results.
instance Applicative (Foldr a) where pure x = Foldr (\_ _ -> ()) () (\_ -> x) (Foldr f1 s1 r1) <*> (Foldr f2 s2 r2) = Foldr foldPair (s1, s2) finishPair where foldPair a ~(x1, x2) = (f1 a x1, f2 a x2) finishPair ~(x1, x2) = r1 x1 (r2 x2) f *> g = g f <* g = f
Note (as to the left) that we have a lazy pattern matching ~ with tuples. This ensures that <*> is lazy enough.
Now we can express map as Foldr :
fromMap :: (a -> b) -> Foldr a [b] fromMap f = Foldr (\x xs -> fx : xs) [] id
This makes unzip easy. We just combine the two cards using fst and the other snd :
unzip' :: Foldr (a, b) ([a], [b]) unzip' = (,) <$> fromMap fst <*> fromMap snd unzip :: [(a, b)] -> ([a], [b]) unzip = fold unzip'
We can verify that it processes the input only once (and lazily): Both
head . snd $ unzip (repeat (3,'a')) head . fst $ unzip (repeat (3,'a'))
enter the correct result.