Introduction 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 O’Caml) Show how to use: more functions exceptions modules
59
Embed
Introduction to ML Last time: Basics: integers, Booleans, tuples,... simple functions introduction to data types This time, we continue writing an evaluator.
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Introduction 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 O’Caml) Show how to use:
more functions exceptions modules
A 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)
LL abstract syntax in ML
datatype term = Bool of bool| If of term * term * term| Zero| Successor of term | Predecessor of term| IsZero of term
datatype declarationsgive you three things:
1. a new type
2. constructors functions for building objects of the new type
3. patterns for decomposingdatatypes
LL abstract syntax in ML
If (Bool true, Zero, Successor (Successor Zero))
represents “if true then 0 else succ(succ 0)”
If
Booltrue ZeroSuc.
Suc.Zero
Function declarations
fun isNumberValue t = case t of Zero => true | Successor t2 => isNumberValue t2 | _ => false
function name function parameter
default pattern matches anything
A very subtle error
fun isNumberValue t = case t of zero => true | Successor t2 => isNumberValue t2 | _ => false
The code above type checks. But whenwe test it refined the function always returns “true.”What has gone wrong?
A very subtle error
fun isNumberValue t = case t of zero => true | Successor t2 => isNumberValue t2 | _ => false
The code above type checks. But whenwe 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!)
Another function
fun isNumberValue t = ...
fun isValue t = case t of Bool _ => true | t => isNumberValue t
fun eval1 t = case t of Bool _ | Zero => raise NoRule...
Evaluator
...
fun eval1 t = case t of Bool _ | Zero => raise NoRule | If(Bool b,t2,t3) => (if b then t2 else t3) | If(t1,t2,t3) => If (eval1 t1,t2,t3) ...
Evaluatorexception NoRule
fun eval1 t = case t of ... | Successor t => if isValue t then raise NoRule else let val t’ = eval1 t in Successor t’ end
let expression:
let declarationsin expressionend
Finishing the Evaluatorfun eval1 t = case t of ... | ... | Successor t => ... | Predecessor t => ... | IsZero t => ...
be sure yourcase isexhaustive
Finishing the Evaluatorfun eval1 t = case t of ... | ... | Successor t => ... What if we
forgot a case?
Finishing the Evaluator
ex.sml:25.2-35.12 Warning: match nonexhaustive (Bool _ | Zero) => ... If (Bool b,t2,t3) => ... If (t1,t2,t3) => ... Successor t => ...
fun eval1 t = case t of ... | ... | Successor t => ... What if we
forgot a case?
Multi-step evaluation
fun eval1 t = ...
fun eval t = let fun loop t = loop (eval1 t) val message = “Done\n” in ((loop t) handle NoRule => print message | Error s => print s) end
Be verycarefulwith thesyntax ofhandle(use extra parens)
Done with the evaluator
ML 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 => ...)
Anonymous functions
val n = 3
val isValue = (fn t => case t of Bool _ => true | t => isNumberValue t)
binds a variable (n)to a value (3)
binds a variable(isValue)
to the anonymous function valuefn keyword
introducesanonymousfun
Anonymous functions
type ifun = int -> int
val intCompose : ifun * ifun -> ifun = ...
fun add3 x = intCompose ((fn x => x + 2), (fn y => y + 1)) x
a pair of anonymous functions
a type definition (very convenient)
Anonymous functions
type ifun = int -> int
val intCompose : ifun * ifun -> ifun = fn (f,g) => (fn x => f (g x))
fun add3 x = intCompose ((fn x => x + 2), (fn y => y + 1)) x
result is a function!
argument is pair of functionspatternmatchagainstarg
Another way to write a function
fun f x = ........
can be written as:
val f = (fn x => ......)
provided the function is not recursive;f does not appear in ........
Another way to write a function
fun f x = ....
can always be written as:
val rec f = (fn x => ...f can be used here...)
keyword rec declares a recursive function value
Yet 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 => true | Successor t2 => true | _ => true
Yet another way to create a type errorfun isNumberValue 0 = true | isNumberValue (Successor t2) = true | isNumberValue (_) = true
ex.sml:9.1-11.29 Error: parameter or result constraints of clauses don't agree [literal] this clause: term -> 'Z previous clauses: int -> 'Z in declaration: isNumberValue = (fn 0 => true | Successor t2 => true | _ => true)
Parametric Polymorphism Functions like compose work on objects
of many different types
val compose = fn f => fn g => fn x => f (g x)
compose (fn x => x + 1) (fn x => x + 2)
compose not (fn x => x < 17)
Parametric Polymorphism Functions like compose work on objects
of many different types
val compose = fn f => fn g => fn x => f (g x)
compose not (fn x => x < 17)
compose (fn x => x < 17) not BAD!!
Parametric Polymorphism Functions like compose work on objects
of many different types
val compose = fn f => fn g => fn x => f (g x)
compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b)
Note: type variables are written with ‘
a type variablestands forany type
Parametric Polymorphism Functions like compose work on objects
of many different types
compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b)
compose: (int -> ‘b) -> (‘c -> int) -> (‘c -> ‘b)
can be used as if it had the type:
Parametric Polymorphism Functions like compose work on objects