(* file: namesStructuredTypesMatching.ml author: Bob Muller Lecture Notes for Meeting 1 of Week 2 of CS1103. Topics: 1. Naming and let Expressions top-level let & let-in 2. Structured Types products, records & sums 3. Match Expressions Usage: You can run the code in these notes from the unix shell by typing: > make > ./go You can run all of this code except the graphics in the OCaml REPL by uncommenting the code on line 222 and commenting the code on line 223. ************************************************************* The unit Type -- () is a, in fact the -only- value of type unit. six : unit -> int *) let six () = 6 (* val six : unit -> int = NB: The notation "six : unit -> int" says that six is a name for a function that expects () as input and returns an integer as a result. Naming ****************************************************** Coders invent a lot of names for various things. Good coders spend a great deal of time thinking carefully about good names. Naming Convention: Use camelCase for names. Make them descriptive! OCaml has two main forms for introducing names for values: 1. top-level let (think global) 2. let-in (think local) *) let piTop = acos(-1.0);; let pi = piTop;; let piLocal = acos(-1.0) in piLocal *. 2.0;; (* For the let-in, the name piLocal is only useable in the single expression following the keyword "in". It's common to have sequences of let-in's. We'll parenthesize to make the scoping explicit: *) let a = 2 + 3 in (let b = a * 2 in (let c = a * b in (c + a)));; (* Simplification of let-in Expressions *********************** The above would simplify to a value in 7 steps as follows: let a = 2 + 3 in (let b = a * 2 in (let c = a * b in (c + a))) -> let a = 5 in (let b = a * 2 in (let c = a * b in (c + a))) -> let b = 5 * 2 in (let c = 5 * b in (c + 5)) -> let b = 10 in (let c = 5 * b in (c + 5)) -> let c = 5 * 10 in (c + 5) -> let c = 50 in (c + 5) -> 50 + 5 -> 55 Indentation Convention: For sequences of let-in's, stack them up as shown, putting the last "in" directly below it's corresponding let: let a = 2 + 3 in let b = a * 2 in let c = a * b in c + a Structured Types ************************************ Product Types div : int -> int -> int * int *) let div m n = (m / n, m mod n) let answer = div 25 10 let (q, r) = div 25 10 (* (q, r) is a pattern *) let (q, r) = answer (* Record Types For aggregate data, it's handy to be able to name the parts of the aggregate. This is increasingly important in virtually all modern programming languages. *) type quotient = {quotient : int; remainder : int} let q0 = {quotient = 0; remainder = 0} (* newDiv : int -> int -> quotient *) let newDiv m n = {quotient = m / n; remainder = m mod n} let answer = newDiv 25 10 let q = answer.quotient let r = answer.remainder let {quotient; remainder} = newDiv 25 10 (* pattern match *) (* Sum Types (aka Union Types, aka Variant Types) Sum types are useful when there are a small number of different kinds of values that might populate a type. *) type fruit = Apple | Orange | Fig | Lemon (* The vertical bar "|" is pronounced "or". The symbols Apple, Orange, Fig and Lemon are now constants of type fruit. These symbols must start with a capital letter. *) let myFruit : fruit = Apple;; (* Match Expressions We often write functions that accept a value of a sum type and we want the result to depend on which variation of the sum type came in. For this, we use the match expression: match expr with | pattern1 -> expr1 | ... | patternK -> exprK isCitrus : fruit -> bool *) let isCitrus someFruit = match someFruit with | Apple -> false | Orange -> true | Fig -> false | Lemon -> true (* Match clauses can sometimes be combined. isCitrus : fruit -> bool *) let isCitrus someFruit = match someFruit with | Apple | Fig -> false | Orange | Lemon -> true (* Simplification of match Expressions match 2 + 3 = 6 with | true -> 4 + 5 | false -> 5 + 5 -> match 5 = 6 with | true -> 4 + 5 | false -> 5 + 5 -> match false with | true -> 4 + 5 | false -> 5 + 5 -> 5 + 5 -> 10 NB: The "->" on the far right is our simplification arrow and is not to be confused with the same symbol used in the clauses of the match expression! The (capitalized) symbols introduced in a sum type can be simple constants, as above, or they can be "constructors" that require some input to construct a value of the type. In the definition below, the symbols Circle and Square are constructors that construct a value of type shape. The constructor Circle constructs a value of type shape only when provided with (i.e., applied to) a value of (record) type {radius : float; color : Color.t}. *) type shape = Circle of {radius : float; color : Color.t} | Square of {side : float; color : Color.t} let myCircle = Circle {radius = 10.0; color = Color.red} (* width : shape -> float *) let width someShape = match someShape with | Circle {radius; color} -> radius | Square {side; color} -> side (* area : shape -> float *) let area someShape = match someShape with | Circle {radius; color} -> pi *. radius ** 2.0 | Square {side; color} -> side ** 2.0 (* imageOf : shape -> Image.t *) let imageOf shape = match shape with | Circle {radius; color} -> Image.circle radius color | Square {side; color} -> Image.rectangle side side color (* draw : unit -> Image.t *) let draw () = let squareImg = imageOf (Square {side = 400.; color = Color.gold}) in let circleImg = imageOf (Circle {radius = 200.; color = Color.maroon}) in Image.place_image circleImg (0.0, 0.0) squareImg (* let go () = World.big_bang *) let () = World.big_bang () ~name:"Week 2 of CS1103!" ~width:800 ~height:800 ~to_draw:draw