CS52 - Spring 2016 - Class 7
Example code in this lecture
directions.sml
student.sml
cs52int_examples.sml
Lecture notes
admin
- assignment 3 out
- due next Monday at 11:59pm
- start now... this is a challenging (and long) assignment
- mentor hours (go to the early ones!)
- my office hours may move for Wednesday
types seen so far
- basic types: int, bool, real, char, string
- compound types: lists and tuples
- combine existing types to make a new type
we can define our own types!
- simplest version: an enumeration type that can take a fixed set of values
datatype <name_of_type> = Option1 | Option2 | ...;
- <name_of_type> is the new type's name
- by convention, this will be lowercased
- Options are the different values is can take (called "constructors")
- by convention, these will be capitalized (but not all caps)
- define a new datatype direction that can have four possible values:
> datatype direction = North | South | East | West;
datatype direction = East | North | South | West
- we can now use any of these for "values" and SML will know what type they are
> North;
val it = North : direction
> East;
val it = East : direction
- notice that SML figures out that they are of type "direction"
- these are literal values, like writing 0 or 1
- we can also use these inside functions, for example the turnRight and turnLeft functions in
directions.sml code
- we must pattern match on *all* of the different values
- if we don't, we would get non-exhaustive search
- What will be the type signature?
- fun rotateRight = fn: direction -> direction
- direction is just like any other type
- We can call this function and pass it any direction and it will give us back another direction:
> turnRight North;
val it = East : direction
> turnRight West;
val it = North : direction
> turnRight (turnRight North);
val it = South : direction
- write a function called isVerticalDirection that takes as input a direction returns true of the direction is a vertical direction (North/South) and false otherwise
- What will be the type signature?
val isVerticalDirection = fn: direction -> bool
- look at isVerticalDirection function in
directions.sml code
- We could have done an exhaustive list, but instead use x to capture East and West
- we can do this since the patterns are matched from the top down
types that hold values
- the basic datatype declaration is useful in some cases, but is fairly limited in that we have to explicitly define all the values
- we can also construct datatypes whose values can take arguments!
datatype <name_of_type> = <constructor_expression1> | <constructor_expression2> | ...
where <constructor_expression> looks like:
<name> of <type>
(or is also just a zero parameter constructor like above, e.g. North)
- for example, look at the student type declaration in
student.sml code
- four different types of values
- Firstyear, Sophomore, Junior, Senior
- each type also can be instantiated with a string value
> Firstyear "Dave";
val it = Firstyear "Dave" : student
> Sophomore "Lucy";
val it = Sophomore "Lucy" : student
- by creating the type, we have created 4 "constructors" of the type
- just like in Java, constructors are used to create objects/values
- This allows us to write functions that can then access the values
- Look at the studentToString function in
student.sml code
- Again, we pattern match against the four type values
- The difference is that if we want to get at the value inside, we pattern match the constructor
- this is similar to getting at the head of the list or getting at the values in a tuple
- what is the type signature of studentToString?
- val studentToString = fn: student -> string
> studentToString (Firstyear "dave");
val it = "First year dave" : string
- write a function isGraduating that takes a student and returns true if that student is graduating (i.e is a senior) and false otherwise
- type signature?
val isGraduating = fn: student -> bool
- look at isGraduatingLong function in
student.sml code
- again, pattern match agains the constructors
- don't really need x at all (could have just written _ here)
- is there a better (more succinct) way of writing this function?
- look at isGraduating function in
student.sml code
- pattern match against Senior
- and then just x as a catchall for everything else
- notice that if you don't need to get at the value you can just match it to a variable
- look at the funFacts function in
student.sml code
- what does it do? what is its type signature?
- val funFacts = fn: student -> string
- how does SML know that the input type is of type student?
- because the input type of both isGraduating and studentToString is student
- takes in a student and produces a string describing that student
> funFacts (Firstyear "dave");
val it = "First year dave has more college fun coming!" : string
> funFacts (Senior "lucy");
val it = "Senior lucy is graduating!" : string
- notice that we do NOT always need to pattern match against the type
- write a function called isOlder that takes as input *two* students and returns true if the first student is strictly older than the second (academically speaking)
- what is the type signature?
val isOlder = fn: student -> student -> bool
- how many different combinations might we need to check?
- each input parameter can take on the four different years, so 4*4 = 16!
- look at isOlder function in
student.sml code
- With a bit of smart checking, we're able to reduce it to 8 patterns that we need to check
- Firstyear isn't older than anyone (pattern 1)
- Sophomores are older than first years (pattern 2), but no one else (pattern 3)
- Juniors are older than first years (pattern 4) and sophomores (pattern 5), but no one else (pattern 6)
- Seniors are not strictly older than other seniors (pattern 7), but are than everyone else (pattern 8)
order
- the isOlder function highlights that there are really three cases when we're comparing values of the same type
- older, younger and the same age
- there is a built-in type in SML called "order" that generalizes this representation
- order can take on three values:
- LESS
- EQUAL
- GREATER
- typing in any of these you'll get the order type
> EQUAL;
val it = EQUAL : order
> LESS;
val it = LESS : order
- how do you think order is define?
- just another datatype!
datatype order = LESS | EQUAL | GREATER
- look at the compareAge function in
student.sml code
- what does it do? what is its type signature?
val compareAge = fn: student -> student -> order
- compares two students and returns whether the first is older (GREATER), younger (LESS) or the same age (EQUAL) than the second
> compareAge (Firstyear "Dave") (Sophomore "Lucy");
val it = LESS : order
> compareAge (Senior "Dave") (Senior "Lucy");
val it = EQUAL : order
cs52int
- For assignment three, we're representing numbers as lists to be able to support very large numbers and numbers in different bases
- We're going to assume the lists represent positive numbers and then encapsulate functionality that operates on lists inside a dataype
datatype cs52int = Pos of int list |
Zero |
Neg of int list;
- three different value types
- Zero (representing the number 0)
- Pos and Neg representing numbers that are positive and negative
- not that the list "in" these values will always be positive
- In base 10, how would we write:
- 21?
> Pos [1, 2];
val it = Pos [1,2] : cs52int
- -23
> Neg [3, 2];
val it = Neg [3,2] : cs52int
- 0
> Zero;
val it = Zero : cs52int
- write a function called isPositive that takes as input a cs52int
- type signature?
val isPositive = fn: cs52int -> bool
- look at isPositive function in
cs52int_examples.sml code
- could pattern match against all three value types, but don't need to!
- second pattern catches anything (i.e. Zero and Neg)
- write a function called isNonzero that takes as input a cs52int
- type signature?
val isNonzero = fn: cs52int -> bool
- look at isNonzero function in
cs52int_examples.sml code
- could pattern matc
- write a function called numDigits that takes as input a cs52int and returns how many digits are in the number (in it's base)
- type signature?
val numDigits = fn: cs52int -> int
- look at numDigits function in
cs52int_examples.sml code
- In this case we do need to pattern match against all values
- why can't we write:
fun numDigits Zero = 0
| numDigits x = length x
- length is defined over lists
- x here would have type cs52int and you would get an error!