Lab 2: Haskell
Overview
In class this week, we are starting to become familiar with the design and features of Haskell, a purely functional, lazy programming language.
In today’s lab, you and your partner will become familiar with writing, compiling, and running Haskell code, with a particular focus on error messages.
In this week’s assignment, you will use Haskell to do more substantial things.
Getting Haskell (0 minutes)
The Glasgow Haskell Compiler is installed on CS
department machines. There is a command-line version ghc
(which produces
executable machine code as clang++
and javac
do) and an interactive version ghci
(which
lets you type in expressions, compiles them, runs them, and prints the
answers).
We assume you will ssh
to knuth and work there (as last week).
Getting the lab files (5 minutes)
As we did last week, use the assignment workflow and the link provided on Piazza to get access to this week’s lab files.
Warmup (15 minutes)
-
Start
ghci
and get the promptPrelude>
. When you need to, you can exitghci
by entering the end-of-file keystroke Control-D at the prompt. -
Try evaluating various Haskell expressions, such as:
map (\x -> x / 2) [2.25, 3.5, 4.5, 7.2, 6.5] map (\x -> 2 / x) [2.25, 3.5, 4.5, 7.2, 6.5] map (/ 2) [2.25, 3.5, 4.5, 7.2, 6.5] map (2 /) [2.25, 3.5, 4.5, 7.2, 6.5] filter (\x -> x > 5) [2.25, 3.5, 4.5, 7.2, 6.5] filter (> 5) [2.25, 3.5, 4.5, 7.2, 6.5] filter (> "Fun") ["C", "Fortran", "Haskell", "ML"] reverse [1,2,3,4,5] reverse "Haskell" show 17 show [1,2,3,4] reverse (show [1,2,3,4]) show (reverse [1,2,3,4]) [22..47] take 15 [22..47] drop 15 [22..47] 1 : [2,3,4] ['h','e','l','l','o'] 'h' : "ello" "Hello" ++ " " ++ "World" [1,2,3,4] ++ [40,50,60,70] [2,3,4] ++ [1] import Data.List partition (> 5) [2.25, 3.5, 4.5, 7.2, 6.5] partition (> "Fun") ["C", "Fortran", "Haskell", "ML"] sort [80, 38, 23, 40, 70, 24, 8, 92, 78, 80, 42, 19, 95, 87, 4] sort "haskell = HASKELL"
-
Make sure you understand the difference between these lines:
reverse "ABC" :type reverse "ABC" reverse :type reverse
-
Come up with at least five more sample expressions to try.
Practice Reading Error Messages (20 minutes)
The files ex1.hs
through ex4.hs
contain errors. You can load a file such as ex1.hs
into ghci by starting ghci in the directory that contains the file and using the command
:load ex1
. Load each file into ghci, and fill out the corresponding
section of the file diagnose.txt
.
Tips for diagnosing and fixing Haskell error messages
-
All error messages are annotated with the filename, row, and column where ghc first detected an error. This is a good place to start your search for the error. (But also read the error message to try to understand exactly how ghc became confused.)
-
If you get lots of error messages, it is always a good idea to scroll back and see if you can fix the first error. It is often a good idea to recompile as soon as the first error is fixed. Quite frequently, one bug either leads to multiple error messages (so fixing the first error makes many error messages disappear), or the first bug confuses ghc enough that it starts complaining about perfectly good code (so that the later “error messages” are bogus).
-
There is one error that you are guaranteed to make this semester, so it’s worth learning to recognize the error message.
If you see an error message that looks like this:
error: Parse error in pattern: «function name»
here’s what’s going on: Haskell has special syntax for defining curried functions (
f a b c = ...
) where you can give names to the many arguments. It turns out that if you define a function with a complicated unparenthesized pattern (g h:t = ...
) then the Haskell parser interprets this as an attempt to define a curried function (g h : t = ...
), then gets totally confused because:
by itself is not a valid pattern for the second argument. So the Haskell rule is: if you define a function with a complex pattern, always put grouping parentheses around the pattern (g (h:t) = ...
). -
This class is not CS 131: Interpreting GHC Type Errors. Spending 20 minutes just staring at an error message is not particularly educational. If it’s taking you a long time to figure out an error message, use Stack Overflow, or ask a friend, a grutor, a prof, or Piazza for help.
Defining Functions (20 minutes)
Put the following function definitions in lab2.hs
. Recall that you can load this file into
ghci
with the :load
or :l
command. (And you will need to reload the file into ghci
every time you make changes, or you’ll be testing a stale version of the code. The :reload
or :r
command might also be helpful.)
-
Define the function
tnpo :: Integer -> Integer
that works like theTnpo
macro in Homework 1.Test the
tnpo
function inghci
with several inputs. -
Define the recursive function
rep :: Integer -> (a -> a) -> a -> a
that turns Haskell integers into Church numerals.From the type, we see that
rep
takes three arguments (via currying). We expect these equations to hold, along with all similar equations:rep 0 f b == b rep 3 f b == f (f (f b)) rep 5 f b == f (f (f (f (f b))))
You can test your definition in
ghci
by, for example, verifying that(rep 5 succ) 2
returns7
and that(rep 4 (* 2)) 1
returns16
. -
Define the recursive function
filt :: (a -> Bool) -> [a] -> [a]
that works like the built-infilter
function i.e., it returns the elements of the given list that make the given predicate returnTrue
.Verify that
filt even [1,2,3,4,5]
returns the list[2,4]
.
Don’t forget to commit your work to the subversion repository at the end of the lab period.
If you finish early…
If you finish early, you can take a look at the page that describes how to test Haskell code.
Testing is a strongly encouraged (but not required) part of the next assignment.