Okay, let's look at one of the Haskellbook end-of-chapter exercises from chapter 10.
myAny :: (a -> Bool) -> [a] -> Bool
myAny f xs = foldr (||) False $ map f xs
myAny f = foldr (||) False . map f
myAny = (foldr (||) False .) . map
First one's pretty obvious, right? But we want to go pointfree. (.)
associates to the right, so in the second myAny
, (. map f)
is the
last arg to the foldr
. Let's make sure the types line up:
foldr (||) False
is a [Bool] -> Bool
-- makes sense, feed it a list
and it'll poop out a Bool.
map f
in this case has type [a] -> [Bool]
-- sure, it'll run your
predicate f
on all the things.
(. map f)
has type ([Bool] -> c) -> [a] -> c
, though, and whoa
what's going on.
Okay, break it down, this wasn't trivial for haskell tweeps so let's check it
out. (.)
has type (b -> c) -> (a -> b) -> a -> c
, it's function
composition, g . f
. So the LHS of (. foo)
is going to be a thing
that can consume whatever foo
produces. In this case, it's a thing
that can consume a [Bool]
.
Now I get it; . map f
is constrained to produce a list of Bool
s, and
the type declaration for myAny
(or foldr, really) tells us that it has
to produce a Bool
. This is where I'd probably stop if I was writing
production code.
Now let's look at that last one. Again, (.)
associates to the right,
so again we'll look at (. map)
and figure out how to plumb it in. Wow,
that was not what I expected:
(. map) :: (([a] -> [b]) -> c) -> (a -> b) -> c
What the fuck. Okay, makes sense with currying. map
takes a function
a -> b
and spits out a function that will transform your list of a
s
to a list of b
s. So we want to glue this together with foldr (||) False
, which is a [Bool] -> Bool
How do I get from there to my
desired lhs type (([a] -> [Bool]) -> bool)
? Well, (.)
seems to be
pretty good at gluing stuff onto the lhs type. So:
(foldr (||) False .) :: (a -> [Bool]) -> a -> Bool
(Okay, it's on Foldable
, but lists for now.) That says "give me a way
to make a list of Bool
s from a thing, and also a thing, and I'll give
you a Bool
. And hey! It also looks a lot like the LHS to (. map)
if
you let that signature's c
resolve to [a]
. And indeed:
(foldr (||) False .) . map :: (a -> Bool) -> [a] -> Bool
So that's fun and all, and probably worth noodling around with for a better understanding of composition, but waaaaaay too cute to check into a repository.