Lecture 10: Exceptions, Type Checking
----
:::warning
Announcements
- **Please sit in the first 2 rows of the room**.
- `A4: Static Types` due tonight (shorter than usual, extendable to Sunday instead of Saturday)
- `A5: Type Checking` out tomorrow
- Quiz 1 grading in progress (likely back tomorrow)
- Next help hours:
- Emily 3-5pm tomorrow
- Mine 6:30-8:30pm Monday
:::
:::success
Agenda for today
- Exceptions
- Conceptually, `try`/`with` in OCaml
- Type checking (A5)
- Dynamic vs. static type checking
- Type representations
- More details for `A5: Type Checking`
- Polymorphism (if time)
:::
# Exceptions
```ocaml
(* New exception without any carried data *)
exception NewException
(* New exception that carries an int *)
exception BadNum of int
```
## Raising exceptions
```ocaml
raise NewException
raise (BadNum -1)
```
```ocaml
try e with NewException -> 0
try e with BadNum n -> n
```
## Examples of using exceptions
```ocaml
exception NewException
(* Want data passed along with my exception *)
exception BadNum of int
let divide x y =
match y with
| 0 -> raise (BadNum 0)
(* _ is a wildcard: it matches anything, but creates no binding *)
(* like "else" in Racket/plait: fallback case *)
| _ -> x / y
```
Running this code gives:
```ocaml
(* Normal, non-exception case *)
utop # divide 4 2 ;;
- : int = 2
(* Exception case *)
utop # divide 4 0 ;;
Exception: BadNum 0.
(* Catch the exception with try/with *)
utop # try (divide 4 0) with
| BadNum n -> 0 ;;
- : int = 0
(* Catching a different exception still raises *)
utop # try (divide 4 0) with
| NewException -> 0 ;;
Exception: BadNum 0.
(* But a non-exceptional case is still fine with the "wrong" one *)
utop # try (divide 4 2) with
| NewException -> 0 ;;
- : int = 2
utop # try (divide 4 0) with
| NewException -> 999999 ;;
Exception: BadNum 0.
(* We can use a wildcase if we don't care about the value *)
utop # try (divide 4 0) with
| BadNum _ -> 999999 ;;
- : int = 999999
```
## Evaluation and typing rules for `raise`
:::info
We raise (throw) exceptions with `raise e`.
**Type-checking:**
- `e` must have type `exn`
- Result type is any type you want (!)
**Evaluation rules:**
- Do not produce a result (!)
- Stop with normal control flow
- Pass the exception produced by `e` to the nearest try expression on the call stack... (sort of like dynamic scope)
:::
## Evaluation and typing rules for `try/with`
:::info
`try... with ...` expressions can catch exceptions.
**Evaluation rules:**
- Evaluate `e1`
- But if `e1` raises an exception and that exception matches the pattern, then evaluate `e2` and that is the result
- Otherwise, `e1`'s value is the result
**Type-checking:** `e1` and `e2` must have the same type and that is the overall type.
:::
## Unit testing for exceptions
```ocaml
open Impl
(* Basic test *)
let%test "basic" = divide 4 2 = 2
(* Test that an exception is raised
Conceptually: try <call function under test>,
if no exception thrown, test should fail -> false
if excepted exception thrown, test should succeed -> true
*)
let%test "divide should raise BadNum" =
try ignore (divide 1 0); false
with BadNum 0 -> true
```
# Type checking (Assignment 5!)
## Dynamic vs. Static type checking
## Type checking analysis
### Let's consider some examples
Do the following OCaml expressions *type check*?
`1 + 2`
`1.0 + 2`
`(fun x -> x + 1) true`
# Assignment 5: simple type checking
```
(lam (x) x)
```
```
(lam (x : Int) x)
```
# Group exercise
:::success
Part 1: Design an OCaml recursive variant type to represent the types of values in MiniLangT: numbers, booleans, lists, or functions. Lists should be homogeneous.
:::
:::success
Part 2: Design an OCaml recursive variant type to represent the following cases of the grammar to an OCaml
```
<expr> ::= <num>
| true
| false
| <var>
| (+ <expr> <expr>)
| (let ((<id> <expr>)+) <expr>)
```
:::
## More on type checking for Assignment 5
`(lam ((x : Bool)) x)`
`empty`
`(empty : Num)`
```ocaml
utop # fun x -> x ;;
- : 'a -> 'a = <fun>
```
```ocaml
utop # [];;
- : 'a list = []
```
More on this next time!