I'm learning Haskell. I defined the following function (I know I don't need addToList
and I can also do Point-free notation I'm just in the process of playing with language concepts):
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = addToList (f x) map f xs
where
addToList :: a -> [a] -> [a]
addToList x [] = [x]
addToList x xs = x:xs
This produces a compile error:
with actual type `(a0 -> b0) -> [a0] -> [b0]'
Relevant bindings include
f :: a -> b (bound at PlayGround.hs:12:5)
map :: (a -> b) -> [a] -> [b] (bound at PlayGround.hs:11:1)
Probable cause: `map' is applied to too few arguments
In the second argument of `addToList', namely `map'
In the expression: addToList (f x) map f xs
If I put parantheses around map it works:
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = addToList (f x) (map f xs)
where
addToList :: a -> [a] -> [a]
addToList x [] = [x]
addToList x xs = x:xs
I understand that function application binds more tightly than operators (as discussed in Haskell - too few arguments), however, I don't understand how the compiler would parse the above differently without the parantheses.
Parsing is a distinct step that is completed before type checking occurs. The expression
addToList (f x) map f xs
has as much meaning to the parser as s1 (s2 s3) s4 s2 s5
has to you. It doesn't know anything about what the names mean. It takes the lexical structure of the string and turns it into a parse tree like
*5
/ \
/ xs
*4
/ \
/ f
*3
/ \
/ map
*2
/ \
addToList *1
/ \
f x
Once the parse tree is complete, then each node is tagged with its type, and type checking can occur. Since function application is denoted simply by juxtaposition, the type checker knows that the left child of a node is a function, the right child is the argument, and the root is the result.
The type checker can proceed roughly as follows, doing an pre-order traversal of the tree. (I'll alter the type signatures slightly to distinguish unrelated type variables until they are unified.)
addToList :: a -> [a] -> [a]
, so it takes an argument of type a
and returns a function of type [a] -> [a]
. The value of a
is not yet known.f :: b -> c
, so it takes an argument of type b
and returns a value of type c
. The values of b
and c
are not yet known.x
has type d
. The value of d
is not yet known.b ~ d
, f
can be applied to x
, so *1 :: c
a ~ c
, addToList
is applied to *1
, so *2 :: [a] -> [a]
*2
expects an argument of type [a]
, but it is being applied to map :: (e -> f) -> [e] -> [f]
. The type checker does not know how to unify a list type and a function type, which produces the error you see.