Yet Another Haskell Tutorial/Language basics

In this chapter we present the basic concepts of Haskell. In addition to familiarizing you with the interactive environments and showing you how to compile a basic program, we introduce the basic syntax of Haskell, which will probably be quite alien if you are used to languages like C and Java.

However, before we talk about specifics of the language, we need to establish some general properties of Haskell. Most importantly, Haskell is a lazy language, which means that no computation takes place unless it is forced to take place when the result of that computation is used.

This means, for instance, that you can define infinitely large data structures, provided that you never use the entire structure. For instance, using imperative-esque pseudo-code, we could create an infinite linked-list containing the number $$1$$ in each position by doing something like:

List makeList { List current = new List; current.value = 1; current.next = makeList; return current; } By looking at this code, we can see what it's trying to do: it creates a new list, sets its value to $$1$$ and then recursively calls itself to make the rest of the list. Of course, if you actually wrote this code and called it, the program would never terminate, because  would keep calling itself ad infinitum.

This is because we assume this imperative-esque language is strict, the opposite of lazy. Strict languages are often referred to as "call by value," while lazy languages are referred to as "call by name." In the above pseudo-code, when we "run" makeList on the fifth line, we attempt to get a value out of it. This leads to an infinite loop.

The equivalent code in Haskell is:

makeList = 1 : makeList This program reads: we're defining something called (this is what goes on the left-hand side of the equals sign). On the right-hand side, we give the definition of. In Haskell, the colon operator is used to create lists (we'll talk more about this soon). This right-hand side says that the value of is the element   stuck on to the beginning of the value of.

However, since Haskell is lazy (or "call by name"), we do not actually attempt to evaluate what  is at this point: we simply remember that if ever in the future we need the second element of, we need to just look at.

Now, if you attempt to write  to a file, print it to the screen, or calculate the sum of its elements, the operation won't terminate because it would have to evaluate an infinitely long list. However, if you simply use a finite portion of the list (say the first $$10$$ elements), the fact that the list is infinitely long doesn't matter. If you only use the first $$10$$ elements, only the first $$10$$ elements are ever calculated. This is laziness.

Second, Haskell is case-sensitive. Many languages are, but Haskell actually uses case to give meaning. Haskell distinguishes between values (for instance, numbers: $$1,2,3,...$$); strings: "abc", "hello", ...; characters: 'a', 'b', ' ', ...; even functions (for instance, the function that squares a value, or the square-root function); and types (the categories to which values belong).

By itself, this is not unusual. Most languages have some system of types. What is unusual is that Haskell requires that the names given to functions and values begin with a lower-case letter and that the names given to types begin with an upper-case letter. The moral is: if your otherwise correct program won't compile, be sure you haven't named your function, or something else beginning with a capital letter.

Being a functional language, Haskell eschews side effects. A side effect is essentially something that happens in the course of executing a function that is not related to the output produced by that function.

For instance, in a language like C or Java, you are able to modify "global" variables from within a function. This is a side effect because the modification of this global variable is not related to the output produced by the function. Furthermore, modifying the state of the real world is considered a side effect: printing something to the screen, reading a file, etc., are all side effecting operations.

Functions that do not have side effects are called pure. An easy test for whether or not a function is pure is to ask yourself a simple question: "Does this function's result depend only on the arguments it receives, and is returning a result the only thing it does?"

All of this means that if you're used to writing code in an imperative language (like C or Java), you're going to have to start thinking differently. Most importantly, if you have a value, you must not think of  as a register, a memory location or anything else of that nature. is simply a name, just as "Hal" is my name. You cannot arbitrarily decide to store a different person in my name any more than you can arbitrarily decide to store a different value in. This means that code that might look like the following C code is invalid (and has no counterpart) in Haskell:

int x = 5; x = x + 1; A call like x = x + 1 is called destructive update because we are destroying whatever was in x before and replacing it with a new value. Destructive update does not exist in Haskell.

By not allowing destructive updates (or any other such side effecting operations), Haskell code is very easy to comprehend. That is, when we define a function, and call that function with a particular argument  in the beginning of a program, and then, at the end of the program, again call with the same argument, we know we will get out the same result. This is because we know that  cannot have changed and because we know that  only depends on   (for instance, it didn't increment a global counter). This property is called referential transparency and basically states that if two functions  and   produce the same values for the same arguments, then we may replace  with (and vice-versa).

Arithmetic
Let's begin our foray into Haskell with simple arithmetic. Start up your favorite interactive shell (Hugs or GHCi; see the chapter Getting started for installation instructions). The shell will output to the screen a few lines talking about itself and what it's doing and then should finish with the cursor on a line reading:

From here, you can begin to evaluate expressions. An expression is basically something that has a value. For instance, the number $$5$$ is an expression (its value is $$5$$). Values can be built up from other values; for instance, $$5+6$$ is an expression (its value is $$11$$). In fact, most simple arithmetic operations are supported by Haskell, including plus, minus , times , divided-by , exponentiation and square-root. You can experiment with these by asking the interactive shell to evaluate expressions and to give you their value. In this way, a Haskell shell can be used as a powerful calculator. Try some of the following:

We can see that, in addition to the standard arithmetic operations, Haskell also allows grouping by parentheses, hence the difference between the values of 5*4+3 and 5*(4+3). The reason for this is that the "understood" grouping of the first expression is (5*4)+3, due to operator precedence.

Also note that parentheses aren't required around function arguments. For instance, we simply wrote, not  , as would be required in most other languages. You could write it with the parentheses, but in Haskell, since function application is so common, parentheses aren't required.

Now try entering 2^5000. Does it work?

Pairs, Triples and More
In addition to single values, we should also address multiple values. For instance, we may want to refer to a position by its $$x$$/$$y$$ coordinate, which would be a pair of integers. To make a pair of integers is simple: you enclose the pair in parenthesis and separate them with a comma. Try the following:

Here, we have a pair of integers, $$5$$ and $$3$$. In Haskell, the first element of a pair need not have the same type as the second element: that is, pairs are allowed to be heterogeneous. For instance, you can have a pair of an integer with a string. This contrasts with lists, which must be made up of elements of all the same type (we will discuss lists further in the section on Lists).

There are two predefined functions that allow you to extract the first and second elements of a pair. They are, respectively, and. You can see how they work below:

In addition to pairs, you can define triples, quadruples etc. To define a triple and a quadruple, respectively, we write:

And so on. In general, pairs, triples, and so on are called tuples and can store fixed amounts of heterogeneous data.

Lists
The primary limitation of tuples is that they hold only a fixed number of elements: pairs hold two, triples hold three, and so on. A data structure that can hold an arbitrary number of elements is a list. Lists are assembled in a very similar fashion to tuples, except that they use square brackets instead of parentheses. We can define a list like:

Lists don't need to have any elements. The empty list is simply.

Unlike tuples, we can very easily add an element on to the beginning of the list using the colon operator. The colon is called the "cons" operator; the process of adding an element is called "consing." The etymology of this is that we are constructing a new list from an element and an old list. We can see the cons operator in action in the following examples:

We can actually build any list by using the cons operator (the colon) and the empty list:

In fact, the  syntax is "syntactic sugar" for the expression using the explicit cons operators and empty list. If we write something using the notation, the compiler simply translates it to the expression using  and.

One further difference between lists and tuples is that, while tuples are heterogeneous, lists must be homogeneous. This means that you cannot have a list that holds both integers and strings. If you try to, a type error will be reported.

Of course, lists don't have to just contain integers or strings; they can also contain tuples or even other lists. Tuples, similarly, can contain lists and other tuples. Try some of the following:

There are two basic list functions: and. The  function returns the first element of a (non-empty) list, and the  function returns all but the first element of a (non-empty) list.

To get the length of a list, you use the  function:

Strings
In Haskell, a  is simply a list of  s.  So, we can create the string "Hello" as:

Lists (and, of course, strings) can be concatenated using the  operator:

Additionally, non-string values can be converted to strings using the function, and strings can be converted to non-string values using the  function. Of course, if you try to read a value that's malformed, an error will be reported (note that this is a run-time error, not a compile-time error):

In the above, the exact error message is implementation dependent. However, the interpreter has inferred that you're trying to add three to something. This means that when we execute, we expect to be returned a number. However,  cannot be parsed as a number, so an error is reported.

Simple List Functions
Much of the computation in Haskell programs is done by processing lists. There are three primary list-processing functions: , and  (also  ).

The  function takes as arguments a list of values and a function that should be applied to each of the values. It returns the result of this application. For instance, there is a built-in function  that takes as input a  and produces a   that is the upper-case version of the original argument. So, to convert an entire string (which is simply a list of characters) to upper case, we can map the function across the entire list:

When you map across a list, the length of the list never changes -- only the individual values in the list change.

To remove elements from the list, you can use the function. This function allows you to remove certain elements from a list depending on their value, but not on their context. For instance, the function  tells you whether a given character is lower case. We can filter out all non-lowercase characters using this:

The function  takes a little more getting used to. takes three arguments: a function, an initial value and a list. The best way to think about  is that it replaces occurrences of the list cons operator with the function parameter and replaces the empty list constructor with the initial value. Thus, if we have a list:

3 : 8 : 12 : 5 : [] and we apply  to it, we get:

3 + 8 + 12 + 5 + 0 which sums the list. We can test this:

We can perform the same sort of operation to calculate the product of all the elements on a list:

We said earlier that folding is like replacing with a particular function and  with an initial element. This raises a question as to what happens when the function isn't associative (a function ($$\cdot$$) is associative if $$a \cdot (b \cdot c) = (a \cdot b) \cdot c$$). When we write $$4 \cdot 8 \cdot 5 \cdot 1$$, we need to specify where to put the parentheses. Namely, do we mean $$((4 \cdot 8) \cdot 5) \cdot 1$$ or $$4 \cdot (8 \cdot (5 \cdot 1))$$? assumes the function is right-associative (i.e., the correct bracketing is the latter). Thus, when we use it on a non-associative function (like minus), we can see the effect:

The exact derivation of this looks something like:

foldr (-) 1 [4,8,5] ==> 4 - (foldr (-) 1 [8,5]) ==> 4 - (8 - foldr (-) 1 [5]) ==> 4 - (8 - (5 - foldr (-) 1 [])) ==> 4 - (8 - (5 - 1)) ==>  4 - (8 - 4) ==>  4 - 4 ==>  0 The   function goes the other way and effectively produces the opposite bracketing. looks the same when applied, so we could have done summing just as well with :

However, we get different results when using the non-associative function minus:

This is because  uses the opposite bracketing. The way it accomplishes this is essentially by going all the way down the list, taking the last element and combining it with the initial value via the provided function. It then takes the second-to-last element in the list and combines it to this new value. It does so until there is no more list left.

The derivation here proceeds in the opposite fashion:

foldl (-) 1 [4,8,5] ==> foldl (-) (1 - 4) [8,5] ==> foldl (-) ((1 - 4) - 8) [5] ==> foldl (-) (((1 - 4) - 8) - 5) [] ==> ((1 - 4) - 8) - 5 ==>  ((-3) - 8) - 5 ==>  (-11) - 5 ==>  -16 Note that once the   goes away, the parenthesization is exactly the opposite of the.

If this discussion of the folding functions is still somewhat unclear, that's okay. We'll discuss them further in the section on Lists.

Source Code Files
As programmers, we don't want to simply evaluate small expressions like these -- we want to sit down, write code in our editor of choice, save it and then use it.

We already saw in the sections Ghc and Nhc how to write a Hello World program and how to compile it. Here, we show how to use functions defined in a source-code file in the interactive environment. To do this, create a file called <tt>Test.hs</tt> and enter the following code:

module Test where

x = 5 y = (6, "Hello") z = x * fst y This is a very simple "program" written in Haskell. It defines a module called "Test" (in general module names should match file names; see the section on Modules for more on this). In this module, there are three definitions: x, y and z. Once you've written and saved this file, in the directory in which you saved it, load this in your favorite interpreter, by executing either of the following:

This will start Hugs or GHCi, respectively, and load the file. Alternatively, if you already have one of them loaded, you can use the ":load" command (or just ":l") to load a module, as:

Between the first and last line, the interpreter will print various data to explain what it is doing. If any errors appear, you probably mistyped something in the file; double check and then try again.

You'll notice that where it used to say "Prelude" it now says "Test." That means that Test is the current module. The Prelude module (usually simply referred to as "the Prelude") is always loaded and contains the standard definitions (for instance, the operator for lists, or  or,  ,   and so on).

Now that we've loaded Test, we can use things that were defined in it. For example:

Perfect, just as we expected!

One final issue regarding how to compile programs to stand-alone executables remains. In order for a program to be an executable, it must have the module name "Main" and must contain a function called. So, if you go in to Test.hs and rename it to "Main" (change the line that reads  to  ), we simply need to add a main function. Try this:

main = putStrLn "Hello World" Now, save the file, and compile it (refer back to the section on Getting started for information on how to do this for your compiler). For example, in GHC, you would say:

This will create a file called "test" (or on Windows, "test.exe") that you can then run.

Functions
Now that we've seen how to write code in a file, we can start writing functions. As you might have expected, functions are central to Haskell, as it is a functional language. This means that the evaluation of a program is simply the evaluation of a function.

We can write a simple function to square a number and enter it into our <tt>Test.hs</tt> file. We might define this as follows:

square x = x * x In this function definition, we say that we're defining a function that takes one argument (aka parameter), which we call . We then say that the value of   is equal to.

Haskell also supports standard conditional expressions. For instance, we could define a function that returns $$-1$$ if its argument is less than $$0$$; $$0$$ if its argument is $$0$$; and $$1$$ if its argument is greater than $$0$$ (this is called the signum function):

signum x = if x < 0 then -1 else if x > 0 then 1 else 0 You can experiment with this as:

Note that the parenthesis around "-1" in the last example are required; if missing, the system will think you are trying to subtract the value "1" from the value "signum," which is illtyped.

The if/then/else construct in Haskell is very similar to that of most other programming languages; however, you must have both a <tt>then</tt> and an <tt>else</tt> clause. It evaluates the condition (in this case $$x < 0$$ and, if this evaluates to, it evaluates the <tt>then</tt> clause; if the condition evaluated to  , it evaluates the <tt>else</tt> clause).

You can test this program by editing the file and loading it back into your interpreter. If <tt>Test</tt> is already the current module, instead of typing <tt>:l Test.hs</tt> again, you can simply type <tt>:reload</tt> or just <tt>:r</tt> to reload the current file. This is usually much faster.

Haskell, like many other languages, also supports <tt>case</tt> 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 section on Pattern matching 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 <tt>if</tt> statements would be long and very unreadable; so we write it using a <tt>case</tt> statement as follows (we call this function ):

f x = case x of     0 -> 1 1 -> 5     2 -> 2      _ -> -1 In this program, we're defining   to take an argument   and then inspect the value of. If it matches $$0$$, the value of is $$1$$. If it matches $$1$$, the value of  is $$5$$. If it maches $$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.

The general rule for layout is that an open-brace is inserted after the keywords <tt>where</tt>, <tt>let</tt>, <tt>do</tt> and <tt>of</tt>, 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 close-brace 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 section on Layout 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:

f x = case x of       { 0 -> 1 ; 1 -> 5 ; 2 -> 2 ; _ -> -1 } 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:

f x = case x of { 0 -> 1 ; 1 -> 5 ; 2 -> 2  ; _ -> -1 } However, structuring your code like this only serves to make it unreadable (in this case).

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:

f 0 = 1 f 1 = 5 f 2 = 2 f _ = -1 Here, the 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.

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 way back in arithmetic (the section on Arithmetic), when we wrote. In this, we were evaluating $$5*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 mathematically oriented, way to express function composition, using the (just a single period) function. This function is supposed to look like the ($$\circ$$) operator in mathematics.

The function (called the function composition function), takes two functions and makes them in to 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 it creates a new function 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.

It would probably be wise to take a little time-out to look at some of the functions that are defined in the Prelude. Undoubtedly, at some point, you will accidentally rewrite some already-existing function (I've done it more times than I can count), but if we can keep this to a minimum, that would save a lot of time. Here are some simple functions, some of which we've already seen:

Here, we show example usages of each of these functions:

We can see that applying  to an empty list gives an error (the exact error message depends on whether you're using GHCi or Hugs -- the shown error message is from Hugs).

Let Bindings
Often we wish to provide local declarations for use in our functions. For instance, the following equation is used to find the roots (zeros) of a polynomial of the form $$ax^2+bx+c=0$$: $$x = (-b \pm \sqrt{b^2-4ac}) / (2a)$$. We could write the following function to compute the two values of $$x$$:

roots a b c = ((-b + sqrt(b*b - 4*a*c)) / (2*a),    (-b - sqrt(b*b - 4*a*c)) / (2*a))

Unfortunately, we duplicate expressions here. To remedy this problem, Haskell allows for local bindings. That is, we can create values inside of a function that only that function can see. For instance, we could create a local binding for the expression , calling it  for discriminant, and then use that in both places where   occurred. We can do this using a <tt>let</tt>/<tt>in</tt> declaration:

roots a b c = let discr = sqrt (b*b - 4*a*c) in ((-b + discr) / (2*a),         (-b - discr) / (2*a)) In fact, you can provide multiple declarations inside a let. Just make sure they're indented the same amount, or you will have layout problems:

roots a b c = let discr = sqrt (b*b - 4*a*c) twice_a = 2*a in ((-b + discr) / twice_a,         (-b - discr) / twice_a)

Infix
Infix functions are ones that are composed of symbols, rather than letters. For instance,,  ,   are all infix functions. You can use them in non-infix mode by enclosing them in parentheses. Hence, the two following expressions are the same:

Similarly, non-infix functions (like ) can be made infix by enclosing them in backquotes (the ticks on the tilde key on American keyboards):

Comments
There are two types of comments in Haskell: line comments and block comments. Line comments begin with the token  and extend until the end of the line. Block comments begin with  and extend to a corresponding. Block comments can be nested.

Comments are used to explain your program in English and are completely ignored by compilers and interpreters. For example:

module Test2 where

main = putStrLn "Hello World" -- write a string -- to the screen

{- f is a function which takes an integer and produces integer. {- this is an embedded comment -} the original comment extends to the matching end-comment token: -} f x = case x of     0 -> 1    -- 0 maps to 1 1 -> 5   -- 1 maps to 5 2 -> 2   -- 2 maps to 2 _ -> -1  -- everything else maps to -1 This example program shows the use of both line comments and (embedded) block comments.

Recursion
In imperative languages like C and Java, the most basic control structure is a loop (like a for loop). However, for loops don't make much sense in Haskell because they require destructive update (the index variable is constantly being updated). Instead, Haskell uses recursion.

A function is recursive if it calls itself (see the appendix on Recursion for more). Recursive functions exist also in C and Java but are used less than they are in functional languages. The prototypical recursive function is the factorial function. In an imperative language, you might write this as something like:

int factorial(int n) { int fact = 1; for (int i=2; i <= n; i++) fact = fact * i; return fact; } While this code fragment will successfully compute factorials for positive integers, it somehow ignores the basic definition of factorial, usually given as:

$$ n! = \begin{cases} 1 & n = 1 \\ n * (n-1)! & \mbox{otherwise}\\ \end{cases} $$

This definition itself is exactly a recursive definition: namely the value of $$n!$$ depends on the value of $$(n-1)!$$. If you think of $$!$$ as a function, then it is calling itself. We can translate this definition almost verbatim into Haskell code:

factorial 1 = 1 factorial n = n * factorial (n-1) This is likely the simplest recursive function you'll ever see, but it is correct.

Recursion can be a difficult thing to master. It is completely analogous to the concept of induction in mathematics (see the chapter Recursion for a more formal treatment of this). However, usually a problem can be thought of as having one or more base cases and one or more recursive-cases. In the case of, there is one base case (when $$n=1$$) and one recursive case (when $$n>1$$). For designing your own recursive algorithms, it is often useful to try to differentiate these two cases.

Turning now to the task of exponentiation, suppose that we have two positive integers $$a$$ and $$b$$, and that we want to calculate $$a^b$$. This problem has a single base case: namely when $$b$$ is $$1$$. The recursive case is when $$b>1$$. We can write a general form as:

$$ a^b = \begin{cases} a & b=1 \\ a * a^{b-1} & \mbox{otherwise} \\ \end{cases} $$

Again, this translates directly into Haskell code:

exponent a 1 = a exponent a b = a * exponent a (b-1) Just as we can define recursive functions on integers, so can we define recursive functions on lists. In this case, usually the base case is the empty list, and the recursive case is a cons list (i.e., a value consed on to another list).

Consider the task of calculating the length of a list. We can again break this down into two cases: either we have an empty list or we have a non-empty list. Clearly the length of an empty list is zero. Furthermore, if we have a cons list, then the length of this list is just the length of its tail plus one. Thus, we can define a length function as:

my_length [] = 0 my_length (x:xs) = 1 + my_length xs

Similarly, we can consider the  function. Again, the base case is the empty list, and the recursive case is a cons list. However, this time, we're choosing whether to keep an element, depending on whether or not a particular predicate holds. We can define the filter function as:

my_filter p [] = [] my_filter p (x:xs) = if p x   then x : my_filter p xs    else my_filter p xs In this code, when presented with an empty list, we simply return an empty list. This is because filter cannot add elements; it can only remove them.

When presented with a list of the form, we need to decide whether or not to keep the value. To do this, we use an <tt>if</tt> statement and the predicate. If  is true, then we return a list that begins with  followed by the result of filtering the tail of the list. If  is false, then we exclude and return the result of filtering the tail of the list.

We can also define  and both fold functions using explicit recursion. See the exercises for the definition of  and the chapter Language advanced for the folds.

Interactivity
If you are familiar with books on other (imperative) languages, you might be wondering why you haven't seen many of the standard programs written in tutorials of other languages (like ones that ask the user for his name and then says "Hi" to him by name). The reason for this is simple: Being a pure functional language, it is not entirely clear how one should handle operations like user input.

After all, suppose you have a function that reads a string from the keyboard. If you call this function twice, and the user types something the first time and something else the second time, then you no longer have a function, since it would return two different values. The solution to this was found in the depths of category theory, a branch of formal mathematics: monads. We're not yet ready to talk about monads formally, but for now, think of them simply as a convenient way to express operations like input/output. We'll discuss them in this context much more in the chapter Io and then discuss monads for monads' sake in the chapter Monads.

Suppose we want to write a function that's interactive. The way to do this is to use the <tt>do</tt> keyword. This allows us to specify the order of operations (remember that normally, since Haskell is a lazy language, the order in which operations are evaluated in it is unspecified). So, to write a simple program that asks a user for his name and then address him directly, enter the following code into "Name.hs":

module Main where

import System.IO

main = do hSetBuffering stdin LineBuffering putStrLn "Please enter your name: " name <- getLine putStrLn ("Hello, " ++ name ++ ", how are you?")

You can then either load this code in your interpreter and execute  by simply typing " ," or you can compile it and run it from the command line. I'll show the results of the interactive approach:

And there's interactivity. Let's go back and look at the code a little, though. We name the module "Main," so that we can compile it. We name the primary function " ," so that the compiler knows that this is the function to run when the program is run. On the fourth line, we import the  library, so that we can access the IO functions. On the seventh line, we start with <tt>do</tt>, telling Haskell that we're executing a sequence of commands.

The first command is, which you should probably ignore for now (incidentally, this is only required by GHC -- in Hugs you can get by without it). The necessity for this is because, when GHC reads input, it expects to read it in rather large blocks. A typical person's name is nowhere near large enough to fill this block. Thus, when we try to read from, it waits until it's gotten a whole block. We want to get rid of this, so we tell it to use instead of block buffering.

The next command is, which prints a string to the screen. On the ninth line, we say. This would normally be written, but using the arrow instead of the equal sign shows that  isn't a real function and can return different values. This command means "run the action, and store the results in  ."

The last line constructs a string using what we read in the previous line and then prints it to the screen.

Another example of a function that isn't really a function would be one that returns a random value. In this context, a function that does this is called. Using this, we can write a "guess the number" program. Enter the following code into "Guess.hs":

module Main where

import System.IO import System.Random

main = do hSetBuffering stdin LineBuffering num <- randomRIO (1::Int, 100) putStrLn "I'm thinking of a number between 1 and 100" doGuessing num

doGuessing num = do putStrLn "Enter your guess:" guess <- getLine let guessNum = read guess if guessNum < num then do putStrLn "Too low!" doGuessing num else if guessNum > num then do putStrLn "Too high!" doGuessing num else do putStrLn "You Win!" Let's examine this code. On the fifth line we write "import Random" to tell the compiler that we're going to be using some random functions (these aren't built into the Prelude). In the first line of, we ask for a random number in the range $$(1,100)$$. We need to write  to tell the compiler that we're using integers here -- not floating point numbers or other numbers. We'll talk more about this in the section on Type basics. On the next line, we tell the user what's going on, and then, on the last line of, we tell the compiler to execute the command.

The  function takes the number the user is trying to guess as an argument. First, it asks the user to guess and then accepts their guess (which is a ) from the keyboard. The <tt>if</tt> statement checks first to see if their guess is too low. However, since  is a string, and   is an integer, we first need to convert   to an integer by  ing it. Since " " is a plain, pure function (and not an IO action), we don't need to use the  notation (in fact, we cannot); we simply bind the value to. Note that while we're in <tt>do</tt> notation, we don't need <tt>in</tt>s for <tt>let</tt>s.

If they guessed too low, we inform them and then start over again. If they didn't guess too low, we check to see if they guessed too high. If they did, we tell them and start again. Otherwise, they didn't guess too low and they didn't guess too high, so they must have gotten it correct. We tell them that they won and exit. The fact that we exit is implicit in the fact that there are no commands following this. We don't need an explicit statement.

You can either compile this code or load it into your interpreter, and you will get something like:

The recursive action that we just saw doesn't actually return a value that we use in any way. In the case when it does, the "obvious" way to write the command is actually incorrect. Here, we will give the incorrect version, explain why it is wrong, then give the correct version.

Let's say we're writing a simple program that repeatedly asks the user to type in a few words. If at any point the user enters the empty word (i.e., he just hits enter without typing anything), the program prints out everything he's typed up until that point and then exits. The primary function (actually, an action) in this program is one that asks the user for a word, checks to see if it's empty, and then either continues or ends. The incorrect formulation of this might look something like:

askForWords = do putStrLn "Please enter a word:" word <- getLine if word == "" then return [] else return (word : askForWords) Before reading ahead, see if you can figure out what is wrong with the above code.

The error is on the last line, specifically with the term. Remember that when using, we are making a list out of an element (in this case ) and another list (in this case,  ). However,  is not a list; it is an action that, when run, will produce a list. That means that before we can attach anything to the front, we need to run the action and take the result. In this case, we want to do something like:

askForWords = do putStrLn "Please enter a word:" word <- getLine if word == "" then return [] else do     rest <- askForWords return (word : rest) Here, we first run, take the result and store it in the variable. Then, we return the list created from and.

By now, you should have a good understanding of how to write simple functions, compile them, test functions and programs in the interactive environment, and manipulate lists.