I have always been challenged by the way lists work and am confused about the whole (x:xs) concept. I just don’t seem to be getting my head around it.
Example
select :: Ord a => a -> [a] -> [a]
select y [] = []
select y (x:xs)
| x < y = x : select y xs
| otherwise = select y xs
P.S. I know exactly what the function does, but would anyone be able to explain the process (including especially the weird Ord a and => signs)?
Any effective strategies would be much appreciated.
Thanks in advance. Ian.
Ok. Let’s walk through the different syntactic elements here.
Line 1
This is a type declaration. It is the declaration of a function (since it has
->types).The function has two arguments.
a(lower case means it is a polymorphic type).The return value is a list of any type, the same as the argument types.
The
Ord acomponent is a typeclass constraint, that says that any type this function is given must also be an instance of theOrdclass. This is a class of types that can be compared.Line 2
Now we look at line 2:
This is one definition of the
selectfunction itself. It is very simple, containing patterns for the two arguments, and a specification of the result. It reads:Line 3
Line 3 contains the other case for lists:
Again, this is part of the definition of the
selectfunction, for the case when the second argument is not an empty list. If it is not an empty list, then it is a list with a head,x, and a tailxs. The “cons” constructor,(:)combines a list head and tail. It is also how we pattern match on a list, to extract the head and tail.By pattern matching on the head and tail of the list, with
(x:xs), we bind a new variable,x, to the value of the head of the list, andxsto value of the tail of the list.Lines 4 and 5
The last two lines are additional guards that test and branch based on additional checks, should the second argument be a non-empty list:
The first guard fires when
xis less than the first argument,y. If so, we return a new list withxat the head, andselectapplied again to the tail.If that is not the case, then we drop
xfrom the list, and return only what happens when tail is called recursively.For more information on how Haskell works, I recommend introductory texts such as:
it will be worth your time.