# Lecture 002

## Types

Expression: value and function Type of expression: $e : t$ (expression $e$ has type $t$) Type of function: $e : t_1 \to t_2$ (expression $e$ has argument type $t_1$ and return type $t_2$)

## Declaration

### val Declarations

identifiers: variable names

val <varname> : <type> = <expr>


#### Evaluation

an output will be the reverse of a declaration

val <varname> = <value> : <type>


if the name is not provided, it will be used as default name

Expression terminated with a semicolon will be evaluated at terminal.

For homework, any semicolon will lose points

If you don't evaluate an expression, the terminal will give =

## Scope

val x : int = 10      (* *)
val y : int = x * x   (* [10/x] *)
val z : int = x + y   (* [100/y, 10/x] *)


Note that comments reflects the bindings before each line's execution. Therefore they are the scope of the line when execute.

If you bind to the same variable twice, the first binding exist prior to the second binding. See code example from Lecture_001

You can think of expressions as "something eventually will be a value" and declarations as "commands" or "actions" for the computer to do work.

## Types

infix operator: a function that can be insert between two arguments

You can use infix 9 ^ to declear infix operator ^ where 9 denotes a priority of evaluation.

### String

^: string concat

## Binding

binding: each use of val is one binding

• can be combined to form declaration

declaration:

• can be consist of multiple bindings

• two declaration written together can be thought as one declaration

• scope of declaration: consists of other declarations that can be used

• "If we can declare some “thing” using a declaration, then we say that “thing” is within the scope of that declaration."

### val expression

Without expecting type

val <varname> = <expr>


With type annotation

val <varname> : <type> = <expr>


You can also write

val <varname> = <expr> : <type>
val <varname> : <type> = <expr> : <type>


### let expression

let val < varname > = < expr1 > in < expr2 > end


### Immutability

Variables in SML refer to values, but are not re-assignable like variables in imperative programming languages. Each time a variable is declared, SML creates a new binding and binds that variable to a value. This binding is available, unchanged, throughout the scope of the declaration that introduced it. If the name was used before, the new binding shadows the previous one: the old binding is still around, but new uses of the variable refer to the most recent one.

Again, see code example from Lecture_001

### Tuples

val ( x : int , y : int ) = (3 ,4)
val z : int * int = (3 ,4)


## Writing Tests

val 3 : int = 1 + 2;
val 10 : int = 5 + 5;
val " hello , world " = " hello , " ^ " world " ;


Values: always bind themselves, and value can only be bind to itself SML/NJ will not output anything because we didn’t create any new bindings; 3 always binds 3, "hello , world " always binds " hello , world ", etc.

## Functions

### Importing from Files

use "file_name.sml";


will copy everything in the file to terminal, output will be like:


- use " playground . sml ";
[ opening playground . sml ]
...
val it = () : unit
-


### Applying Functions

Example Functions

fun fst ( x : int , y : int ) : int = x (* has type fst: int * int -> int *)
fun snd ( x : int , y : int ) : int = y
fun diag ( x : int ) : int * int = (x , x )


when evaluating diag(37) is the same as diag 37 only use parentheses as necessary, but keep order and write (2 * 6) + (3 * 5)

note: type with parentheses and without are actually different type

• so fst diag(5) is not the same as fst (diag 5)

### Function Specifications (function spec)

(* incr : int -> int
* REQUIRES : true
* ENSURES : incr x == > the next integer after x
*)
fun incr ( x : int ) : int = x + 1


## Error Message

### Operator's Type Error

operand: input provided for function operator domain: expected input type

stdIn:4.1-4.4 Error: operator and operand don’t agree [literal]
operator domain: string * string
operand:
int * int
in expression:
3 ^ 7


### Binding Type Unmatch

variable expect string, but got int

stdIn:5.5-5.24 Error: pattern and expression in val dec don’t agree
[tycon mismatch]
pattern:
string
expression:
int
in declaration:
y : string = x * x


### Unbounded Variable

stdIn:1.3 Error: unbound variable or constructor: z
stdIn:1.1 Error: unbound variable or constructor: z


## Sample Functions

A function that takes a tuple and return the sum

(* add3: int * int * int -> int
* REQUIRES: true
* ENSURES: add3 (x, y, z) == > x + y + z
*)
fun add3 (t: int * int * int): int =
let
val (x, y, z) = t
in
x+y+z
end

val 0 = add3 (0, 0, 0)
val 1 = add3 (1, 0, 0)
val 3 = add3 (0, 0, 3)


A function that flips a tuple

fun flip (t: int * string): string * int =
let
val (x, s) = t
in
(s, x)
end

val (" hi ", 6) = flip (6, "hi")


A function that returns the difference

(* diff : int * int -> int
* REQUIRES : x < y
* ENSURES : diff (x , y ) == > y - x
*)
fun diff ( x : int , y : int ) : int = y - x

val 2 = diff (3 ,5)


A function that check zero

(* isZero : int -> bool
* REQUIRES : true
* ENSURES : isZero ( x ) == > true if x is 0 , and false otherwise
*)
fun isZero ( x : int ) : bool = x = 0

val false = isZero 5

fun isZero 0 = true
| isZero ( x : int ) : bool = false

val true = isZero 0


built-in orelse function

val true = true orelse true
val true = true orelse false
val true = false orelse true
val false = false orelse false


a function that checks if one of element in tuple is zero

(* detectZeros : int * int -> bool
* REQUIRES : true
* ENSURES : detectZeros (x , y ) == > true if either x or y is zero .
*
detectZeros (x , y ) == > false otherwise
*)
fun detectZeros ( x : int , y : int ) : bool = x = 0 orelse y = 0

val true = detectZeros (0 ,3)
val true = detectZeros (3 ,0)
val false = detectZeros (3 ,3)


## Tricks

### Type Checking

op ^ to show the type of ^ "a" ^ "b" is the same as op^("a", "b") where op treats infix normally (see: https://piazza.com/class/kk66yt4thab5yx?cid=73)

### Parenthesis

fun times2 (x : int) : int = 2 * x
val 16 : int = times2 5 + 6
val 22 : int = times2 (5 + 6)


You can only pass 1 argument to a function

### Type Binding

type float = real
type point = float * float
val p: point = (1.0, 2.0)


### Functions

fun square (x:int) : int = x * x (* declearation *)
fn (x:int) => x * x (* expression *)
val square : int = (fn (x:int) => x * x) (* declaration *)


Function value of area (2.1 + 1.9) is [3.14/pi] (fn (r:real) => pi * r * r) (environment + code = closure = value of function)

Static Scope: the scope is created when function creates, and only above ("I am a professional programming language researcher, so I have opinions, and all of my opinions are correct")

val pi: real = 3.14
val area : int = (fn (r : int) => pi * r * r)
area (2.1+1.9)
(* gives 50.26 *)
val pi: int = 0
(* still gives 50.26 *)


Recursion

(* fact: int -> int
REQUIRES n >= 0
ENSURES fact n => n!
*)
fun fact (0 : int) : int = 1
|  fact (n : int) : int = n * fact (n-1)


Pattern: constants(int, bool, string, char), variable(name), tuple, wildcard(__, does not generate binding, default case)

• a function can only take one type

• the order of definition matters

• when function domain is not exhaustive

• compiler error
• raise exception (Match / Bind)
• when redundant domain

• compiler error
• only sees the first one
• you can't bind multiple times in the same clause/pattern (like (x, x) as a pattern or x: int, x: int as a pattern)

val (k, r) : int * real = (2, 3.14)?????????????????????????????
(* [2/k, 3.14/r] *)


so when doing the following, its doing pattern match (no variable, don't generate bindings)

val 1 = fact 0


Case: never use if...then...else (always put parenthesis around fn and case)

(case e of
p1 => e1
| p2 => e2
| pn => en
)


Note that pattern matching does not generate new bindings because there is no symbol to bind to.

Table of Content