I'm trying to understand why the type of fun g x = ys where ys = [x] ++ filter (curry g x) ys
is ((a, a) -> Bool) -> a -> [a]
.
I understand that:
filter :: (a -> Bool) -> [a] -> [a]
and that curry :: ((a, b) -> c) -> a -> b -> c
But I don't understand how to continue.
The approach below is not necessarily the easiest or fastest, but it's relatively systematic.
Strictly speaking, you're looking for the type of
(
let
andwhere
are equivalent, but it's sometimes a little easier to reason usinglet
), given the typesDon't forget that you're also using
Let's first look at the 'deepest' part of the syntax tree:
We have
g
andx
, of which we haven't seen before yet, so we'll assume that they have some type:We also have
curry
. At every point where these functions occur, the type variables (a
,b
,c
) can be specialized differently, so it's a good idea to replace them with a fresh name every time you use these functions. At this point,curry
has the following type:We can then only say
curry g x
if the following types can be unified:It's then also safe to assume that
Let's continue:
We see
ys
for the first time, so let's keep it atys :: t3
for now. We also have to instantiatefilter
. So at this point, we knowNow we must match the types of
filter
's arguments:The first constraint can be broken down to
We now know the following:
Let's continue.
I won't spend too much time on explaining
[x] :: [a1]
, let's see the type of(++)
:We get the following constraints:
Since these constraints can be reduced to
we'll just call all these
a
'sa1
:For now, I'll ignore that
ys
' type gets generalized, and focus onWe know what type we need for
x
, and we know the type ofys
, so we now knowIn a similar fashion, we can conclude that
At this point, you only have to rename (actually, generalize, because you want to bind it to
fun
) the type variables and you have your answer.We can derive types in Haskell in a more or less mechanical manner, using the general scheme of
which means that e.g.
entails
etc. With these simple tricks type derivation will become trivial for you. Here, with
we simply write down the stuff carefully lined up, processing it in a top-down manner, consistently renaming and substituting the type variables, and recording the type equivalences on the side:
so we have that
a ~ ((b,b) -> Bool)
andc ~ [b]
, and thuswhich is the same as
((a,a) -> Bool) -> a -> [a]
, up to a consistent renaming of type variables.