foo = foldl $ flip ($)
This piece of Haskell code lies on my homepage. I no longer program in Haskell, but I can still figure out what this does (though I did have to reïnstall ghc to check).
foo = foldl $ flip ($)
is just an obtuse way of saying foo = foldl (flip ($))
. Now
foldl
's signature is:
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
Or, more legibly, it takes:
- A function that takes two arguments of type
b
anda
, then returns something of typeb
. - A "seed value" of type
b
. - A foldable thing
t
ofa
s. This can be, for instance, a list.
Now typing
foldl (+) 44 [5, 8, 3, 5]
into ghci
outputs 65
– that is, ((((44 + 5) + 8) + 3) + 5).
But in the code example on my homepage, foldl
gets passed in too few arguments. That's perfectly legal,
since technically speaking, all functions take only one parameter. In our case, the other two arguments to
foldl
can be passed into foo
to get the value we want. Let's rewrite the definition to
include explicit parameters:
foo seed entries = foldl (flip ($)) seed entries
And speaking of which, let's rewrite the first argument passed to foldl
. $
is an operator
such that f $ x = f x
(and ($)
is just Haskell's way of expressing that operator as a
function). flip
's signature is (a -> b -> c) -> b -> a -> c
, and you can think of it as
taking three parameters, but here it's more useful to think of it as taking a function with two parameters and
returning another function that takes in those parameters in the other order.
foo seed entries = foldl (\x f -> f x) seed entries
Here, we can see that if seed
has a type of b
, then (\x f -> f x)
takes in
two arguments of b
and b -> c
, respectively, and spits out a c
. Because of the
signature of foldl
, b
and c
have to refer to the same type. Then a
in the signature shown earlier is b -> b
. seed
is also a b
, and
entries
is some foldable thing (let's go with lists for now) of functions b -> b
. You can
verify this in ghci
.
So what does this code do? The function returns the result from applying each element of entries
successively on seed
, so, for instance, foo x [f1, f2, f3] = f3 (f2 (f1 x))
. For a concrete
example:
foo 5.2 [\a -> sqrt a, \a -> 35 + a, \a -> cos (2.6 * a)] = (\a -> cos (2.6 * a)) ((\a -> 35 + a) ((\a -> sqrt a) 5.2)) = (\a -> cos (2.6 * a)) ((\a -> 35 + a) (sqrt 5.2)) = (\a -> cos (2.6 * a)) (35 + (sqrt 5.2)) = cos (2.6 * (35 + (sqrt 5.2))) = -0.8958481640418573
I hate Haskell.