Angus Comber
Angus Comber

Reputation: 9708

Haskell [x] and x notation - as-pattern example

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

Answers (4)

Per Persson
Per Persson

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

Marco Righele
Marco Righele

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

Vladimir
Vladimir

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

dave4420
dave4420

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

Related Questions