User:Duplode/Haskell leftovers 2

case
Haskell, like many other languages, also supports case constructions. These are used when there are multiple values that you want to check against (case expressions are actually quite a bit more powerful than this – see the ../Pattern matching/ chapter for all of the details).

Suppose we wanted to define a function that had a value of $$1$$ if its argument were $$0$$; a value of $$5$$ if its argument were $$1$$; a value of $$2$$ if its argument were $$2$$; and a value of $$-1$$ in all other instances. Writing this function using if statements would be long and very unreadable; so we write it using a case statement as follows (we call this function ):

In this program, we're defining  to take an argument   and then inspecting the value of. If it matches $$0$$, the value of  is $$1$$. If it matches $$1$$, the value of  is $$5$$. If it matches $$2$$, then the value of  is $$2$$; and if it hasn't matched anything by that point, the value of   is $$-1$$ (the underscore can be thought of as a "wildcard" – it will match anything).

The indentation here is important. Haskell uses a system called "layout" to structure its code (the programming language Python uses a similar system). The layout system allows you to write code without the explicit semicolons and braces that other languages like C and Java require.

Indentation
The general rule for layout is that an opening brace (the { character) is inserted after the keywords where, let, do and of, and the column position at which the next command appears is remembered. From then on, a semicolon is inserted before every new line that is indented the same amount. If a following line is indented less, a closing brace (<tt>}</tt>) is inserted. This may sound complicated, but if you follow the general rule of indenting after each of those keywords, you'll never have to remember it (see the ../Indentation/ chapter for a more complete discussion of layout).

Some people prefer not to use layout and write the braces and semicolons explicitly. This is perfectly acceptable. In this style, the above function might look like:

Of course, if you write the braces and semicolons explicitly, you're free to structure the code as you wish. The following is also equally valid:

However, structuring your code like this only serves to make it unreadable (in this case).

Defining one function for different parameters
Functions can also be defined piece-wise, meaning that you can write one version of your function for certain parameters and then another version for other parameters. For instance, the above function  could also be written as:

Just like in the  situation above, order is important. If we had put the last line first, it would have matched every argument, and  would return , regardless of its argument (most compilers will warn you about this, though, saying something about overlapping patterns). If we had not included this last line,  would produce an error if anything other than 0, 1 or 2 were applied to it (most compilers will warn you about this, too, saying something about incomplete patterns). This style of piece-wise definition is very popular and will be used quite frequently throughout this tutorial. These two definitions of  are actually equivalent – this piece-wise version is translated into the case expression.

Function composition
More complicated functions can be built from simpler functions using function composition. Function composition is simply taking the result of the application of one function and using that as an argument for another. We've already seen this back in the ../Getting set up/ chapter, when we wrote. In this, we were evaluating $$5 \cdot 4$$ and then applying $$+3$$ to the result. We can do the same thing with our  and   functions:

The result of each of these function applications is fairly straightforward. The parentheses around the inner function are necessary; otherwise, in the first line, the interpreter would think that you were trying to get the value of  , which has no meaning. Function application like this is fairly standard in most programming languages.

There is another, more mathematical way to express function composition: the enclosed period function. This function is modeled after the ($$\circ$$) operator in mathematics.

The function (called the function composition function), takes two functions and makes them into one. For instance, if we write, this means that it creates a new function that takes an argument, applies   to that argument and then applies   to the result. Conversely,  means that a new function is created that takes an argument, applies   to that argument and then applies   to the result. We can see this by testing it as before:

Here, we must enclose the function composition in parentheses; otherwise, the Haskell compiler will think we're trying to compose  with the value   in the first line, which makes no sense since   isn't even a function.

Controlling actions
(...)

We can write the same  function using a <tt>case</tt> statement. To do this, we first introduce the Prelude function , which takes two values of the same type (in the class) and returns a value of type , namely one of  ,  ,  , depending on whether the first is greater than, less than or equal to the second.

Here, again, the <tt>do</tt>s after the s are necessary on the first two options, because we are sequencing actions.

If you're used to programming in an imperative language like C or Java, you might think that <tt>return</tt> will exit you from the current function. This is not so in Haskell. In Haskell, <tt>return</tt> simply takes a normal value (for instance, one of type ) and makes it into an action that when evaluated, gives the given value (for the same example, the action would be of type ). In particular, in an imperative language, you might write this function as:

Here, because we have the  in the first   match, we expect the code to exit there (and in most imperative languages, it does). However, the equivalent code in Haskell, which might look something like:

First of all, if you guess correctly, it will first print "You win!," but it won't exit, and it will check whether  is less than. Of course it is not, so the else branch is taken, and it will print "Too high!" and then ask you to guess again.

On the other hand, if you guess incorrectly, it will try to evaluate the case statement and get either  or   as the result of the. In either case, it won't have a pattern that matches, and the program will fail immediately with an exception.

Case expressions
Another typical usage of pattern binding is on the left hand side of case branches:

Other places
There are a few other situations in which patterns can be used that we'll meet later on. Here's a list in case you're very eager already:


 * clauses, which are an alternative to  bindings;
 * lambdas, which are anonymous function definitions;
 * In  assignments in do-blocks,   can be a pattern.
 * Similarly, with let bindings in do-blocks, you can pattern match analogously to 'real' let bindings.