Lab #1: Haskell Orientation and Exploration
Assigned: Wednesday 26 Jan 2005
Due: Wednesday 2 February 2005
Our first lab this semester is intended to help familiarize you
with the logistics of using Haskell (via the Hugs interpreter)
and to provide you with a number of shorter exercises involving
functions and simple types. These two goals are realized in separate
sections below: the first section is geared towards inspiring some open-ended
investigation and should be followed up with short replies phrased in English,
whereas the second lists specific questions that are best answered with short
programs or expressions written in Haskell. All your answers should be combined
in a text file ready for demonstration in the lab on the due date.
Make use of this lab to try out the programming environment using the Hugs
interpreter, Wordpad and the various documents available from the new links
The new CS 454 Haskell links page
While documentation can be helpful, your best approach to getting started is
to watch the demonstrations in lecture and then experiment on your own.
Get in the habit of trying out your ideas interactively, as this is the best way
to learn Haskell and FP.
A. Short questions to inspire investigation
- What is the difference between the (infix, arithmetic) operators
"**", "^^" and "^" in Haskell?
(hint: consider typings, but also try a few sample arguments;
use the ":t" command to find out the type of an expression,
and rememebr to put infix operators in parenthese for this purpose)
- What is the associativity of these operators in Haskell
(i.e., either left-associative or right-associative)?
- What is the meaning of the "const" function in Haskell
(i.e., what does it compute, based on its arguments)?
(hint: try applying it to two successive arguments, as in "const 3 5")
- What is the definition (i.e., program text) of the "sum"
operator in Haskell? How about the "maximum" operator?
(hint: look in the library file "Prelude.hs": you can do this
by having only the Prelude loaded and then typing the ":e" command)
- Using the Hugs reduction count feature (with command ":s +s"),
see if you can determine a rule for how many reductions are performed when
an integer arithmetic expression with n operators is evaluated. (Express
your answer as a function of n.) For example, the expression
"2 + 3 * 5" has two operators, whereas "17 * (12 - 4) + 6 / 3"
has 4 operators.
Does the rule change if the literal values are of type Float rather than
integers? (Use literals like "4.5" to get Float values.)
- Where in the Haskell libraries is the list of all primes defined
(under that name) and what is its definition?
(This is really more an exercise in seraching the libraries: you might either
try some of the documentation tools or search "by hand" through the libraries
in the "C:\Haskell\Hugs" directory.)
- Name three identifiers that you cannot use in Haskell programs because
they are reserved.
(Really an exercise in finding the right part of the documentation files.)
B. Short programming exercises
Our next few labs will consist, like this section, of several relatively simple
Although the solutions to the exercises will be fairly short (compared to, say, Java),
they will still take some time to write, as you are learning both a new
language and a new style (or "paradigm).
Here are some tips and strategies for developing code:
- sketch out problems and solutions in terms of types first,
before you start coding;
- try to use pre-defined functions whenever possible (peruse the Prelude.hs
file or the Prelude tour in the Haskell docs page);
- if you have trouble seeing a solution to a problem, take time off to work
on another one or something else completely;
- try writing functions in different styles, even if you have a working
version in one style, just to acquaint yourself with other options.
Note: For the exercises involving lists, try to find pre-defined
functions from which you can build a solution. We haven't yet seen how to
define functions on lists "explicitly", by taking apart and putting together
the lists, item by item. All the functions requested here can be written more
simply by just "gluing" together other functions.
- The following function twice applies its function argument
twice to a value:
twice f x = f (f x)
Show how twice can be defined without reference to a second argument
(i.e., without the "x" or any other variable in its place).
- Consider the following functions defined using twice; for each
one, describe what it does in plain English. Also give an example
application and result for each (not just "<<function>>").
nice = twice twice
slice = twice (twice twice)
dice = (twice twice) twice
- Give alternate, simpler definitions for each of the following (they
should be equivalent to the ones given here):
foo = twice (take 10)
bar = twice (drop 10)
- Write a function stutter which will take a number n and a
string s and return
a new string which consists of n copies of s joined together. You will
want to use the concat function (defined in the prelude) which joins
together a list of strings as follows:
> concat ["this", "is", "a", "test"]
Your function should behave as follows:
> stutter 3 "foobar"
- Write another function stammer which will take a number n and a
string s and return
a new string which consists of all the characters from s, in order,
but with each one repeated n times. You may
want to use the map function (as defined in the prelude and demo'ed
in lecture). Remember also that a string is really just a list
Your function should behave as follows:
> stammer 3 "foobar"
- It's often said that two wrongs don't make a right. On the other hand,
sometimes two applications of a function get us right back where we
started from (for example, two applications of the logical "not" is just
the identity on booleans). Explain how the following functions differ,
and which one is really just an identity function:
flop = twice flip
flap = flip flip
Hint: once again, the types will be helpful.
- Define a function count which, when given a predicate (i.e.,
a boolean-valued function) and a list, will count how many items in
the list satisfy the predicate. For example, we should have:
> count even [1..10]
> count (>8) [1..10]
(Hint: I showed this briefly in class the other day, before the
WinHugs environment melted down on me.)
- The two prelude functions words and unwords
can be used to break a string into a list of words and to combine
a list of words back into a single string, respectively. For what
arguments, then, will the following function not be (quite)
the identity function?
almostID = unwords . words