Reputation: 9708
I am reading Learn you a Haskell for great good and on page 40 - as-patterns.
I have changed the example slightly to be:
firstLetter :: String -> String
firstLetter "" = "Empty string, oops"
firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x] ++ " otherbit " ++ xs
Then can use like this:
*Main> firstLetter "Qwerty"
"The first letter of Qwerty is Q otherbit werty"
But I was confused about the difference between [x] and x and why I have to use [x] in the above example.
For example if I change to
firstLetter :: String -> String
firstLetter "" = "Empty string, oops"
firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ x ++ " otherbit " ++ xs
I get error:
Couldn't match expected type `[Char]' with actual type `Char'
In the first argument of `(++)', namely `x'
In the second argument of `(++)', namely `x ++ " otherbit " ++ xs'
In the second argument of `(++)', namely
`" is " ++ x ++ " otherbit " ++ xs'
I can use xs
to print "werty"
but have to use [x]
to print "Q". Why is that?
What does [x]
mean?
In the (x:xs
), :
just delimits each element, so x
is the first element. Why can I not print by using x
?
Also xs
is of what type? list of values? So does this mean x
is an element and xs
must be of type list?
Upvotes: 2
Views: 268
Reputation: 167
One could also note that in many programming languages the concatenation operator would either be overloaded so that there are several implementations of it depending on the types on both sides, or the arguments would be cast to strings. This is not the case in Haskell.
Upvotes: 1
Reputation: 2862
[x] means "a list containing only the element x".
As a String in Haskell is a list of characters, if x is the character 'Q' then [x] is the string "Q". The ++ operator expects to receive two strings as arguments, so you can't give it only a character.
That's what the compiler is telling you: Couldn't match expected type `[Char]' with actual type `Char'
means that the compiler was expecting an argument of type [Char] (which is a list of character, the same as String) but you are passing it a Char.
Upvotes: 3
Reputation: 7475
++
is for concatenating lists:
(++) :: [a] -> [a] -> [a]
[x]
is a list, x
is not a list.
firstLetter (x:xs)
is an example of pattern matching.
(:) :: a -> [a] -> [a]
This operator adds element before list.
Therefore type of x
is element and type of xs
is list of elements.
It is common in Haskell to add suffix 's' to lists' names.
Upvotes: 6
Reputation: 47062
String
is defined as type String = [Char]
.
"Qwerty"
is shorthand for ['Q', 'w', 'e', 'r', 't', 'y']
; this in turn is shorthand for 'Q' : 'w' : 'e' : 'r' : 't' : 'y' : []
.
So when you match x : xs
against "Qwerty"
, you get x = 'Q'
and xs = "werty"
.
x : xs
('Q') : ('w' : 'e' : 'r' : 't' : 'y' : [])
Note: x = 'Q'
, NOT x = "Q"
. 'Q'
is a Char
and "Q"
is a String
(i.e. a [Char]
). But if you have 'Q'
and you want "Q"
, you can write ['Q']
because "Q"
is just shorthand for ['Q']
.
So the short answer is that you have to do it to make the types match up. [x]
is a list of length one, its single element being x
.
In this:
firstLetter all@(x:xs)
= "The first letter of " ++ all ++ " is " ++ x ++ " otherbit " ++ xs
You're building up the string to be printed using ++
. That has type
(++) :: [a] -> [a] -> [a]
i.e. ++
takes two lists, and gives you back a list.
But x
is not a list. So you get a type error.
Instead you use
"The first letter of " ++ all ++ " is " ++ [x] ++ " otherbit " ++ xs
Now all the parameters to ++
are lists, and all the types match up.
But you could instead use
"The first letter of " ++ all ++ " is " ++ x : " otherbit " ++ xs
because the type of :
is given by
(:) :: a -> [a] -> [a]
Upvotes: 5