Question: How do we know that let foo bar = (bar 3) + 5 has type val foo : (int -> int) -> int Answer: No other type assignment makes sense. 1. This type assignment makes sense. This is potentially easy to check. 2. _No_ other type assignment makes sense. Necessary therefore to go beyond "guessing" and "type checking". Need for "type inference" -- mechanically derives the type Observation: (+) : int -> int -> int Since return value of (+) is an integer, (foo bar) has to evaluate to an integer itself. For the left argument of (+) to be an integer, (bar 3) has to evaluate to an integer. Therefore, bar has to be a function which returns integer. Only way for bar to be defined on the integer 3 is for its input type to be int. Therefore, bar has to be of the type (int -> int). Therefore, foo has to be of the type (int -> int) -> int. "First-class functions" == "Higher order functions" Recall the rule for expression evaluation: e1 e2 e3 ... en "Eager" evaluation / eager semantics 1. Evaluate each of the expressions e1 e2 e3 ..., in some order 2. Require that e1 evaluates to a function (or the name of a function) 3. Apply that function to the arguments (e2 e3 ... en). "Lazy" foo addOne 1. The function name e1 needs to be evaluated, just like any other argument. 2. Justifies why Ocaml doesn't care about parenthesis. Question: Consider let foo m = let addM n = m + n in addM Function from integr to function val foo : int -> int -> int = + (2 + 3) (5 + 8) 2 + (5 + 8) 2+5+8+13 Consider let f2 m n = m + n Function from two integers to an integer val f2 : int -> int -> int = Question: Why do both have the same type? Question: Consider let f3 bar = (bar 3) + 5;; val f3 : (int -> int) -> int = Why does f3 have type (int -> int) -> int Hypothesis: The type int -> int -> int really means int -> (int -> int). Because of Ocaml's associativity rules, they are unnecessary. 2 + 5 + 8 = (2 + 5) + 8 = 2 + (5 + 8) 2 - 5 - 8 =? 2 - (5 - 8) = 2 - (-3) = 5 <== Right associative interpretation =? (2 - 5) - 8 = (-3) - 8 = -11 <== Left associative interpretation Arrows are right associative: t1 -> t2 -> t3 means t1 -> (t2 -> t3) This explains the difference between foo and f3. "Curry": A function of two arguments is really just a function from one argument to another function which is itself one argument. Product types I introduced as a mechanism to return multiple values from the same function: int * float Question: Shouldn't this also be a way to send multiple arguments to a single function? let f2 m n = m + n This function just has a single argument m, and returns another function (int -> int) of the second argument. let f4 m = let f5 n = m + n in f5 Why is the type not int * int -> int Question: Write a function which takes a pair of integers as input int * int and produces a single integer as output. (their sum). let f (a, b) = a + b let g p = (fst p) + (snd p) In both cases, the argument (a, b) or p is a pair of integers. Unpacked either using pattern matching syntax, or using fst, snd. Sojhal's: Compiler representation Even if your default position was let foo m n = m + n;; as (int * int) -> int let g m = let h n = m + n in h. int -> (int -> int) int -> int -> int g 2 3 Higher order functions have provided a way to build multi-argument functions for free. Features of the Ocaml language seen so far =========================================== F1. Basic data: int, chars, strings, lists, ... F2. Variable bindings: let expressions F3. Pattern matching F4. Writing our own functions, recursion, arrow types, multi argument, higher order functions, anonymous functions ... F5. "Fixpoints": Y combinator, ... (fix) Point of a let expression ========================= 1. Assignment statement <====== 1*. Variable binding let x = ... in ... (fun x -> ...) 2. Scopes (fun x -> ...) ... 3. Kick off evaluation m + n let x = 3 + 8 in y + 9; let x = 3 + 8 in if (y > 3) then x else y;; ==> if (y > 3) then 3 + 8 else y Problem: Can you express a let expression, (let x = e1 in e2) using one of our other language mechanisms? Or: Tell me what is special about lets? My claim: The following two expressions are equivalent: let x = e1 in e2 ==> (fun x -> e2) e1 "Desugaring" Alan Perlis: "Syntactic sugar causes cancer of the semicolon" let x = 3 + 8 in x + 9; e2; e3; ..; (fun x -> x + 9; e2; e3; ...) (3 + 8);; let x = 3 + 8 in (let y = x + 8 in (x + y + 13)) (fun x -> let y = x + 8 in (x + y + 13)) (3 + 8) (fun x -> (fun y -> x + y + 13) (x + 8)) (3 + 8) let y = x + 8 in (x + y + 13) (fun y -> x + y + 13) (x + 8) let r = 0.125 in let pi = 3.14 in pi *. r *. r (fun r -> (let pi = 3.14 in pi *. r *. r)) 0.125 (fun r -> (fun pi -> pi *. r *. r) 3.14) 0.125 ======================================================================================================================== let sum n = if n == 0 then 0 else n + sum (n - 1) in sum 5;; (fun n -> if (n == 0) then 0 else n + (fun n -> if (n == 0) then 0 else n + (fun n -> if (n == 0) then 0 else n + (fun n -> if (n == 0) then 0 else n + ??? (n - 1)) (n - 1)) (n - 1)) (n - 1)) 5 "Fixpoints"