690 likes | 1.1k Views
Introduction to ML. You will be responsible for learning ML on your own. Today I will cover some basics Read Robert Harper’s notes on “an introduction to ML” See course webpage for pointers and info about how to get the software. Intro to ML. Highlights Functional Language
E N D
Introduction to ML • You will be responsible for learning ML on your own. • Today I will cover some basics • Read Robert Harper’s notes on “an introduction to ML” • See course webpage for pointers and info about how to get the software
Intro to ML • Highlights • Functional Language • Functions are pervasive: • First-class values • Storable, arguments, results, nested • Strongly-typed language • Every expression has a type • Certain errors cannot occur • Polymorphic types provide flexibility • Flexible Module System • Abstract Types • Higher-order modules (functors)
Intro to ML • Interactive Language • Type in expressions • Evaluate and print type and result • Compiler as well • High-level programming features • Data types • Pattern matching • Exceptions • Mutable data discouraged
Preliminaries • Read – Eval – Print – Loop - 3 + 2;
Preliminaries • Read – Eval – Print – Loop - 3 + 2; > 5: int
Preliminaries • Read – Eval – Print – Loop - 3 + 2; > 5: int - it + 7; > 12 : int
Preliminaries • Read – Eval – Print – Loop - 3 + 2; > 5: int - it + 7; > 12 : int - it – 3; > 9 : int - 4 + true; Type clash in : 3 + true Looking for a : int Type I have found a : bool Error
Preliminaries • Read – Eval – Print – Loop - 3 + 2; > 5: int - it + 7; > 12 : int - it – 3; > 9 : int - 4 + true; Type clash in : 3 + true Looking for a : int Type I have found a : bool Error - 3 div 0; Failure : Div - run-time error
Basic Values - (); > () : unit => like “void” in C (sort of) => the uninteresting value/type - true; > true : bool - false; > false : bool - if it then 3+2 else 7; “else” clause is always necessary > 7 : int - false andalso loop_Forever; > false : bool and also, or else short-circuit eval
Basic Values Integers - 3 + 2 > 5 : int - 3 + (if not true then 5 else 7); > 10 : int No division between expressions and statements Strings - “Dave” ^ “ “ ^ “Walker”; > “Dave Walker” : string - print “foo\n”; foo > 3 : int Reals - 3.14; > 3.14 : real
Using SML/NJ • Interactive mode is a good way to start learning and to debug programs, but… • Type in a series of declarations into a “.sml” file - use “foo.sml” [opening foo.sml] … list of declarations with their types
Larger Projects • SML has its own built in interactive “make” • Pros: • It automatically does the dependency analysis for you • No crazy makefile syntax to learn • Cons: • May be more difficult to interact with other languages or tools
Compilation Manager sources.cm a.sig b.sml c.sml Group is a.sig b.sml c.sml • % sml • OS.FileSys.chDir “~/courses/510/a2”; • CM.make(); looks for “sources.cm”, analyzes dependencies • [compiling…] compiles files in group • [wrote…] saves binaries in ./CM/ • - CM.make’“myproj/”(); specify directory
What is next? • ML has a rich set of structured values • Tuples: (17, true, “stuff”) • Records: {name = “Dave”, ssn = 332177} • Lists: 3::4::5::nil or [3,4]@[5] • Datatypes • Functions • And more! • Rather than list all the details, we will write a couple of programs
An interpreter • Interpreters are usually implemented as a series of transformers: lexing/ parsing evaluate print stream of characters abstract syntax abstract value stream of characters
An interpreter compilers COS 320 US! lexing/ parsing evaluate print stream of characters abstract syntax abstract value stream of characters
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 -- constructors are capitalized -- constructors can take a single argument of a particular type type of a tuple another eg: string * char vertical bar separates alternatives
LL 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
Function declarations function name function parameter fun isNumberValue t = case t of Zero => true | Successor t2 => true | _ => false default pattern matches anything
What is the type of the parameter t? Of the function? function name function parameter fun isNumberValue t = case t of Zero => true | Successor t2 => true | _ => false default pattern matches anything
What is the type of the parameter t? Of the function? fun isNumberValue (t:term) : bool = case t of Zero => true | Successor t2 => true | _ => false val isNumberValue : term -> bool ML does type inference => you need not annotate functions yourself (but it can be helpful)
A type error fun isNumberValue t = case t of Zero => 0 | Successor t2 => true | _ => false line 22 line 25 ex.sml:22.3-25.15 Error: types of rules don't agree [literal] earlier rule(s): term -> int this rule: term -> bool in rule: Successor t2 => true
A type error Actually, ML will give you several errors in a row: ex.sml:22.3-25.15 Error: types of rules don't agree [literal] earlier rule(s): term -> int this rule: term -> bool in rule: Successor t2 => true ex.sml:22.3-25.15 Error: types of rules don't agree [literal] earlier rule(s): term -> int this rule: term -> bool in rule: _ => false
A very subtle error fun isNumberValue t = case t of zero => true | Successor t2 => true | _ => false The code above type checks. But when we 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 => true | _ => 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!)
Another function fun isNumberValue t = ... fun isValue t = case t of Bool _ => true | t => isNumberValue t
Exceptions exception Error of string fun debug s : unit = raise (Error s)
Exceptions exception Error of string fun debug s : unit = raise (Error s) in SML interpreter: - debug "hello"; uncaught exception Error raised at: ex.sml:15.28-15.35
Evaluator fun isNumberValue t = ... fun isValue t = ... exception NoRule 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) ...
Evaluator exception NoRule fun eval1 t = case t of Bool _ | Zero => ... | ... | Successor t => if isValue t then raise NoRule else let val t’ = eval1 t in Successor t’ end
Finishing the Evaluator fun eval1 t = case t of ... | ... | Successor t => ... | Predecessor t => ... | IsZero t => ... be sure your case is exhaustive
Finishing the Evaluator fun eval1 t = case t of ... | ... | Successor t => ... What if we forgot a case?
Finishing the Evaluator fun eval1 t = case t of ... | ... | Successor t => ... What if we forgot a case? ex.sml:25.2-35.12 Warning: match nonexhaustive (Bool _ | Zero) => ... If (Bool b,t2,t3) => ... If (t1,t2,t3) => ... Successor t => ...
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 very careful with the syntax of handle (use extra parens)
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 binds a variable (n) to a value (3) val n = 3 val isNumberValue = (fn t => case t of zero => true | Successor t2 => true | _ => false) binds a variable (isNumberValue) to the anonymous function value fn keyword introduces anonymous fun
Anonymous functions a type definition (very convenient) 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
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 argument is pair of functions pattern match against arg result is a function!
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 error fun 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) BAD!! compose (fn x => x < 17) not
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 ‘
Parametric Polymorphism • compose not : (‘c -> bool) -> (c’ -> bool) • compose not not : bool -> bool compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool
Parametric Polymorphism • compose (fn x => x) : ? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool
Parametric Polymorphism • compose (fn x => x) : ? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool ‘d -> ‘d