I need to understand how types works and can be interpreted.
For example, if we take map function we have map :: (a -> b) -> [a] -> [b]
Well, how do I interpret this?
I need to understand how types works and can be interpreted.
For example, if we take map function we have map :: (a -> b) -> [a] -> [b]
Well, how do I interpret this?
->
is a type constructor for the type of functions. It's a right-associative infix operator, meaning it's grouped together from the right. This means that we can rewrite the type by adding explicit grouping for the functions to the right side.
map :: (a -> b) -> [a] -> [b]
map :: (a -> b) -> ([a] -> [b])
An infix expression for an operator *
applied to two arguments, x
and y
, x * y
can be written in prefix notation as (*) a b
. We can rewrite the preceding type, starting with the outermost ->
, which is the one in the middle.
map :: (->) (a -> b) ([a] -> [b])
And we can now translate the last type into English
map :: (->) (a -> b) ([a] -> [b])
map is a function that takes a "(a -> b)" and returns a "([a] -> [b])"
Where we interpret a -> b ~ (->) a b
(here ~
means the types are equivalent) as
(->) a b
function that takes an "a" and return a "b"
And interpret [a] -> [b] ~ (->) [a] [b]
as
(->) [ a ] [ b ]
function that takes a list of "a"s and returns a list of "b"s
We say "a function from a
to b
" as shorthand for "a function that takes an a
and returns a b
"
The a
s and b
s in the type signature are type variables, they can take on any type, which we call polymorphism. Occasionally, you will see this written explicitly in Haskell as forall
So, in all we could say:
map
is a polymorphic value for all types a
and b
which is a function that:
a
to b
anda
s to a list of b
s. The fact that this signature contains ->
tells us it's a function. Whatever comes after the last ->
is the return type of the function once fully applied. Let's look at the individual pieces.
(a -> b)
This is the first argument, and it, too is a function. This means that map
is a higher-order-function -- it takes a function as one of its arguments. a -> b
itself is a function that transforms some value of type a
into some value of type b
.
[a]
The second argument. The square brackets is special syntax that denotes list. This argument, therefore, is a list with elements of type a
.
[b]
The type of the result. Again, a list, but this time with elements of type b
.
We can try to reason about this now. Given a function a -> b
and a list of a
, map
seems to be (it really is) a function that transforms that list of a
s into a list of b
s.
Here's an example: map (*2) [1,2,3]
. In this case, a
is Integer
(or some other integer type) and each element is doubled. b
, too, is Integer
, because (*2)
assumes the same return type, so in the case the type variables a
and b
are the same. This need not be the case; we could have a different function instead of (*2)
, say show
which would have produced a b
distinct from a
, namely String
.
Try them out in ghci. You can type in map show [1,2,3]
directly and see the result. You can query the type of the expression by prepending :t to that line.
To learn more, you should look up one of the marvelous starter resources. LYAH has an entire chapter dedicated to the basic understanding of types, and is definitely worth a read!