I. CCSC Haskell tutorial
A. Introduction to functional programming and Haskell
1. Functional languages: an alternative paradigm
a. Mainstream languages are imperative or object-oriented; functional programs are more like mathematical expressions, i.e., without {\bf side-effects}
2. Lack of side-effects facilitates logical reasoning and formal methods
a. In traditional languages, even the simplest aspects of algebraic and equational reasoning can fail
b. \begin{haskell}f(x) + f(x) == 2 * f(x)
g(y) == g(y)\end{haskell}
3. A high-level perspective on programming
a. Powerful abstraction features (higher-order functions, polymorphic types, etc.) allow for very concise expression of programs and a high "signal-to-noise" ratio
b. \begin{haskell}quicksort [] = []
quicksort (x:xs) = quicksort small ++ [x] ++ quicksort large
where small = [y | y<-xs, y< x]
large = [y | y<-xs, y>=x]\end{haskell}
4. Haskell and other functional programming languages
a. Most functional languages (LISP, APL, Scheme, ML) make concessions to side-effects; Haskell is a study in {\it pure} functional style (plus, it uses a familiar algebraic syntax)
5. Regional relevance in the Northwest
a. \begin{itemize}
b. \item \xlink{http://www.cse.ogi.edu/PacSoft}{the OGI Pacsoft group} is a world leader in Haskell and FP
c. \item Intel uses Haskell and other FPs for hardware modeling and development
d. \item \xlink{http://www.galoisconnections.com/}{Galois Connections}: a profitable private company with industrial and government contracts
e. \item Microsoft has bought into Haskell, both in Cambridge and Redmond
f. \end{itemize}
6. Overview of the tutorial
a. \begin{itemize}
b. \item basic syntax and features of Haskell (by example)
c. \item short applications (text processing, number bases, DFA simulation, etc.)
d. \item medium-sized example: generic sorting and simple database
e. \item medium-sized example: parsing and evaluating a simple expression language
f. \item medium-sized example: simple vector graphics package
g. \item overview of larger applications and libraries (music, graphics, animation, GUIs, web, etc.)
h. \end{itemize}
7. Haskell tools and resources (see also the physical hand-out)
a. \begin{itemize}
b. \item \xlink{http://www.haskell.org}{the Haskell.org website}
c. \item \xlink{http://www.haskell.org/hugs/}{the Hugs interactive interpreter}
d. \item \xlink{http://www.haskell.org/ghc/}{the GHC compiler and GHCi interactive system}
e. \item \xlink{http://www.haskell.org/ghc/dist/5.02/ghc-5-02.exe}{the latest Windows GHC installer (v. 5.02)}
f. \item \xlink{http://cm.bell-labs.com/cm/cs/who/wadler/realworld/}{functional programming in the real world}
g. \end{itemize}
B. Basic Haskell: interaction with the Hugs interpreter
1. A calculator-style interpretive interface
a. We'll use the Hugs interpreter: it provides an interactive command-line on which user expressions can be evaluated and printed
b. \begin{haskell}> 2 +3 * 5
17
> reverse "Hello, world!"
"!dlrow ,olleH"\end{haskell}
2. The "\$\$" short-cut
a. We can use a short-cut to refer to the last expression evaluated; this allows for a convenient exploratory paradigm
b. \begin{haskell}> 2 + 3 * 5
17
> 2 ^ $$
131072
> $$ `mod` 3
2
> replicate 3 "hello"
["hello","hello","hello"]
> concat $$
"hellohellohello"
> length $$
15\end{haskell}
3. Interpreter response versus printed output
a. The response we receive from Hugs is formatted to allow for easy re-entry; we can also print output in a more traditional format
b. \begin{haskell}> "hello, world!"
"hello, world!"
> putStr $$
hello, world!\end{haskell}
4. Interpreter commands
a. Hugs also supports a number of "meta-level" commands at the prompt, including the following useful ones:
b. \begin{itemize}
c. \item \hask{:t} -- reports the type of an expression
d. \item \hask{:s +s} -- turns on resource statistics
e. \item \hask{:n} -- looks up names based on patterns
f. \item \hask{:l} -- loads program files to provide definitions
g. \item \hask{:q} -- quits the interpreter
h. \end{itemize}
C. Basic Haskell: simple data types
1. Ints and Integers
a. The default Integer type in Haskell provides for very large numbers
b. \begin{haskell}> 2 ^ 100
1267650600228229401496703205376
> 2 ^ 200
1606938044258990275541962092341162602522202993782792835301376\end{haskell}
c. (there is also a more efficient and traditional Int type)
2. Floating-point arithmetic
a. Traditional IEEE floating-point arithmetic is also available (with the usual caveats)
b. \begin{haskell}> 2.3 ^ 5
64.36343
> $$ * 25.333
1630.51877\end{haskell}
3. Exact rational arithmetic
a. Haskell provides exact rational numbers (written with an infix "\%") as a safer alternative to floating-point
b. \begin{haskell}> 51 % 3
17 % 1
> (51 % 3) * (2 % 5)
34 % 5\end{haskell}
4. Booleans and {\tt if} expressions
a. The usual boolean values are written with initial caps; the conditional construct is an {\it expression}, not a {\it statement}
b. \begin{haskell}> if 3 < 2 then "oops" else "hurray!"
"hurray!"
> (if 3 < 2 then "oops" else "hurray!") ++ " arithmetic works!"
"hurray! arithmetic works!"
> (if 3 < 2 then (+) else (*)) 10 20
200\end{haskell}
5. Pairs and tuples
a. Unlike most languages, Haskell provides pairs and tuples as independent entities (e.g., to return multiple results)
b. \begin{haskell}> fst (2, "foo")
2
> 17 `divMod` 3
(5,2)\end{haskell}
6. The Maybe type
a. The Maybe datatype marks values as either Nothing or "Just" a single value; this allows for a natural light-weight exception mechanism
b. \begin{haskell}> lookup "sam" [("amy", 25), ("fred", 1), ("sam", 2)]
Just 2
> lookup "jim" [("amy", 25), ("fred", 1), ("sam", 2)]
Nothing\end{haskell}
D. Basic Haskell: lists and list notations
1. Lists in Haskell
a. Although Haskell provides for convenient definition of data types and structures, lists are pre-defined and ubiquitous (storage is automatically allocated and collected)
2. Bracket notation
a. Lists can be written easily using square brackets and commas as delimiters
b. \begin{haskell}> reverse [1, 2, 3, 4, 5]
[5,4,3,2,1]\end{haskell}
3. Infix notation and head, tail, null
a. In fact, lists are defined as a recursive data type (as in LISP), constructed from the head (first item) and tail
b. \begin{haskell}> head [1,2,3,4]
1
> tail [1,2,3,4]
[2,3,4]
> 1 : [2,3,4]
[1,2,3,4]
>\end{haskell}
c. (LISPs "cons" is written with an infix colon)
4. Ellipses for ordered domains
a. As a convenient further short-hand, lists over ordered domains can be written with a special ellipsis notation
b. \begin{haskell}> [1..10]
[1,2,3,4,5,6,7,8,9,10]
> [0,2..20]
[0,2,4,6,8,10,12,14,16,18,20]\end{haskell}
5. Strings as lists of characters
a. In fact, Haskell's strings are just lists over the atomic Char type (by default, they are printed using the quoted form)
b. \begin{haskell}> :t "hello"
"hello" :: String
> ['h', 'e', 'l', 'l', 'o']
"hello"
> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"\end{haskell}
6. Component and lists types
a. Haskell lists are polymorphic but homogeneous: different lists may hold different types of elements, but a single list must hold just one uniform type of element
b. \begin{haskell}> [True, False, True]
[True,False,True]
> :t $$
[True,False,True] :: [Bool]
> :t reverse
reverse :: [a] -> [a]\end{haskell}
7. Convenient list functions
a. Many useful functions on lists are provided in a standard Prelude
b. \begin{haskell}> length ['a'..'z']
26
> [1..10] ++ reverse [1..10]
[1,2,3,4,5,6,7,8,9,10,10,9,8,7,6,5,4,3,2,1]\end{haskell}
8. Infinite lists and lazy evaluation
a. Haskell provides {\bf lazy evaluation} for functions and data structures, so we can define infinite lists (it is prudent to avoid evaluation of a whole infinite list!)
b. \begin{haskell}> [1,3..]
[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,{Interrupted!}
> take 20 [1,3..]
[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39]\end{haskell}
9. Z-F expressions
a. One last notational convenience (due to David Turner) is {\bf list comprehension}, which mimics notation from Zermelo-Fraenkel set theory
b. \begin{haskell}> [ a * b | a<-[1..3], b<-reverse [1..4] ]
[4,3,2,1,8,6,4,2,12,9,6,3]
> [ 2^i | i<-[1..10], odd i ]
[2,8,32,128,512]\end{haskell}
E. Basic Haskell: higher-order functions
1. Currying
a. Haskell functions of several arguments are actually "curried", i.e., they are higher-order functions of successive arguments
b. \begin{haskell}> :t take
take :: Int -> [a] -> [a]
> :t take 10
take 10 :: [a] -> [a]
> :t take 10 ['a'..'z']
take 10 (enumFromTo 'a' 'z') :: [Char]\end{haskell}
2. Infix operators versus prefix functions
a. Infix operators are just curried functions; by default, symbolic identifiers are written infix and alphabetic ones prefix, but we can over-ride these defaults
b. \begin{haskell}> :t (+)
(+) :: Num a => a -> a -> a
> (+) 10 20
30
> 10 `divMod` 3
(3,1)\end{haskell}
3. Operator sections
a. We can conveniently apply an infix operator to just one argument by enclosing the phrase in parentheses
b. \begin{haskell}> (2^) 10
1024
> (^2) 10
100\end{haskell}
4. The map functional
a. The map functional takes a function as its first argument, then applies it to every element of a list
b. \begin{haskell}> map (^2) [1..10]
[1,4,9,16,25,36,49,64,81,100]
> map (`div` 3) [1..20]
[0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6]
> map reverse ["hey", "there", "world"]
["yeh","ereht","dlrow"]
> reverse ["hey", "there", "world"]
["world","there","hey"]\end{haskell}
5. Higher-order predicates
a. Predicates (boolean-valued functions) can be extended to lists via the higher-order predicates {\tt any} and {\tt all}
b. \begin{haskell}> map even [1..5]
[False,True,False,True,False]
> all even (map (2*) [1..5])
True
> any odd [ x^2 | x<-[1..5] ]
True\end{haskell}
6. The fold functions
a. The fold functions {\tt foldl} and {\tt foldr} combine elements of a list based on a binary function and an initial value
b. \begin{haskell}> foldr (+) 0 [1..10]
55
> sum [1..10]
55
> foldr (*) 1 [1..5] == 1 * 2 * 3 * 4 * 5 * 1
True
> foldl (&&) True (map even [2,4..10])
True\end{haskell}
7. Other useful higher-order functions
a. The standard Prelude defines scores of useful functions, many of which enjoy great generality due to the abstractional capabilities of polymorphic types and higher-order functions
b. \begin{haskell}> zipWith (*) [1..10] [1..10]
[1,4,9,16,25,36,49,64,81,100]
> :t replicate
replicate :: Int -> a -> [a]
> zipWith replicate [1..6] ['a'..'z']
["a","bb","ccc","dddd","eeeee","ffffff"]
> takeWhile (<100) [ 2^n | n<-[1..] ]
[2,4,8,16,32,64]
> :t takeWhile
takeWhile :: (a -> Bool) -> [a] -> [a]\end{haskell}
F. Basic Haskell: script or program files
1. External text files for definitions
a. The interactive command line is quite powerful by itself, but for serious programming we need to make and use {\bf definitions}; this is done in external files, which are then loaded into the interpreter
2. "Off-side" rule uses layout to control structure
a. Haskell program files are just series of definitions (values, functions, data types, etc.) which may refer to each other (and themselves, recursively)
b. Layout is used to control the nesting of local clauses in definitions: this leads to less need for punctuation
3. "Literate script" option
a. In a standard Haskell file ({\tt ".hs"} suffix) comments are set off from code (using {\tt "--"} or {\tt "\{- ... - \}" }
b. A second form, the "literate style ({\tt ".lhs"} suffix) inverts the normal code/comment relationship: code must be set off (with a {\tt "> "} prefix)
4. Simple modules system for control of namespaces
a. Although Haskell does not have a sophisticated higher-order module system (as in Standard ML), it provides modular structure with simple import commands and "hiding" specifications
G. Basic Haskell: data types and classes
1. Lists as defined data types
a. As mentioned above, lists are pre-defined in Haskell; but they behave exactly as if defined as a recursive algebraic data type
b. \begin{haskell}data List a = Nil | Cons a (List a)
[1,2,3] == 1 : (2 : (3 : [])) == Cons 1 (Cons 2 (Cons 3 Nil))\end{haskell}
c. (note that the {\it constructors} Nil and Cons are spelled differently in actual Haskell code)
2. The role of data constructors
a. Data constructors such as Nil and Cons for lists are applied like functions, but they are really only "place-holders" which build larger structures from smaller ones
3. Function definition by pattern matching
a. We can "de-construct" constructed data either with pre-defined functions (such as {\tt head} and {\tt tail}) or by using {\bf pattern-matching}, a kind of "definition by example"
b. \begin{haskell}length Nil = 0
length (Cons x y) = 1 + length y\end{haskell}
4. Other data type definitions
a. We can define our own data types in Haskell and use their constructors to build data structures or in pattern-matching to define functions on these structures
b. \begin{haskell}data BTree a = Tip | Node a (BTree a) (BTree a)
ins x Tip = Node x Tip Tip
ins x (Node y l r) | x <= y = Node y (ins x l) r
| otherwise = Node y l (ins x r)\end{haskell}
5. Higher-order, nested and other complex types
a. Haskell has a rich type system which supports powerful data structuring techniques: data types may be "higher-order" (viewed as functions from types to types) and recursion may be nested
b. \begin{haskell}data GenTree s a = Branch a (s (GenTree s a))
data PerfTree a = Empty | Split a (PerfTree (a,a))\end{haskell}
6. Class system allows for O-O style code sharing
a. In addition to powerful data types, Haskell allows types to be classified into groups based on the availability of functions matching a particular interface specification (as in many O-O languages)
b. \begin{haskell}class Printable a where
print :: a -> String
instance Printable Foo where
print x = ...\end{haskell}
END