Lecture 7: Elementary Haskell I
--------------------------------
(contributor: Adam Shaw)
Welcome to the introductory Haskell lecture.
I refrain from presenting "Hello World" in Haskell as it is an
advanced topic.
A general comparison of Haskell and ML.
---------------------------------------
Points of similarity between ML (SML in particular) and Haskell
1. both have first-class functions (functions are values)
2. both are statically typed
3. both have polymorphic types
4. both have algebraic datatypes (& pattern matching)
5. both have automatic type inference (based on Hindley-Milner)
6. both have "layered patterns" ("as" in ML, "@" in Haskell)
7. both support a fairly conventional set of basic types
integers, reals, booleans, strings
8. both have lists as a basic and heavily used type
(even more privileged in Haskell)
Differences between ML and Haskell
1. Haskell is pure, without side effects or exceptions,
while ML has mutable data structures (refs and arrays)
and exceptions (control effects)
2. Haskell has lazy evaluation (a variant of call-by-name
evaluation with memoization), while ML has strict
evaluation. (ML can implement forms of lazy evaluation
though libraries, but there is no special syntactic
support for it.)
3. Haskell has even more special notational support for
lists -- list comprehensions.
4. Haskell has boolean guards associated with pattern
matching
5. Haskell functions are usually curried, while SML functions
use tuple or record arguments for multi-argument functions
6. Haskell has type classes to support abstraction over
types with associated interfaces.
7. ML uses modules and functors to support types with
associated interfaces and abstraction over such.
8. Haskell uses monads to simulate imperative, effectful
programming (e.g. assignable variables and I/O).
ML does not need to simulate imperative programming
since it supports it directly.
Syntactically, there are similarities between Haskell and
ML, but they differ in style. Haskell has a minimal,
calculator-style syntax. ML syntax is heavier, but probably
more readable, particularly for larger bodies of code.
-----------------------------------------------------------------
Practical issues:
1. installing ghc and ghci (Haskell Platform)
-----------------------------------------------------------------
Consider the differences between ML and Haskell in three broad
categories:
- superficial syntactic differences,
- fancy extra syntax on Haskell's part, and
- deep semantic differences with far-reaching implications.
Today's lecture is concerned with the first two. Time permitting,
we'll get into the juicy third category.
Category 1 -- superficial differences in syntax:
thing ML Haskell
types int Int
'a tree Tree a
'a list [a]
'a * 'b (a,b)
type ascr. x : int x :: Int
cons x :: xs x : xs
list append xs @ ys xs ++ ys
fun. comp. f o g f . g
anon. fn. fn x => x \ x -> x
val val n = 1 n = 1
fun fun f x = x f x = x
fun type ML
ascription fun f (b:bool) : int = if b then 1 else 0
Haskell
f :: Bool -> Int
f b = if b then 1 else 0
case case xs case xs of
of [] => 0 [] -> 0
| h::_ => h h:_ -> h
let let val x = 1 let x = 1
val y = 2 y = 2
in in
(x+y, x-y) (x+y, x-y)
end
type syn. type 'a pr = 'a * 'a type Pr a = (a,a)
datatype datatype color data color = R | G | B
= R | G | B
ML
datatype 'a tree
= Lf
| Nd of 'a * 'a tree * 'a tree
Haskell
data Tree a
= Lf
| Nd a (Tree a) (Tree a)
comments (* comm *) -- comm to EOL
{- multiline comm -}
ALSO in Haskell, String means [Char], really.
ALSO the fact that functions are usu. curried affects interfaces, i.e.,
foldr in ML is ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
in Haskell is (a -> b -> b) -> b -> [a] -> b
Note also that in Haskell, foldl and foldr have different polymorphic
types (whereas in SML they have the same type):
Prelude> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
Why is this?
foldl f b [x,y,z] ==> f (f (f b x) y) z (f :: b -> e -> b)
foldr f b [x,y,z] ==> f x (f y (f z b)) (f :: e -> b -> b)
while in SML:
foldl f b [x,y,z] ==> f(x, f(y, f(z,b))) (f : 'e * 'b -> 'b)
foldr f b [x,y,z] ==> f(z, f(y, f(x,b))) (f : 'e * 'b -> 'b)
-----------------------------------------------------------------------
Category 2 -- Haskell syntactic bonuses:
enumeration notation
[1..10]
[1,4..20]
[10,9..0]
In ML, we can generate such integer ranges using a function:
fun range (init: int, incr: int, final: int) =
if incr < 0
then if init < final then []
else init :: range(init+incr, incr, final)
else if init > final then []
else init :: range(init+incr, incr, final);
val r1 = range(1,1,10);
val r2 = range(1,3,20);
val r3 = range(10,~1,0);
A different function would have to be used for similar
sequences of reals.
Haskell also permits open-ended (infinite) enumeration
expressions:
[1..] -- all positive integers [1,2,3,4,5,...]
[1, 3 .. ] -- all odd integers [1,3,5,7,...]