Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [22]

By Root 447 0
a value, so GHCi just printed that value.

case Expressions

case expressions allow you to execute blocks of code for specific values of a particular variable. Essentially, they are a way to use pattern matching almost anywhere in your code. Many languages (like C, C++, and Java) have some kind of case statement, so you may already be familiar with the concept.

Haskell takes that concept and one-ups it. As the name implies, case expressions are expressions, much like if else expressions and let expressions. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching.

This is very similar to performing pattern matching on parameters in function definitions, where you take a value, pattern match it, and evaluate pieces of code based on that value. In fact, that kind of pattern matching is just syntactic sugar for case expressions. For example, the following two pieces of code do the same thing and are interchangeable:

head' :: [a] -> a

head' [] = error "No head for empty lists!"

head' (x:_) = x

head' :: [a] -> a

head' xs = case xs of [] -> error "No head for empty lists!"

(x:_) -> x

Here's the syntax for a case expression:

case expression of pattern -> result

pattern -> result

pattern -> result

...

This is pretty simple. The first pattern that matches the expression is used. If it falls through the whole case expression and no suitable pattern is found, a runtime error occurs.

Pattern matching on function parameters can be done only when defining functions, but case expressions can be used anywhere. For instance, you can use them to perform pattern matching in the middle of an expression, like this:

describeList :: [a] -> String

describeList ls = "The list is " ++ case ls of [] -> "empty."

[x] -> "a singleton list."

xs -> "a longer list."

Here, the case expression works like this: ls is first checked against the pattern of an empty list. If ls is empty, the whole case expression then assumes the value of "empty". If ls is not an empty list, then it’s checked against the pattern of a list with a single element. If the pattern match succeeds, the case expression then has the value of "a singleton list". If neither of those two patterns match, then the catchall pattern, xs, applies. Finally, the result of the case expression is joined together with the string "The list is". Each case expression represents a value. That’s why we were able to use ++ between the string "The list is" and our case expression.

Because pattern matching in function definitions is the same as using case expressions, we could have also defined the describeList function like this:

describeList :: [a] -> String

describeList ls = "The list is " ++ what ls

where what [] = "empty."

what [x] = "a singleton list."

what xs = "a longer list."

This function acts just like the one in the previous example, although we used a different syntactic construct to define it. The function what gets called with ls, and then the usual pattern-matching action takes place. Once this function returns a string, it’s joined with "The list is".

Chapter 4. Hello Recursion!

In this chapter, we’ll take a look at recursion. We’ll learn why it’s important in Haskell programming and how we can find very concise and elegant solutions to problems by thinking recursively.

Recursion is a way of defining functions in which a function is applied inside its own definition. In other words, the function calls itself. If you still don’t know what recursion is, read this sentence. (Haha! Just kidding!)

Kidding aside, the strategy of a recursively defined function is to break down the problem at hand into smaller problems of the same kind and then try to solve those subproblems, breaking them down further if necessary. Eventually we reach the base case (or base cases) of the problem, which can’t be broken down any more and whose solutions need to be explicitly (non-recursively) defined by the programmer.

Definitions in mathematics are often recursive. For instance, we can specify the Fibonacci

Return Main Page Previous Page Next Page

®Online Book Reader