Just keep expanding:
pp1 [1, 2, 3, 4] = zip /:/ xnull tail $ [1, 2, 3, 4]
-- inline (/:/) f g x = f x (g x)
-- f = zip, g = xnull tail, x = [1, 2, 3, 4]
-- therefore:
= zip [1, 2, 3, 4] (xnull tail $ [1, 2, 3, 4])
-- inline the definition of xnull and note that the argument is not null
-- so we just want f x, or tail [1, 2, 3, 4]
= zip [1, 2, 3, 4] (tail [1, 2, 3, 4])
-- evaluate tail
= zip [1, 2, 3, 4] [2, 3, 4]
-- evaluate zip
= [(1, 2), (2, 3), (3, 4)]
Operator presidence matters. You didn't specify the associativity of (/:/)
so it was defaulted to be relatively weak. Therefore, (xnull tail)
bound tighter than (/:/)
.
Also, as a side note, (/:/)
already exists in the standard library as (<*>)
from Control.Applicative
. It's sufficiently general so this might be difficult to see, but the Applicative
instance for Reader
(or perhaps better understood as the function Applicative
) provides this exact instance. It's also known as ap
from Control.Monad
.
zip <*> tail :: [b] -> [(b, b)]
zip <*> tail $ [1, 2, 3, 4] = [(1, 2), (2, 3), (3, 4)]