The approach below is not always the easiest or fastest, but it is relatively systematic.
Strictly speaking, you are looking for type
\g -> (\ x -> let ys = (++) [x] (filter (curry gx) ys) in ys)
( let and where equivalent, but sometimes it's a little easier to reason with let ), given the types
filter :: (a -> Bool) -> [a] -> [a] curry :: ((a, b) -> c) -> a -> b -> c
Do not forget that you also use
(++) :: [a] -> [a] -> [a]
First, consider the โdeepestโ part of the syntax tree:
curry gx
We have g and x , which we have not yet seen, so suppose they are of some type:
g :: t1 x :: t2
We also have curry . At each point where these functions occur, variables of type ( a , b , c ) can be specialized in different ways, so it is recommended that you replace them with a new name each time you use these functions. At this point, curry is of the following type:
curry :: ((a1, b1) -> c1) -> a1 -> b1 -> c1
We can then say only curry gx if the following types can be unified:
t1 ~ ((a1, b1) -> c1) -- because we apply curry to g t2 ~ a1 -- because we apply (curry g) to x
Then it can also be safely assumed that
g :: ((a1, b1) -> c1) x :: a1 --- curry gx :: b1 -> c1
Continue:
filter (curry gx) ys
We see ys for the first time, so let's save it to ys :: t3 . We also need to create a filter instance. So, at the moment, we know
filter :: (a2 -> Bool) -> [a2] -> [a2] ys :: t3
Now we must match the filter argument types:
b1 -> c1 ~ a2 -> Bool t3 ~ [a2]
The first restriction can be broken down into
b1 ~ a2 c1 ~ Bool
Now we know the following:
g :: ((a1, a2) -> Bool) x :: a1 ys :: [a2] --- filter (curry gx) ys :: [a2]
Let's continue.
(++) [x] (filter (curry gx) ys)
I will not spend too much time explaining [x] :: [a1] by looking at type (++) :
(++) :: [a3] -> [a3] -> [a3]
We get the following restrictions:
[a1] ~ [a3] -- [x] [a2] ~ [a3] -- filter (curry gx) ys
Since these restrictions can be reduced to
a1 ~ a3 a2 ~ a2
we just call it all a a1 :
g :: ((a1, a1) -> Bool) x :: a1 ys :: [a1] --- (++) [x] (filter (curry gx) ys) :: [a1]
For the time being, I will ignore that ys ' type is generalized and focuses on
\x -> let { {- ... -} } in ys
We know what type we need for x , and we know the type of ys , so we now know
g :: ((a1, a1) -> Bool) ys :: [a1] --- (\x -> let { {- ... -} } in ys) :: a1 -> [a1]
Similarly, we can conclude that
(\g -> (\x -> let { {- ... -} } in ys)) :: ((a1, a1) -> Bool) -> a1 -> [a1]
At this point, you only need to rename (in fact, generalize, because you want to bind it to fun ) type variables, and you have your own answer.