Let's say we have these 2 functions
f1 :: Int -> [Int] -> Int -> [Int]
f1 _ [] _ = []
f1 x (h:t) y = (x * h y):(f1 x t y)
f2 :: [Int] -> Int
f2 (h:t) = h
Why does (f2 . f1 1 [1..10]) 1 work, but (f2 . f1 1) [1..10] 1 doesn't work?
CodePudding user response:
All functions in Haskell take exactly one argument and return one value. The argument and/or the return type could be another function.
f1 has an argument type of Int and a return type of [Int] -> Int -> [Int]. The right associativity of (->) means we don't have to explicitly write this as
f1 :: Int -> ([Int] -> (Int -> [Int]))
but can instead drop the parentheses.
(Yes, (->) is an operator. You can use :k in GHCi to see the kind, but unfortunately what you get back is more complicated than we want to explain here:
> :k (->)
(->) :: TYPE q -> TYPE r -> *
Don't worry about what TYPE q and TYPE r stand for. Suffice it to say that (->) takes two types and returns a new type, and we can assume a simpler kind like
(->) :: * -> * -> *
The kind * is the kind of an ordinary type, more frequently written as Type these days.
)
In order to compose two functions, the return type of one must match the argument type of the other. We can see this from the type of (.) itself:
(.) :: (b -> c) -> (a -> b) -> a -> c
^ ^
That's not the case with f1 and f2:
-- vvvvvvvvvvvvvvvvvvvvv
f1 :: Int -> ([Int] -> Int -> [Int])
f2 :: [Int] -> Int
-- ^^^^^
-- [Int] -> Int -> [Int] and [Int] are different types
nor with f1 1 and f2
-- vvvvvvvvvvvv
f1 1 :: [Int] -> (Int -> [Int])
f2 :: [Int] -> Int
-- ^^^^^
-- Int -> [Int] and [Int] are different types
but it is true of f1 1 [1..10] and f2:
-- vvvvv
f1 1 [1..10] :: Int -> [Int]
f2 :: [Int] -> Int
-- ^^^^^
-- [Int] and [Int] are the same type
While (->) is right-associative, function application is left-associative, which is why we can write
((f1 1) [1..10]) 1
as f1 1 [1..10] 1 instead, leading to the appearance of f1 as taking 3 arguments, rather than an expression involving 3 separate function calls. You can see the three calls more clearly if you use a let expression to name each intermediate function explicitly:
let t1 = f1 1
t2 = t1 [1..10]
t3 = t2 1
in t3
