Like many other modern languages, OCaml uses **lexical** (or **static**) **scoping**. That is, in OCaml, when your function includes a name that calls a variable, in the function, that variable has the value when the function is **defined**. The opposite is **dynamic scoping**, in which the variable has the value when the function is **called**. E.g.,

let a = 1 ;; (* OCaml reads this as 'from now on until otherwise specified, a = 1.' *) let f x = x + a ;; (* f x = x + a can be read as f x = x + 1 because of OCaml's lexical scope. *) let a = 2 ;; (* The old value of a is shadowed, but not overwritten. a = 2 from now on. *)

What would OCaml return when you call *f 2*? Because at the time **when f is defined**, *a = 1*, so, OCaml sees *f*‘s definition as *f x = x + 1*. Therefore, it returns

# f 2 ;; - : int = 3

Note that the result would be different for a language with dynamic scoping. With dynamic scoping, because *a = 2* **when the function is called**, *f 2* would return *4* instead.

What happens if we now define *f* again the same way?

# let f x = x + a ;; (* OCaml reads this definition as f x = x + 2, because a = 2 now. *) val f : int -> int = <fun> # f 2 ;; (* As expected, f 2 returns 2 + 2. *) - : int = 4

The new definition of *f* shadows the old definition, and so OCaml returns 4 instead of 3 in this case. Of course, the variable in question can be a function too! E.g.,

let a = 1 ;; let f x = x + a ;; let a = 100 ;; let g x = 2 * f x ;; (* OCaml reads this as g x = 2 * (x + 1) *) # g 2 ;; (* g 2 = 2 * (2 + 1) *) - : int = 6 let f x = x + a ;; (* OCaml reads this as f x = x + 100. The new definition of f shadows the old one. *) # g 2 ;; (* When g was defined, f x = x + 1, so the new definition of f does not change the value of g 2. *) - : int = 6

One have to distinguish between **an input** and **a variable called in a function** though. An input is passes on to the function at the time the function is called. That is, the latest value of the input is passed. E.g.,

let a = 100 ;; let f x = x + a ;; # let h f x = 2 * (f x) ;; (* h takes f and x as inputs. *) val h : ('a -> int) -> 'a -> int = <fun> # h f 2 ;; (* Because f x = x + 100, h f 2 = 2 * (2 + 100) = 204. *) - : int = 204

let f x = a * x ;; (* This new definition of f shadows the old one. *) # h f 2 ;; (* Because f x = a * x now, h f 2 = 2 * (100 * 2) = 400. *) - : int = 400

One advantage of lexical scope is that values can be determined at **compile time**. This is why it is also known as **early binding. **With dynamic scoping, values can in general only be determined at **run time**, and thus is known as **late binding**.

Closure is a closely related concept in a language with first-class functions, like OCaml! More later!

## Leave a Reply