530 likes | 606 Views
More ML. Compiling Techniques David Walker. Today. More data structures lists More functions More modules Next week: Lexical Analysis. Lists. Lists are created with nil (makes empty list) head :: tail (makes a longer list) 5 :: nil : int list. element.
E N D
More ML Compiling Techniques David Walker
Today • More data structures • lists • More functions • More modules • Next week: Lexical Analysis
Lists • Lists are created with • nil (makes empty list) • head :: tail (makes a longer list) 5 :: nil : int list element list of elements
Lists • Lists are created with • nil (makes empty list) • head :: tail (makes a longer list) 4 :: (5 :: nil) : int list element list of elements
Lists • Lists are created with • nil (makes empty list) • head :: tail (makes a longer list) 3 :: 4 :: (5 :: nil) : int list element list of elements
Lists • Lists are created with 3 :: (4 :: (5 :: nil)) : int list 3 :: 4 :: 5 :: nil : int list (true, 1) :: (false, 2) :: nil : (bool * int) list (3 :: nil) :: (2 :: nil) :: nil : (int list) list
Lists • Lists: 3 :: [] : int list 3 :: [4, 5] : int list [true] : bool list a different way of writing “nil” a different way of writing a list
Lists • Bad List: [4]::3; ???
Lists • Bad List: [4]::3; stdIn:1.1-2.2 Error: operator and operand don't agree [literal] operator domain: int list * int list list operand: int list * int in expression: (4 :: nil) :: 3
Lists • Bad List: [true, 5]; ???
Lists • Bad List: [true, 5]; stdIn:17.1-17.9 Error: operator and operand don't agree [literal] operator domain: bool * bool list operand: bool * int list in expression: true :: 3 :: nil
List Processing • Functions over lists are usually defined by pattern matching on 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)
List Processing Two arguments f and l fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) What does it do?
List Processing fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) applies the function f to every element in the list - fun add1 x = x + 1; - map add1 [1,2,3]; > val it = [2,3,4] : int list
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 isValue = (fn t => case t of Bool _ => true | t => false) binds a variable (isValue) to the anonymous function value fn keyword introduces anonymous fun
Anonymous functions fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) fun addlist x l = map (fn y => y + x) l anonymous functions
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 isValue Num n = true | isValue (Bool b) = true | isValue (_) = false This is just an abbreviation for fun isValue t = case t of Num n => true | Bool b => true | _ => false
Yet another way to create a type error fun isValue 0 = true | isValue (Bool b) = true | isValue (_) = false 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: isValue = (fn 0 => true | Bool b => true | _ => false)
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) a type variable stands for any type compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) Note: type variables are written with ‘
Parametric Polymorphism • Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) can be used as if it had the type: compose: (int -> ‘b) -> (‘c -> int) -> (‘c -> ‘b)
Parametric Polymorphism • Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) can be used as if it had the type: compose: ((int * int) -> ‘b) -> (‘c -> (int * int)) -> (‘c -> ‘b)
Parametric Polymorphism • Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) can be used as if it had the type: compose: (unit -> int) -> (int -> unit) -> (int -> int)
Parametric Polymorphism • compose not : ?? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool
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)
Parametric Polymorphism • compose not : ?? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool therefore: ‘a must be bool ‘b must be bool as well (in this use of compose)
Parametric Polymorphism • compose not : (‘c -> bool) -> (c’ -> bool) compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool ‘a = bool ‘b = bool
Parametric Polymorphism • compose not : (‘c -> bool) -> (c’ -> bool) • (compose not) not : ?? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool
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)
Parametric Polymorphism • compose (fn x => x) : ? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) ‘d -> ‘d
Parametric Polymorphism • compose (fn x => x) : ? compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) must be the same ie: ‘a = ‘d ‘b = ‘d ‘d -> ‘d
Parametric Polymorphism • compose (fn x => x) : ? compose : (‘d -> ‘d) -> (‘c -> ‘d) -> (‘c -> ‘d) must be the same ie: ‘a = ‘d ‘b = ‘d ‘d -> ‘d
Parametric Polymorphism • compose (fn x => x) : ? compose : (‘d -> ‘d) -> (‘c -> ‘d) -> (‘c -> ‘d) ‘d -> ‘d (‘c -> ‘d) -> (‘c -> ‘d)
What is the type of map? fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l)
What is the type of map? fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) Hint: top-level shape is: .... -> ... -> ....
What is the type of map? fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) Solution: (‘a -> ‘b) -> ‘a list -> ‘b list
ML Modules • Signatures • Interfaces • Structures • Implementations • Functors • Parameterized structures • Functions from structures to structures
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
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))
Structures structure Queue = struct ... end structure Q = Queue fun insert2 q x y = Q.insert (y, Q.insert (q, x)) convenient abbreviation
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
Signatures abstract type -- we don’t know the type ‘a queue 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
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 does not type check fun insert2 (q1,q2) x y : ‘a queue = (x::y::q1,q2)