Given ist the Haskell function:
head . filter fst
The question is now how to find the type \"manually\" by hand. If I let Haskell tell me t
Let's start with (.)
. It's type signature is
(.) :: (b -> c) -> (a -> b) -> a -> c
which says
"given a function from b
to c
, and a function from a
to b
,
and an a
, I can give you a b
". We want to use that with head
and
filter fst
, so`:
(.) :: (b -> c) -> (a -> b) -> a -> c
^^^^^^^^ ^^^^^^^^
head filter fst
Now head
, which is a function from an array of something to a
single something. So now we know that b
is going to be an array,
and c
is going to be an element of that array. So for the purpose of
our expression, we can think of (.)
as having the signature:
(.) :: ([d] -> d) -> (a -> [d]) -> a -> d -- Equation (1)
^^^^^^^^^^
filter fst
The signature for filter
is:
filter :: (e -> Bool) -> [e] -> [e] -- Equation (2)
^^^^^^^^^^^
fst
(Note that I've changed the name of the type variable to avoid confusion
with the a
s
that we already have!) This says "Given a function from e
to a Bool,
and a list of e
s, I can give you a list of e
s". The function fst
has the signature:
fst :: (f, g) -> f
says, "given a pair containing an f
and a g
, I can give you an f
".
Comparing this with Equation 2, we know that
e
is going to be a pair of values, the first element of
which must be a Bool
. So in our expression, we can think of filter
as having the signature:
filter :: ((Bool, g) -> Bool) -> [(Bool, g)] -> [(Bool, g)]
(All I've done here is to replace e
with (Bool, g)
in Equation 2.)
And the expression filter fst
has the type:
filter fst :: [(Bool, g)] -> [(Bool, g)]
Going back to Equation 1, we can see that (a -> [d])
must now be
[(Bool, g)] -> [(Bool, g)]
, so a
must be [(Bool, g)]
and d
must be (Bool, g)
. So in our expression, we can think of (.)
as
having the signature:
(.) :: ([(Bool, g)] -> (Bool, g)) -> ([(Bool, g)] -> [(Bool, g)]) -> [(Bool, g)] -> (Bool, g)
To summarise:
(.) :: ([(Bool, g)] -> (Bool, g)) -> ([(Bool, g)] -> [(Bool, g)]) -> [(Bool, g)] -> (Bool, g)
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
head filter fst
head :: [(Bool, g)] -> (Bool, g)
filter fst :: [(Bool, g)] -> [(Bool, g)]
Putting it all together:
head . filter fst :: [(Bool, g)] -> (Bool, g)
Which is equivalent to what you had, except that I've used g
as the type variable rather than b
.
This probably all sounds very complicated, because I described it in gory detail. However, this sort of reasoning quickly becomes second nature and you can do it in your head.