Title: Introduction to ML
1Introduction to ML
- Last time
- Basics integers, Booleans, tuples,...
- simple functions
- introduction to data types
- This time, we continue writing an evaluator for a
simple language - Pierce, Chapter 4 (in OCaml)
- Show how to use
- more functions
- exceptions
- modules
2A little language (LL)
- An arithmetic expression e is
- a boolean value
- an if statement (if e1 then e2 else e3)
- the number zero
- the successor of a number
- the predecessor of a number
- a test for zero (isZero e)
3LL abstract syntax in ML
datatype declarations give you three things 1.
a new type 2. constructors functions for
building objects of the new type 3. patterns
for decomposing datatypes
datatype term Bool of bool If of term
term term Zero Successor of term
Predecessor of term IsZero of term
4LL abstract syntax in ML
If (Bool true, Zero, Successor (Successor
Zero)) represents if true then 0 else succ(succ
0)
If
Suc.
Bool
true
Zero
Suc.
Zero
5Function declarations
function name
function parameter
fun isNumberValue t case t of Zero gt
true Successor t2 gt isNumberValue t2 _
gt false
default pattern matches anything
6A very subtle error
fun isNumberValue t case t of zero gt
true Successor t2 gt isNumberValue t2 _
gt false
The code above type checks. But when we test it
refined the function always returns true. What
has gone wrong?
7A very subtle error
fun isNumberValue t case t of zero gt
true Successor t2 gt isNumberValue t2 _
gt false
The code above type checks. But when we test it
refined the function always returns true. What
has gone wrong? -- zero is not capitalized -- ML
treats it like a variable pattern (matches
anything!)
8Another function
fun isNumberValue t ... fun isValue t case
t of Bool _ gt true t gt isNumberValue t
9Exceptions
exception Error of string fun debug s unit
raise (Error s)
10Exceptions
exception Error of string fun debug s unit
raise (Error s)
in SML interpreter
- debug "hello" uncaught exception Error
raised at ex.sml15.28-15.35
11Evaluator
fun isNumberValue t ... fun isValue t
... exception NoRule fun eval1 t case t
of Bool _ Zero gt raise NoRule ...
12Evaluator
... fun eval1 t case t of Bool _ Zero
gt raise NoRule If(Bool b,t2,t3) gt (if b
then t2 else t3) If(t1,t2,t3) gt If (eval1
t1,t2,t3) ...
13Evaluator
exception NoRule fun eval1 t case t of ...
Successor t gt if isValue t then
raise NoRule else let val t
eval1 t in Successor t end
let expression let declarations in
expression end
14Finishing the Evaluator
fun eval1 t case t of ... ...
Successor t gt ... Predecessor t gt ...
IsZero t gt ...
be sure your case is exhaustive
15Finishing the Evaluator
fun eval1 t case t of ... ...
Successor t gt ...
What if we forgot a case?
16Finishing the Evaluator
fun eval1 t case t of ... ...
Successor t gt ...
What if we forgot a case?
ex.sml25.2-35.12 Warning match nonexhaustive
(Bool _ Zero) gt ... If (Bool
b,t2,t3) gt ... If (t1,t2,t3) gt ...
Successor t gt ...
17Multi-step evaluation
fun eval1 t ... fun eval t let fun
loop t loop (eval1 t) val message
Done\n in ((loop t) handle
NoRule gt print message Error s gt
print s) end
Be very careful with the syntax of handle (use
extra parens)
18Done with the evaluator
19ML is all about functions
- There are many different ways to define
functions! - I almost always use fun f x ...
- When I am only going to use a function once and
it is not recursive, I write an anonymous
function - (fn x gt ...)
20Anonymous functions
binds a variable (n) to a value (3)
val n 3 val isValue (fn t gt case
t of Bool _ gt true t gt
isNumberValue t)
binds a variable (isValue)
to the anonymous function value
fn keyword introduces anonymous fun
21Anonymous functions
a type definition (very convenient)
type ifun int -gt int val intCompose ifun
ifun -gt ifun ... fun add3 x intCompose
((fn x gt x 2), (fn y gt y 1)) x
a pair of anonymous functions
22Anonymous functions
type ifun int -gt int val intCompose ifun
ifun -gt ifun fn (f,g) gt (fn x gt f (g
x)) fun add3 x intCompose ((fn x gt x
2), (fn y gt y 1)) x
argument is pair of functions
pattern match against arg
result is a function!
23Another way to write a function
fun f x ........ can be written as val f
(fn x gt ......)
provided the function is not recursive f does
not appear in ........
24Another way to write a function
fun f x .... can always be written as val
rec f (fn x gt ...f can be used here...)
keyword rec declares a recursive function value
25Yet another way to write a function
fun isNumberValue Zero true isNumberValue
(Successor t2) true isNumberValue (_)
true
This is just an abbreviation for
fun isNumberValue t case t of Zero gt
true Successor t2 gt true _ gt true
26Yet another way to create a type error
fun isNumberValue 0 true isNumberValue
(Successor t2) true isNumberValue (_)
true
ex.sml9.1-11.29 Error parameter or result
constraints of clauses don't agree literal
this clause term -gt 'Z previous clauses
int -gt 'Z in declaration isNumberValue
(fn 0 gt true Successor t2 gt
true _ gt true)
27Parametric Polymorphism
- Functions like compose work on objects of many
different types
val compose fn f gt fn g gt fn x
gt f (g x)
compose (fn x gt x 1) (fn x gt x 2)
compose not (fn x gt x lt 17)
28Parametric Polymorphism
- Functions like compose work on objects of many
different types
val compose fn f gt fn g gt fn x
gt f (g x)
compose not (fn x gt x lt 17)
BAD!!
compose (fn x gt x lt 17) not
29Parametric Polymorphism
- Functions like compose work on objects of many
different types
val compose fn f gt fn g gt fn x
gt f (g x)
a type variable stands for any type
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
Note type variables are written with
30Parametric Polymorphism
- Functions like compose work on objects of many
different types
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
can be used as if it had the type
compose (int -gt b) -gt (c -gt int) -gt (c -gt b)
31Parametric Polymorphism
- Functions like compose work on objects of many
different types
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
can be used as if it had the type
compose ((int int) -gt b) -gt (c -gt (int
int)) -gt (c -gt b)
32Parametric Polymorphism
- Functions like compose work on objects of many
different types
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
can be used as if it had the type
compose (unit -gt int) -gt (int -gt unit) -gt (int
-gt int)
33Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
not bool -gt bool
34Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
not bool -gt bool
type of composes argument must equal the type of
not bool -gt bool (a -gt b)
35Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
not bool -gt bool
a must be bool b must be bool as well (in this
use of compose)
therefore
36Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
not bool -gt bool
a bool b bool
- compose not (c -gt bool) -gt (c -gt bool)
37Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
not bool -gt bool
- compose not (c -gt bool) -gt (c -gt bool)
- (compose not) not bool -gt bool
38Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
not bool -gt bool
- compose not (c -gt bool) -gt (c -gt bool)
- (compose not) not ??
39Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
40Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
d -gt d
41Parametric Polymorphism
compose (a -gt b) -gt (c -gt a) -gt (c -gt b)
must be the same ie a d b d
d -gt d
42Parametric Polymorphism
compose (d -gt d) -gt (c -gt d) -gt (c -gt d)
must be the same ie a d b d
d -gt d
43Parametric Polymorphism
compose (d -gt d) -gt (c -gt d) -gt (c -gt d)
d -gt d
(c -gt d) -gt (c -gt d)
44Lists
- Lists
-
- nil a list
- a a list -gt a list
- 3 4 5 nil int list
- (fn x gt x) nil (a -gt a) list
45Lists
- Lists
-
- a list
-
- 3 4, 5 int list
- fn x gt x (a -gt a) list
a different way of writing nil
46List Processing
- Functions over lists are usually defined by case
analysis (induction) over the structure of a list - Hint often, the structure of a function is
- guided by the type of the argument (recall eval)
fun length l case l of nil gt 0 l _
l gt 1 (length l)
47List Processing
fun map f l case l of nil gt l x
l gt (f x) (map f l)
an incredibly useful function - map (fn x gt
x1) 1,2,3 gt val it 2,3,4 int list
48List Processing
fun fold f a l case l of nil gt a l x
l gt f (fold f a l) x
another incredibly useful function what does it
do? what is its type? use it to write map.
49ML Modules
- Signatures
- Interfaces
- Structures
- Implementations
- Functors
- Parameterized structures
- Functions from structures to structures
50Structures
- structure Queue
- struct
- type a queue a list a list
- exception Empty
- val empty (nil, nil)
- fun insert (x, q)
- fun remove q
- end
51Structures
- structure Queue
- struct
- type a queue a list a list
- exception Empty
- ...
- end
- fun insert2 q x y
- Queue.insert (y, Queue.insert (q, x))
52Structures
- structure Queue
- struct
- ...
- end
- structure Q Queue
- fun insert2 q x y
- Q.insert (y, Q.insert (q, x))
convenient abbreviation
53Structures
- structure Queue
- struct
- ...
- end
- open Queue
- fun insert2 q x y
- insert (y, insert (q, x))
for lazy programmers -- not encouraged!
54Structures
- structure Queue
- struct
- type a queue a list a list
- ...
- end
- fun insert2 (q1,q2) x y a queue
- (xyq1,q2)
by default, all components of the structure may
be used -- we know the type a queue
55Signatures
abstract type -- we dont know the type a queue
- signature QUEUE
- sig
- type a queue
- exception Empty
- val empty a queue
- val insert a a queue -gt a queue
- val remove a queue -gt a a queue
- end
56Information hiding
structure Queue gt QUEUE struct type a
queue a list a list val empty (nil,
nil) end
- signature QUEUE
- sig
- type a queue
- ...
- end
does not type check
fun insert2 (q1,q2) x y a queue
(xyq1,q2)
57Signature Ascription
- Opaque ascription
- Provides abstract types
- structure Queue gt QUEUE
- Transparent ascription
- A special case of opaque ascription
- Hides fields but does not make types abstract
- structure Queue_E QUEUE
- SEE Harper, chapters 18-22 for more on modules
58Other things
- functors (functions from structures to
structures) - references (mutable data structures)
- ref e !e e1 e2
- while loops, for loops
- arrays
- ( comments ( can be ) nested )
- a bunch of other stuff...
59Last Things
- Learning to program in SML can be tricky at first
- But once you get used to it, you will never want
to go back to imperative languages - Check out the reference materials listed on the
course homepage