Presentation is loading. Please wait.

Presentation is loading. Please wait.

Introduction to ML Last time: Basics: integers, Booleans, tuples,... simple functions introduction to data types This time, we continue writing an evaluator.

Similar presentations


Presentation on theme: "Introduction to ML Last time: Basics: integers, Booleans, tuples,... simple functions introduction to data types This time, we continue writing an evaluator."— Presentation transcript:

1 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

2 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)

3 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 declarations give you three things: 1. a new type 2. constructors functions for building objects of the new type 3. patterns for decomposing datatypes

4 LL abstract syntax in ML If (Bool true, Zero, Successor (Successor Zero)) represents “if true then 0 else succ(succ 0)” If Booltrue Zero Suc. Zero

5 Function declarations fun isNumberValue t = case t of Zero => true | Successor t2 => isNumberValue t2 | _ => false function name function parameter default pattern matches anything

6 A very subtle error fun isNumberValue t = case t of zero => true | Successor t2 => isNumberValue t2 | _ => false The code above type checks. But when we test it refined the function always returns “true.” What has gone wrong?

7 A very subtle error fun isNumberValue t = case t of zero => true | Successor t2 => isNumberValue t2 | _ => 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!)

8 Another function fun isNumberValue t =... fun isValue t = case t of Bool _ => true | t => isNumberValue t

9 Exceptions exception Error of string fun debug s : unit = raise (Error s)

10 Exceptions exception Error of string fun debug s : unit = raise (Error s) - debug "hello"; uncaught exception Error raised at: ex.sml:15.28-15.35 in SML interpreter:

11 Evaluator fun isNumberValue t =... fun isValue t =... exception NoRule fun eval1 t = case t of Bool _ | Zero => raise NoRule...

12 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)...

13 Evaluator exception 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 declarations in expression end

14 Finishing the Evaluator fun eval1 t = case t of... |... | Successor t =>... | Predecessor t =>... | IsZero t =>... be sure your case is exhaustive

15 Finishing the Evaluator fun eval1 t = case t of... |... | Successor t =>... What if we forgot a case?

16 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?

17 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)

18 Done with the evaluator

19 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 =>...)

20 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 value fn keyword introduces anonymous fun

21 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)

22 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 functions pattern match against arg

23 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........

24 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

25 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

26 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)

27 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)

28 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!!

29 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 variable stands for any type

30 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:

31 Parametric Polymorphism Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) compose: ((int * int) -> ‘b) -> (‘c -> (int * int)) -> (‘c -> ‘b) can be used as if it had the type:

32 Parametric Polymorphism Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) compose: (unit -> int) -> (int -> unit) -> (int -> int) can be used as if it had the type:

33 Parametric Polymorphism compose not : ?? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

34 Parametric Polymorphism compose not : ?? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool type of compose’s argument must equal the type of not: bool -> bool == (‘a -> ‘b)

35 Parametric Polymorphism compose not : ?? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool ‘a must be bool ‘b must be bool as well (in this use of compose) therefore:

36 Parametric Polymorphism compose not : (‘c -> bool) -> (c’ -> bool) compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool ‘a = bool ‘b = bool

37 Parametric Polymorphism compose not : (‘c -> bool) -> (c’ -> bool) (compose not) not : bool -> bool compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

38 Parametric Polymorphism compose not : (‘c -> bool) -> (c’ -> bool) (compose not) not : ?? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

39 Parametric Polymorphism compose (fn x => x) : ? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b)

40 Parametric Polymorphism compose (fn x => x) : ? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) ‘d -> ‘d

41 Parametric Polymorphism compose (fn x => x) : ? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) ‘d -> ‘d must be the same ie: ‘a = ‘d ‘b = ‘d

42 Parametric Polymorphism compose (fn x => x) : ? compose : (‘d -> ‘d) -> (‘c -> ‘d) -> (‘c -> ‘d) ‘d -> ‘d must be the same ie: ‘a = ‘d ‘b = ‘d

43 Parametric Polymorphism compose (fn x => x) : ? compose : (‘d -> ‘d) -> (‘c -> ‘d) -> (‘c -> ‘d) ‘d -> ‘d (‘c -> ‘d) -> (‘c -> ‘d)

44 Lists Lists: nil : ‘a list :: : ‘a * ‘a list -> ‘a list 3 :: 4 :: 5 :: nil : int list (fn x => x) :: nil : (‘a -> ‘a) list

45 Lists Lists: [] : ‘a list 3 :: [4, 5] : int list [fn x => x] : (‘a -> ‘a) list a different way of writing “nil”

46 List 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 => 0 l _ :: l => 1 + (length l)

47 List Processing fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) an incredibly useful function: - map (fn x => x+1) [1,2,3]; > val it = [2,3,4] ; int list

48 List Processing fun fold f a l = case l of nil => a l x :: l => f (fold f a l) x another incredibly useful function what does it do? what is its type? use it to write map.

49 ML Modules Signatures Interfaces Structures Implementations Functors Parameterized structures Functions from structures to structures

50 Structures structure Queue = struct type ‘a queue = ‘a list * ‘a list exception Empty val empty = (nil, nil) fun insert (x, q) = … fun remove q = … end

51 Structures 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))

52 Structures structure Queue = struct... end structure Q = Queue fun insert2 q x y = Q.insert (y, Q.insert (q, x)) convenient abbreviation

53 Structures structure Queue = struct... end open Queue fun insert2 q x y = insert (y, insert (q, x)) for lazy programmers -- not encouraged!

54 Structures structure Queue = struct type ‘a queue = ‘a list * ‘a list... end fun insert2 (q1,q2) x y : ‘a queue = (x::y::q1,q2) by default, all components of the structure may be used -- we know the type ‘ a queue

55 Signatures signature QUEUE = sig type ‘a queue exception Empty val empty : ‘a queue val insert : ‘a * ‘a queue -> ‘a queue val remove : ‘a queue -> ‘a * ‘a queue end abstract type -- we don ’ t know the type ‘ a queue

56 Information hiding signature QUEUE = sig type ‘a queue... end structure Queue :> QUEUE = struct type ‘ a queue = ‘ a list * ‘ a list val empty = (nil, nil) … end fun insert2 (q1,q2) x y : ‘ a queue = (x::y::q1,q2) does not type check

57 Signature Ascription Opaque ascription Provides abstract types structure Queue :> 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

58 Other 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...

59 Last 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


Download ppt "Introduction to ML Last time: Basics: integers, Booleans, tuples,... simple functions introduction to data types This time, we continue writing an evaluator."

Similar presentations


Ads by Google