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
\g -> (\ x -> let ys = (++) [x] (filter (curry g x) ys) in ys)
(let and where are equivalent, but it's sometimes a little easier to reason using let), given the types
filter :: (a -> Bool) -> [a] -> [a]
curry :: ((a, b) -> c) -> a -> b -> c
Don't forget that you're also using
(++) :: [a] -> [a] -> [a]
Let's first look at the 'deepest' part of the syntax tree:
curry g x
We have g and x, of which we haven't seen before yet, so we'll assume that they have some type:
g :: t1
x :: t2
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:
curry :: ((a1, b1) -> c1) -> a1 -> b1 -> c1
We can then only say curry g x 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
It's then also safe to assume that
g :: ((a1, b1) -> c1)
x :: a1
---
curry g x :: b1 -> c1
Let's continue:
filter (curry g x) ys
We see ys for the first time, so let's keep it at ys :: t3 for now. We also have to instantiate filter. So at this point, we know
filter :: (a2 -> Bool) -> [a2] -> [a2]
ys :: t3
Now we must match the types of filter's arguments:
b1 -> c1 ~ a2 -> Bool
t3 ~ [a2]
The first constraint can be broken down to
b1 ~ a2
c1 ~ Bool
We now know the following:
g :: ((a1, a2) -> Bool)
x :: a1
ys :: [a2]
---
filter (curry g x) ys :: [a2]
Let's continue.
(++) [x] (filter (curry g x) ys)
I won't spend too much time on explaining [x] :: [a1], let's see the type of (++):
(++) :: [a3] -> [a3] -> [a3]
We get the following constraints:
[a1] ~ [a3] -- [x]
[a2] ~ [a3] -- filter (curry g x) ys
Since these constraints can be reduced to
a1 ~ a3
a2 ~ a2
we'll just call all these a's a1:
g :: ((a1, a1) -> Bool)
x :: a1
ys :: [a1]
---
(++) [x] (filter (curry g x) ys) :: [a1]
For now, I'll ignore that ys' type gets generalized, and focus 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]
In a similar fashion, we can conclude that
(\g -> (\x -> let { {- ... -} } in ys)) :: ((a1, a1) -> Bool) -> a1 -> [a1]
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
foo x = y -- is the same as
foo = \x -> y -- so the types are
foo :: a -> b , x :: a , y :: b -- so that
foo x :: b
which means that e.g.
f x y z :: d , x :: a , y :: b, z :: c
entails
f x y :: c -> d
f x :: b -> c -> d
f :: a -> b -> c -> d
etc. With these simple tricks type derivation will become trivial for you. Here, with
filter :: (a -> Bool) -> [a] -> [a]
curry :: ((a, b) -> c) -> a -> b -> c
(++) :: [a] -> [a] -> [a]
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:
fun g x = ys where ys = [x] ++ filter (curry g x) ys
fun g x :: c , ys :: c
fun g :: b -> c , x :: b
fun :: a -> b -> c , g :: a
ys = [x] ++ filter (curry g x) ys c ~ c (++) [x] (filter (curry g x) ys) :: c (++) :: [a1] -> [a1] -> [a1] ----------------------------------------------- (++) :: [b] -> [b] -> [b] , a1 ~ b , c ~ [b] filter (curry g x ) ys :: [b] filter :: (a2 -> Bool) -> [a2] -> [a2] , a2 ~ b -------------------------------------- filter :: (b -> Bool) -> [b] -> [b] curry g x :: b -> Bool curry :: ((a3, b) -> c3 ) -> a3 -> b -> c3 , c3 ~ Bool , a3 ~ b ------------------------------------------- curry :: ((b , b) -> Bool) -> b -> b -> Bool , a ~ ((b,b) -> Bool)
so we have that a ~ ((b,b) -> Bool) and c ~ [b], and thus
fun :: a -> b -> c
fun :: ((b,b) -> Bool) -> b -> [b]
which is the same as ((a,a) -> Bool) -> a -> [a], up to a consistent renaming of type variables.
来源:https://stackoverflow.com/questions/23315823/type-of-fun-g-x-ys-where-ys-x-filter-curry-g-x-ys