CS54 - Fall 2022 - Class 2
Example code in this lecture
power.sml
interval.sml
Lecture notes
admin
- Assignment 0 is due Friday at 5pm
- Laptop setup
- Corey has office hours 4-5pm today if you need help
- I'll have my office hours up soon!
- email me if you need to meet before they're up
- Mentor hours up soon
- Everyone should be added to piazza and gradescope
- PERM update
- masks
- next week
- no class Monday
- will have an assignment out on Monday, due on Sunday still
- can start before Wednesday, but will some material will covered on Wednesday
Everything is a value in SML
- notice that if-then-else represents a value
- each branch has a value of the same type
- no matter what branch we go down, we obtain a value
- we'll see this type of behavior A LOT
Look at the power3 function
- What's new in power3 we haven't seen before?
- orelse (equivalent to || in Java)
- the equivalent of && is andalso
- new language = new syntax
- There are almost always ideas in new languages that you've seen before
- Often, they're just slightly different than other languages you've seen
- Just takes a little while to get used to the syntax of the new language
- Are there any differences in functionality between these two?
- no
- power2 uses pattern matching, while power3 uses if/then
A note about formatting conventions
- Why do we format code?
- make it easier to read and understand
- What conventions have you seen so far in the code I've shown you?
- indenting schemes
- some variable naming
- because of the length of the code, more common to use simple variable names
- you'll still see some conventions as we go forward, though
- For this class you will need to follow *some* reasonable convention
- Doing what I do in class is a great idea :)
- You may also do your own if you'd prefer
- Whatever you do:
1) you need to be consistent!
2) it needs to highlight the structure of the code
Look at power4 in
power.sml code
- Any difference?
- no parentheses around parameters! (and similarly when it's called recursively)
- If we copy this into sml we get the following type signature:
val power4 = fn : int -> int -> int
- What does this say?
- it's a function
- that takes as input an int
- and returns "int -> int"
- that's another function!
- To call it, instead of passing a tuple, we just give it two arguments:
> power4 10 2;
val it = 100 : int
- What do you think happens if we just call it with one?
> power4 10;
val it = fn : int -> int
- just like the type signature said, if we give it an int we get back an int->int
- What do you think this function is?
- We have specified the first parameter (n = 10)
- It's the power4 function, but with n already assigned to 10!
- To use it, though, we need to store it somewhere
val
- the keyword "val" is used to declare a new variable
> val x = 4;
val x = 4 : int
> x;
val it = 4 : int
- Functions in SML are first-class variables. They are just like any other values
- We can store them to a variable, just like any other values:
> val tenToThe = power4 10;
val tenToThe = fn : int -> int
> tenToThe 2;
val it = 100 : int
> tenToThe 3;
val it = 1000 : int
> tenToThe 4;
val it = 10000 : int
curried vs. uncurried
- power, power2 and power3 are called "uncurried" functions
- they take a single parameter that is a tuple
- power4 is called a "curried" function
- We'll often prefer the curried version
loading files
- the SML interpreter is nice for doing interactive things and debugging, but we don't want to keep copying and pasting code into the SML interpreter
- use
- we can run the contents of an entire file through using "use", e.g.
> use "power.sml";
val power = fn : int * int -> int
val power2 = fn : int * int -> int
val power3 = fn : int * int -> int
val power4 = fn : int -> int -> int
val it = () : unit
- it behaves as if you started at the top of the file and typed each of the lines one at a time
- you'll see the output from the SML for each line
- additionally, you'll see one last line at the end which is the "result" from the actual use command
- most of the time this is how you will work:
- edit your function(s) in your text editor
- *save* the file
- "use" the file
- try out some of the functions in the interpreter
- repeat
Write a function called add that takes two integers and adds them
- Write an uncurried version
- Write a curried version
- State what the types of both would be
built in datatypes in SML
- Which ones have we seen so far?
- int
- fn
look at SMLDataTypes.pdf handout
- the most common datatypes we'll use
- int
- ~ is for negation (not the minus sign!)
- div and mod (not / and %)
- distinguishes these from real valued versions
- real
- you cannot mix ints and reals:
> 1.0 + 1;
stdIn:1.2-1.9 Error: operator and operand don't agree [literal]
operator domain: real * real
operand: real * int
in expression:
1.0 + 1
- SML error messages take a little while to get used to
- operator domain: it expects two reals
- we gave it real * int
- the location was stdIn (standard input, i.e. we typed it) line 1 characters 2-9
- trunc, round and real are used to convert between them
> 1.0 + real 1;
val it = 2.0 : real
- list
- the simplest list is just an empty list
> [];
val it = [] : 'a list
> nil;
val it = [] : 'a list
- What is the type of the empty list?
- it's a list
- we don't know the type yet!
- 'a is a type variable, it stands for *some* type
- the "fancy", way to make a list, is similar to what you may have seen in other languages:
> [1, 2, 3, 4];
val it = [1,2,3,4] : int list
- the more realistic way, though, uses the :: (append) operator
> 1::2::3::4::[];
val it = [1,2,3,4] : int list
- this more closely mirrors how the list is actually stored
- a list consists of two things:
- an element
- followed by the rest of the list
- draw the picture
- lists are very naturally recursive!
- what is the "base" case?
- empty list
- @ vs. ::
- there is also list concatenation using @
> [1] @ [2,3,4];
val it = [1,2,3,4] : int list
- be careful that you understand the distinction between these two
> [1] :: [2,3,4];
stdIn:21.1-21.15 Error: operator and operand don't agree [literal]
operator domain: int list * int list list
operand: int list * int list
in expression:
(1 :: nil) :: 2 :: 3 :: 4 :: nil
> 1 @ [2, 3, 4];
stdIn:1.2-1.15 Error: operator and operand don't agree [literal]
operator domain: 'Z list * 'Z list
operand: int * int list
in expression:
1 @ 2 :: 3 :: 4 :: nil
- string
- should feel very similar to strings in Java
> "banana";
val it = "banana" : string
- main thing to watch out for is ^ is used for concatenation (NOT +)
> "banana" ^ " cream " ^ "pie";
val it = "banana cream pie" : string
- char
- a single character
- can create constants using the # sign to indicate that it's a character and not a string
> #"a";
val it = #"a" : char
> #"C";
val it = #"C" : char
> #"ab";
stdIn:26.1-26.5 Error: character constant not length 1
- underneath the covers, each character has a number associated with it
- the numbers roughly follow the alphabetical ordering
> ord(#"a");
val it = 97 : int
> ord(#"b");
val it = 98 : int
- be careful about capitalized vs. not, etc.
> ord(#"A");
val it = 65 : int
look at the interval function in
interval.sml code
- What does it do?
- takes two numbers as parameters
- how do you know they're numbers?
- m+1
- m >= n
- creates a list (using ::)
- the list contains the number from m to n
- including m and n?
- including m
- not including n
> interval 1 10;
val it = [1,2,3,4,5,6,7,8,9] : int list
- What is its type signature (curried or uncurried)?
val interval = fn : int -> int -> int list
- curried function
- has two parameters (sort of), both ints
- gives us back a list of ints!
look at the interval2 function in
interval.sml code
- does the same thing!
- uses @ instead of ::
- notice that because we're using @, we have to do [n-1] because @ expects two lists
- functionally these behave the same, but there are some differences in performance (more on this later!)
> interval 1 100000;
val it = [1,2,3,4,5,6,7,8,9,10,11,12,...] : int list
> interval2 1 100000;
Interrupt
Comments, an aside
- SML has one type of comment, similar to Java's /* */ comment
- Comment start with (* and end with *)
- They can span multiple lines
- Commenting code:
- You should have a comment at the top of any file you create explaining what's in there, your name, etc.
- All functions should have comments above them explaining what that function does
- If there are any complicated portions of the functions (or funny parts) you should also put a small comment to the side of that line
- In general, SML tends to have less comments than other languages because the code is more compact and self-contained. BUT, you still should be putting in *some* comments