Presentation is loading. Please wait.

Presentation is loading. Please wait.

A Fourth Look At ML Chapter 11 Type-Safe Data Structures Chapter ElevenModern Programming Languages1.

Similar presentations


Presentation on theme: "A Fourth Look At ML Chapter 11 Type-Safe Data Structures Chapter ElevenModern Programming Languages1."— Presentation transcript:

1 A Fourth Look At ML Chapter 11 Type-Safe Data Structures Chapter ElevenModern Programming Languages1

2 Type Definitions Predefined, but not primitive in ML: Type constructor for lists: Defined for ML in ML Chapter ElevenModern Programming Languages2 datatype bool = true | false; datatype 'element list = nil | :: of 'element * 'element list

3 Outline Enumerations Data constructors with parameters Type constructors with parameters Recursively defined type constructors Farewell to ML  ◦ We hardly knew ye! Chapter ElevenModern Programming Languages3

4 Defining Your Own Types New types can be defined using the keyword datatype These declarations define both: ◦ type constructors for making new (possibly polymorphic) types ◦ data constructors for making values of those new types Chapter ElevenModern Programming Languages4

5 Example day is the new type constructor and Mon, Tue, etc. are the new data constructors Why “constructors”? In a moment we will see how both can have parameters… Chapter ElevenModern Programming Languages5 - datatype day = Mon | Tue | Wed | Thu | Fri | Sat | Sun; datatype day = Fri | Mon | Sat | Sun | Thu | Tue | Wed - fun isWeekDay x = not (x = Sat orelse x = Sun); val isWeekDay = fn : day -> bool - isWeekDay Mon; val it = true : bool - isWeekDay Sat; val it = false : bool

6 No Parameters The type constructor day takes no parameters: it is not polymorphic, there is only one day type The data constructors Mon, Tue, etc. take no parameters: they are constant values of the day type Capitalize the names of data constructors Chapter ElevenModern Programming Languages6 - datatype day = Mon | Tue | Wed | Thu | Fri | Sat | Sun; datatype day = Fri | Mon | Sat | Sun | Thu | Tue | Wed

7 Strict Typing ML is strict about these new types, just as you would expect Unlike C enum, no implementation details are exposed to the programmer Chapter ElevenModern Programming Languages7 - datatype flip = Heads | Tails; datatype flip = Heads | Tails - fun isHeads x = (x = Heads); val isHeads = fn : flip -> bool - isHeads Tails; val it = false : bool - isHeads Mon; Error: operator and operand don't agree [tycon mismatch] operator domain: flip operand: day

8 Data Constructors In Patterns You can use the data constructors in patterns In this simple case, they are like constants But we will see more general cases next… Chapter ElevenModern Programming Languages8 fun isWeekDay Sat = false | isWeekDay Sun = false | isWeekDay _ = true;

9 The order Datatype Used with pre-defined compare functions ◦ Int.compare( ) ◦ Real, String, Char, Time, Date, … datatype order = LESS | GREATER | EQUAL; - Real.compare(1.0,2.0); val it = LESS : order - Char.compare(#"b", #"a"); val it = GREATER : order Chapter ElevenModern Programming Languages9

10 Outline Enumerations Data constructors with parameters Type constructors with parameters Recursively defined type constructors Farewell to ML Chapter ElevenModern Programming Languages10

11 Wrappers You can add a parameter of any type to a data constructor, using the keyword of : datatype exint = Value of int | PlusInf | MinusInf; In effect, such a constructor is a wrapper that contains a data item of the given type Chapter ElevenModern Programming Languages11 PlusInf Value 36 MinusInfValue 26 Value 38 Some things of type exint :

12 Value is a data constructor that takes a parameter: the value of the int to store It looks like a function that takes an int and returns an exint containing that int Chapter ElevenModern Programming Languages12 - datatype exint = Value of int | PlusInf | MinusInf; datatype exint = MinusInf | PlusInf | Value of int - PlusInf; val it = PlusInf : exint - MinusInf; val it = MinusInf : exint - Value; val it = fn : int -> exint (* Function notation! *) - Value 3; val it = Value 3 : exint

13 A Value Is Not An int Value 5 is an exint It is not an int, though it contains one How can we get the int out again? By pattern matching… Chapter ElevenModern Programming Languages13 - val x = Value 5; val x = Value 5 : exint - x+x; Error: overloaded variable not defined at type symbol: + type: exint

14 Patterns With Data Constructors To recover a data constructor’s parameters, use pattern matching So Value is no ordinary function: ordinary functions can't be pattern-matched this way Chapter ElevenModern Programming Languages14 - val (Value y) = x; Warning: binding not exhaustive Value y =... val y = 5 : int

15 An Exhaustive Pattern Like most uses of the match construct, you get a warning if it is not exhaustive An exint can be a PlusInf, a MinusInf, or a Value This one says what to do in all cases Chapter ElevenModern Programming Languages15 - val s = case x of = PlusInf => "infinity" | = MinusInf => "-infinity" | = Value y => Int.toString y; val s = "5" : string

16 Pattern-Matching Function Pattern-matching function definitions are especially important when working with your own datatypes Chapter ElevenModern Programming Languages16 - fun square PlusInf = PlusInf = | square MinusInf = PlusInf = | square (Value x) = Value (x*x); val square = fn : exint -> exint - square MinusInf; val it = PlusInf : exint - square (Value 3); val it = Value 9 : exint

17 Exception Handling (A Peek) Patterns are also used in ML for exception handling, as in this example Chapter ElevenModern Programming Languages17 - fun square PlusInf = PlusInf = | square MinusInf = PlusInf = | square (Value x) = Value (x*x) = handle Overflow => PlusInf; val square = fn : exint -> exint - square (Value 10000); val it = Value 100000000 : exint - square (Value 100000); val it = PlusInf : exint

18 Outline Enumerations Data constructors with parameters Type constructors with parameters Recursively defined type constructors Farewell to ML Chapter ElevenModern Programming Languages18

19 Partial Functions A function that is not defined for all values of its domain type is called a partial function Examples: ◦ 1/x; functions that expect a non-null pointer In ML, the option data type indicates undefined values datatype ‘a option = NONE | SOME of ‘a; Chapter ElevenModern Programming Languages19

20 Type Constructors With Parameters Type constructors can also use parameters: datatype 'a option = NONE | SOME of 'a; The parameters of a type constructor are type variables, which are used in the data constructors The result: a new polymorphic type Chapter ElevenModern Programming Languages20 NONE SOME "Hello" Values of type string option SOME "world" NONE SOME 1.5 Values of type real option SOME 123.4

21 Parameter Before Name Type constructor parameter comes before the type constructor name: datatype 'a option = NONE | SOME of 'a; We have types 'a option and int option, just like 'a list and int list Chapter ElevenModern Programming Languages21 - SOME 4; val it = SOME 4 : int option - SOME 1.2; val it = SOME 1.2 : real option - SOME "pig"; val it = SOME "pig" : string option

22 Uses For option Predefined type constructor in ML Used by predefined functions (or your own) when the result is not always defined Chapter ElevenModern Programming Languages22 - fun optdiv a b = = if b = 0 then NONE else SOME (a div b); val optdiv = fn : int -> int -> int option - optdiv 7 2; val it = SOME 3 : int option - optdiv 7 0; val it = NONE : int option

23 Working with option Retrieve the value of an option, other than NONE, with valOf Provide a default value for NONE with getOpt val x = SOME 2 : int option - valOf x; val it = 2 : int - getOpt (x,0); val it = 2 : int - getOpt (NONE, 0); val it = 0 : int See fact.sml Chapter ElevenModern Programming Languages23

24 Longer Example: bunch An 'x bunch is either a thing of type 'x, or a list of things of type 'x As usual, ML infers types: Chapter ElevenModern Programming Languages24 datatype 'x bunch = One of 'x | Group of 'x list; - One 1.0; val it = One 1.0 : real bunch - Group [true,false]; val it = Group [true,false] : bool bunch

25 Example: Polymorphism ML can infer bunch types, but does not always have to resolve them, just as with list types Chapter ElevenModern Programming Languages25 - fun size (One _) = 1 = | size (Group x) = length x; val size = fn : 'a bunch -> int - size (One 1.0); val it = 1 : int - size (Group [true,false]); val it = 2 : int

26 Example: No Polymorphism We applied the + operator (through foldr ) to the list elements So ML knows the parameter type must be int bunch Chapter ElevenModern Programming Languages26 - fun sum (One x) = x = | sum (Group xlist) = foldr (op +) 0 xlist; val sum = fn : int bunch -> int - sum (One 5); val it = 5 : int - sum (Group [1,2,3]); val it = 6 : int

27 Outline Enumerations Data constructors with parameters Type constructors with parameters Recursively defined type constructors Farewell to ML Chapter ElevenModern Programming Languages27

28 Recursively Defined Type Constructors The type constructor being defined may be used in its own data constructors: datatype intlist = INTNIL | INTCONS of int * intlist; Chapter ElevenModern Programming Languages28 Some values of type intlist :

29 Constructing Those Values Chapter ElevenModern Programming Languages29 - INTNIL; val it = INTNIL : intlist - INTCONS (1,INTNIL); val it = INTCONS (1,INTNIL) : intlist - INTCONS (1,INTCONS(2,INTNIL)); val it = INTCONS (1,INTCONS (2,INTNIL)) : intlist

30 An intlist Length Function A length function Much like you would write for native lists Except, of course, that native lists are not always lists of integers… Chapter ElevenModern Programming Languages30 fun intlistLength INTNIL = 0 | intlistLength (INTCONS(_,tail)) = 1 + (intListLength tail); fun listLength nil = 0 | listLength (_::tail) = 1 + (listLength tail);

31 Parametric List Type A parametric list type, almost like the predefined list ML handles type inference in the usual way: Chapter ElevenModern Programming Languages31 datatype 'element mylist = NIL | CONS of 'element * 'element mylist; - CONS(1.0, NIL); val it = CONS (1.0,NIL) : real mylist - CONS(1, CONS(2, NIL)); val it = CONS (1,CONS (2,NIL)) : int mylist

32 Some mylist Functions This now works almost exactly like the predefined list type constructor Of course, to add up a list you would use foldr … Chapter ElevenModern Programming Languages32 fun myListLength NIL = 0 | myListLength (CONS(_,tail)) = 1 + myListLength(tail); fun addup NIL = 0 | addup (CONS(head,tail)) = head + addup tail;

33 A foldr For mylist Definition of a function like foldr that works on 'a mylist Can now add up an int mylist x with: myfoldr (op +) 0 x One remaining difference: :: is an operator and CONS is not Chapter ElevenModern Programming Languages33 fun myfoldr f c NIL = c | myfoldr f c (CONS(a,b)) = f(a, myfoldr f c b);

34 Defining Operators ML allows new operators to be defined Like this: Chapter ElevenModern Programming Languages34 - infixr 5 CONS; infixr 5 CONS - 1 CONS 2 CONS NIL; val it = 1 CONS 2 CONS NIL : int mylist

35 Chinese Boxes Nested, square boxes ◦ like Russian Dolls Each box has a size (length of side) and a color Chapter ElevenModern Programming Languages35 datatype color = Red | Green | Blue; datatype 'a cbox = Contents of 'a option | Trio of int * color * 'a cbox;

36 Chinese Box Functions See cbox.sml makebox: int * color * 'a cbox -> 'a cbox boxcount: 'a cbox -> int tracebox: 'a cbox -> unit ◦ prints the box attributes outside-in openbox: 'a cbox -> 'a option ◦ returns the inner contents insert: int * color * 'a cbox -> 'a cbox makebox2: (int * color) list * 'a option -> 'a cbox Chapter ElevenModern Programming Languages36

37 One More Function difflist = fn : 'a cbox -> int list - difflist b; val it = [1,1,1,1] : int list Chapter ElevenModern Programming Languages37 fun difflist1 (Trio (s, _, Trio(s2,c,box))) = (s-s2)::difflist1 (Trio(s2,c,box)) | difflist1 _ = nil;

38 Layered Patterns with as Chapter ElevenModern Programming Languages38 fun difflist (Trio (s,_,b as Trio(s2,_,_))) = (s-s2)::difflist b | difflist _ = nil;

39 Polymorphic Binary Tree datatype 'data tree = Empty | Node of 'data tree * 'data * 'data tree; Chapter ElevenModern Programming Languages39 Some values of type int tree :

40 Constructing Those Values Chapter ElevenModern Programming Languages40 - val treeEmpty = Empty; val treeEmpty = Empty : 'a tree - val tree2 = Node(Empty,2,Empty); val tree2 = Node (Empty,2,Empty) : int tree - val tree123 = Node(Node(Empty,1,Empty), = 2, = Node(Empty,3,Empty));

41 Increment All Elements Chapter ElevenModern Programming Languages41 fun incall Empty = Empty | incall (Node(x,y,z)) = Node(incall x, y+1, incall z); - incall tree123; val it = Node (Node (Empty,2,Empty), 3, Node (Empty,4,Empty)) : int tree

42 Add Up The Elements Chapter ElevenModern Programming Languages42 fun sumall Empty = 0 | sumall (Node(x,y,z)) = sumall x + y + sumall z; - sumall tree123; val it = 6 : int

43 Convert To List (Polymorphic) Chapter ElevenModern Programming Languages43 (* In-order traversal *) fun listall Empty = nil | listall (Node(x,y,z)) = listall x @ y :: listall z; - listall tree123; val it = [1,2,3] : int list

44 Tree Search Chapter ElevenModern Programming Languages44 fun isintree x Empty = false | isintree x (Node(left,y,right)) = x=y orelse isintree x left orelse isintree x right; - isintree 4 tree123; val it = false : bool - isintree 3 tree123; val it = true : bool

45 Infinite Lists aka “Streams” It is possible, and often useful, to define infinite lists The elements are computed on demand Allows a high degree of separation between software components (functions) Chapter ElevenModern Programming Languages45

46 The unit Data Type Like void in C++ ◦ a “nothing” return type, or ◦ an “empty” parameter list Denoted by ( ) in ML Chapter ElevenModern Programming Languages46 - print "hello\n"; hello val it = () : unit - fun f () = "goodbye"; val f = fn : unit -> string - f(); val it = "goodbye" : string

47 Nodes for Infinite Linked Lists Recall the list type definition: The first node slot is the data The second is a “pointer” to the tail For infinite lists, we will delay the evaluation of the tail: Chapter ElevenModern Programming Languages47 datatype 'element list = nil | :: of 'element * 'element list datatype 'a stream = Nil | Cons of 'a * (unit -> 'a stream);

48 An Infinite Sequence of Integers Chapter ElevenModern Programming Languages48 - fun intsfrom k = Cons(k,fn()=>intsfrom(k+1)); val intsfrom = fn : int -> int stream - val nums = intsfrom 5; val nums = Cons (5,fn) : int stream - val Cons(head,_) = nums; val head = 5 : int - val Cons(_,f) = nums; val f = fn : unit -> int stream - val rest = f(); val rest = Cons (6,fn) : int stream - val Cons(head,_) = rest; val head = 6 : int

49 Head and Tail for Streams Chapter ElevenModern Programming Languages49 fun head Nil = raise Empty | head (Cons(h,_)) = h; fun thunk Nil = raise Empty | thunk (Cons(_,t)) = t; fun force f = f (); fun tail s = force (thunk s); (* Like the real list type, just gives the next Cons *)

50 Traversing the Stream Chapter ElevenModern Programming Languages50 - nums; val it = Cons (5,fn) : int stream - head nums; val it = 5 : int - thunk nums; val it = fn : unit -> int stream - tail nums; val it = Cons (6,fn) : int stream - val rest = tail nums; val rest = Cons (6,fn) : int stream - head rest; val it = 6 : int - tail rest; val it = Cons (7,fn) : int stream

51 Other Stream Functions Chapter ElevenModern Programming Languages51 fun next Nil = raise Empty | next c = head (tail c); fun nth 0 s = head s | nth n s = nth (n-1) (tail s); fun filter _ Nil = Nil | filter f (Cons (h,t)) = if (f h) then Cons (h, fn () => filter f (force t)) else filter f (force t); fun map_ _ Nil = Nil | map_ f (Cons(h,t)) = Cons(f h, fn() => map_ f (force t));

52 Chapter ElevenModern Programming Languages52 - nums; val it = Cons (5,fn) : int stream - next nums; val it = 6 : int - nth 5 nums; val it = 10 : int - val evens = filter (fn x => x mod 2 = 0) nums; val evens = Cons (6,fn) : int stream - head evens; val it = 6 : int - next evens; val it = 8 : int - val iseven = map (fn x => x mod 2 = 0) nums; val iseven = Cons (false,fn) : bool stream - nth 0 iseven; val it = false : bool - nth 1 iseven; val it = true : bool

53 More Examples Chapter ElevenModern Programming Languages53 - val takeeven = filter (fn x => x mod 2 = 0); val takeeven = fn : int stream -> int stream - val evens = takeeven (intsfrom 1); val evens = Cons (2,fn) : int stream - printStrm 10 evens; 2 4 6 8 10 12 14 16 18 20

54 A printStrm Function for ints Chapter ElevenModern Programming Languages54 fun printStrm _ Nil = () | printStrm 0 _ = () | printStrm n (Cons(x,rest)) = ( print(Int.toString x ^ "\n"); printStrm (n-1) (force rest) ); val printStrm = fn : int -> int stream -> unit

55 Obtaining “All” Prime Numbers Chapter ElevenModern Programming Languages55 - val primes = filter isPrime (intsfrom 2); val primes = Cons (2,fn) : int stream - strm2list 1000 primes; val it = [2,3,5,7,11,13,17,19,23,29,31,37,...] : int list - nth 1000 primes; val it = 7927 : int - nth 10000 primes; val it = 104743 : int -nth 100000 primes; val it = 1299721 : int (* took 1 second *) - nth 1000000 primes; val it = 15485867 : int (* took 21 seconds *)

56 Implementing foldl Remember foldl f c xs = f(x n,f(x n-1,…,f(x 2,f(x 1,c))…)) fun foldl _ c nil = c | foldl f c (x::xs) = foldl f (f (x,c)) xs; Chapter ElevenModern Programming Languages 56

57 foldl for Streams Chapter ElevenModern Programming Languages57 fun foldl_ _ c Nil = Cons(c, fn()=>Nil) | foldl_ f c (Cons(a,rest)) = let val v = f (a,c) in Cons(v, fn () => foldl_ f v (force rest)) end;

58 More Examples Composing Even More Streams Chapter ElevenModern Programming Languages58 -fun sum strm = foldl_ (op +) 0 strm; val sum = fn : int stream -> int stream - val addevens = sum (takeeven (intsfrom 1)); val addevens = Cons (2,fn) : int stream - printStrm 10 addevens; 2 6 12 20 30 42 56 72 90 110

59 Yet More! Chapter ElevenModern Programming Languages59 - fun drop _ Nil = Nil | drop 0 strm = strm | drop n (Cons(x,rest)) = drop (n-1) (force rest); val drop = fn : int -> 'a stream -> 'a stream - val less5 = drop 5 nums; val less5 = Cons (10,fn) : int stream - printStrm 5 less5; 10 11 12 13 14 val it = () : unit - fun strm2list _ Nil = nil | strm2list 0 _ = nil | strm2list n (Cons(x,rest)) = x::(strm2list (n-1) (force rest)); val strm2list = fn : int -> 'a stream -> 'a list - strm2list 5 less5; val it = [10,11,12,13,14] : int list

60 Even More Examples From Mathematics See: ◦ iterate.sml ◦ newton.sml ◦ taylor.sml Chapter ElevenModern Programming Languages60

61 Streams in Python Uses generators ◦ functions that can be resumed ◦ thereby producing a sequence of values See stream.py, iterate.py, newton.py Chapter ElevenModern Programming Languages 61


Download ppt "A Fourth Look At ML Chapter 11 Type-Safe Data Structures Chapter ElevenModern Programming Languages1."

Similar presentations


Ads by Google