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