CSE 428, SPRING 99, LECTURE 19 Two of the problems on Homework 7 require a user defined data type. The following note extends the lecture of 3/25/99 by introducing the datatype feature of SML. Prof Palamidessi will provide additional examples on class next Tuesday. The datatype declaration is used to introduce new types into SML. You do this by supplying the name of the type and the constructors for that type. For example, we can introduce a type for colors that contains three colors (constructors for colors). datatype color = red | blue | green; This will declare the type color and three constuctors: red, blue, green. These constructors can now be used within patterns. For example, fun warmth red = 1 | warmth blue = 2 | warmth green = 0; is a function that maps colors to integers. For another example, consider the data type of numbers, which is composed of either integers or real numbers. datatype number = r of real | i of int; Here there are two new constructors, r of type real -> number and i of type int -> number. These constructors can also be used in patterns. For example, the following function adds all the numbers in a list, returning the total as a real number. The function real coerces an integer to a real number. fun addnumbs(nil) = 0.0 | addnumbs(i x :: k) = real(x) + addnumbs(k) | addnumbs(r x :: k) = x + addnumbs(k); Entering the following line to the interpreter val x = addnumbs((i 1 :: r 5.4 :: i 9 :: nil)); returns val x = 15.4 : real It is possible to also introduce polymorphic user datatypes, similar to lists. Consider the following declaration of binary trees that is taken from Homework 7. datatype 'a btree = emptybt | consbt of 'a * 'a btree * 'a btree; Two new constructors are introduced, namely, emptybt of type 'a btree, and consbt of type 'a * 'a btree * 'a btree -> 'a btree. Remember that 'a is a type variable and hence can be set to any other type and that btree is a postfix operator. For example, consbt(1, consbt(2,emptybt,emptybt), consbt(3,emptybt,emptybt)) denotes the tree 1 / \ / \ 2 3 while the expression consbt(1, consbt(2, emptybt,emptybt), consbt(3, consbt(4,emptybt,emptybt), emptybt)) denotes the tree 1 / \ / \ 2 3 / / 4 We can now write functions that can compute on binary trees. fun max(x,y) = if x < y then y else x; fun height(emptybt) = 0 | height(consbt(x,left,right)) = 1 + max(height(left), height(right)); The height function computes the height of its argument. fun sum_labels(emptybt) = 0 | sum_labels(consbt(x,left,right)) = sum_labels(left) + x + sum_labels(right); This function will add up the labels in the binary tree. Its type is int btree -> int The following function takes a binary tree of integers and returns the binary tree with all the labels in it doubled. fun double(emptybt) = emptybt | double(consbt(x,left,right)) = consbt(2 * x, double(left), double(right)); *** All of the code in the above note is collected below. If you save *** the lines below into a file, say lnotes.sml, then type *** - use "lnotes.sml"; *** into the SML interpreter, that code will be loaded. datatype color = red | blue | green; fun warmth red = 1 | warmth blue = 2 | warmth green = 0; datatype number = r of real | i of int; fun addnumbs(nil) = 0.0 | addnumbs(i x :: k) = real(x) + addnumbs(k) | addnumbs(r x :: k) = x + addnumbs(k); val x = addnumbs((i 1 :: r 5.4 :: i 9 :: nil)); datatype 'a btree = emptybt | consbt of 'a * 'a btree * 'a btree; val one = consbt(1, consbt(2,emptybt,emptybt), consbt(3,emptybt,emptybt)); val two = consbt(1, consbt(2,emptybt,emptybt), consbt(3, consbt(4,emptybt,emptybt), emptybt)); fun max(x,y) = if x < y then y else x; fun height(emptybt) = 0 | height(consbt(x,left,right)) = 1 + max(height(left), height(right)); fun sum_labels(emptybt) = 0 | sum_labels(consbt(x,left,right)) = sum_labels(left) + x + sum_labels(right); fun double(emptybt) = emptybt | double(consbt(x,left,right)) = consbt(2 * x, double(left), double(right));