Haskell track: assignment 1: getting started


Goals

In this assignment you will get familiar with the Haskell programming environment and write some simple programs.

Language concepts covered this week

Other material for this week

Reading

Read the first four chapters of the Gentle Introduction to Haskell. This is a fair amount of reading, but once it's done you'll understand the most basic parts of the Haskell language.


Getting set up on the CS cluster

DO NOT SKIP THIS SECTION!

Many of you will want to develop and run your programs on your own computer, and you are welcome to do so, but the official environment for developing your programs is on the CS cluster. This consists of (a) the computers in Annenberg 104 which run Linux, and (b) any remote login machines available for the cluster. [Currently, there are no remote login machines available on the cluster, but we're working on it.] They all run the same software. You need to do a few small things to get set up to use an up-to-date programming environment on the CS cluster.

The first rule is as follows:

Do not, under any circumstances, develop or run your programs on the CS cluster machine called login.cs.caltech.edu or login.cms.caltech.edu! This computer is not intended to be used for program development at all.

Instead, if you want to develop and test your code remotely, use a remote login machine, if there is one. It will work just like all the other CS cluster computers in the lab.

The software for the CS 11 Haskell track lives in the directory /cs/courses/cs11/install. In order to make this software available to you, you need to do these simple steps:

  1. Log in to a CS cluster computer.

  2. Execute the following line in a terminal (where % represents the terminal prompt):

    % cp /cs/courses/cs11/setup/bashrc-cs11 ~/.bashrc-cs11
    
  3. If you do not have a file in your home directory called .bashrc (use ls -a to check for files beginning with a dot, which are normally hidden), execute the following line in the terminal:

    % cp ~setup/general.bashrc ~/.bashrc
    
  4. At the end of your .bashrc file, add the following line:

    source ~/.bashrc-cs11
    

Now log out and log back in again. You're all set!


The Haskell programming environment

For this track we will be using the Glasgow Haskell Compiler as our Haskell programming environment. This is the most advanced and "industrial-strength" Haskell compiler there is. It supports the full Haskell 98 language plus lots of language extensions (which we won't cover in this track). It includes both a stand-alone compiler (ghc) and an interactive interpreter (ghci); we'll be working with both of them. We'll refer to the system as ghc from now on; this will mean both the compiler and the interpreter. When we want to specifically refer to the interpreter we'll say ghci.

The main deficiency of ghc is that the error messages it generates can be difficult to understand unless you know a lot about the language. If you get an error message which is incomprehensible, email it to me and I'll tell you what it means.

Simple interactions with ghci

For now we'll only be using ghci to run Haskell programs. Type this at the unix prompt:

% ghci

and you'll see this (but the version number may be different):

GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
Prelude> 

Now type in some simple expressions:

Prelude> 1 + 1
2
Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> fst (1, "foo")
1
Prelude> snd (1, "foo")
"foo"
Prelude>

Note the Prelude> prompt. The prelude is the standard library of Haskell functions that are loaded up when ghci starts running. When you interact with ghci, you type in an expression and ghci returns the value obtained after evaluating your expression. In addition to this, there are a lot of other useful interpreter commands built into ghci that are not part of the Haskell language; these all start with a colon (:). You can get a list of these by typing :? or :help at the prompt. The most useful colon commands are these:

Here's an example:

Prelude> :t "foo"
"foo" :: [Char]

This says that the string "foo" has the type [Char] (which is also known as type String). This notation means that character strings have the type "list of Char". Sometimes, types are more complicated than you'd expect:

Prelude> :t 42
42 :: forall t. (Num t) => t

You'd expect 42 to be an Int or an Integer, but instead you get this bizarre type. This has to do with the type class system wherein a literal number like 42 can have any of multiple types as long as they obey certain rules; we'll get to that later in the track. If you declare the type explicitly:

Prelude> let n = 42 :: Int
Prelude> :t n
n :: Int

then the type is what you expect.

Note that although you can type in any expression to ghci, you can't type in function definitions or type definitions. These have to be typed in to a separate file and loaded in (or reloaded).

Program to write

Write the following functions in a file called lab1.hs. All numeric function arguments and results are of type Double unless otherwise specified. Write type declarations for all your functions.

Enabling warnings in ghci

If you invoke ghci with the -W option, then several useful warnings will be enabled. Most importantly, any incomplete (non-exhaustive) pattern matches in your code will be reported as such. We expect that all of your code will go through ghci -W without warnings. So you can type this at the unix prompt:

% ghci -W lab1.hs

and then test your code inside ghci. Another way to do this is to create a file called .ghci in your home directory. Inside that file put this line:

:set -W

What this will do is automatically set the -W option every time you use ghci. With this done, you can just type:

% ghci lab1.hs

and the -W option will automatically be enabled. This is the approach we recommend.

Note that sometimes ghci will report incomplete pattern matches which don't look incomplete to you. A classic example would be this function:

absoluteValue :: Int -> Int
absoluteValue x | x >= 0 = x
                | x < 0  = -x

If you load this into ghci with the -W option enabled, you'll get something like this:

abs.hs:2:0:
    Warning: Pattern match(es) are non-exhaustive
             In the definition of `absoluteValue': Patterns not matched: _

The problem here is that ghci is not smart enough to realize that the patterns you've given cover all possible Int arguments. A solution is simple:

absoluteValue :: Int -> Int
absoluteValue x | x >= 0 = x
absoluteValue x = -x

or, alternatively:

absoluteValue :: Int -> Int
absoluteValue x | x >= 0 = x
                | otherwise  = -x

Just because your function really does exhaustive pattern matching isn't good enough for us; we want you to make sure ghci thinks so too.

To hand in

Your program lab1.hs.

References