I think I can help with closures. Basically, you just have to keep in mind that functions are mathematical objects like any other:
(* A named integer, then a named function *)
let x : int = 42
let f : int -> int = fun x -> 3 * x + 2
(* an anonymous integer, then an anonymous function *)
42
fun x -> 3 * x + 2
So functions, like integers, can be expressed literally. Expressions (`2 + 2`, `map ((+) 2)`) can denote functions as well as integers. (You can make the analogy with C, where function pointers (not the full thing) can be the result of an expression).
Now imagine you pass an integer around (as parameter, return value, bound to a name, or stored in a variable), surely you don't expect it to be modified behind your back? For instance:
let x = 40 in
let y = x + 2 in (* so, that's 42... *)
let x = 0 in (* ...right? *)
print_int y (* right. It does print 42 *)
So, `y` hasn't been modified behind your back. How is this possible? Well, there's 2 ways to see this: either
(i) `y` was completely computed at the time it was defined. So, we grabbed the `x` that was in scope by the time we defined `y`, computed 40 + 2, and bam, we got 42. Of course, the value of 42 is not at all dependent on the value of the `x` name that is currently in scope.
(ii) `y` was not completely computed by the time it was defined, but only by the time we really really need it (here, at print time). But it still prints 42, not 2. The catch is, the value under `y` is made up of two things: the expression `x + 2`, and a key-value store, which will tell you the value of that `x` in the expression (here, that would be [("x", 40)]). Those two things do not change when you define a new x. And when you need to compute `y`, then you compute the expression, knowing that the above key-value store has the priority (you will see `x = 40` first, and won't bother to look up the other `x`, which has value `0`).
Functions work in exactly the same way. No trap. No trick. For instance, the program
let x = 40 in
let f = fun a -> x + a in
let x = 0 in
print_int (f 2)
will still print 42. Also, the two interpretations above still hold. Just remember that functions are values like any other.
Now there are some caveats. If you allow side effects and it is not clear whether you enclose over values or over variables (or references), then some things may indeed be modified behind your back. (Imagine that the closure uses a key variable store where you thought it would be a key value store. Then it would be possible to change the content of the variables behind your back.) Anyway, my advice is to avoid side effects as much as you can[1].
Now imagine you pass an integer around (as parameter, return value, bound to a name, or stored in a variable), surely you don't expect it to be modified behind your back? For instance:
So, `y` hasn't been modified behind your back. How is this possible? Well, there's 2 ways to see this: either(i) `y` was completely computed at the time it was defined. So, we grabbed the `x` that was in scope by the time we defined `y`, computed 40 + 2, and bam, we got 42. Of course, the value of 42 is not at all dependent on the value of the `x` name that is currently in scope.
(ii) `y` was not completely computed by the time it was defined, but only by the time we really really need it (here, at print time). But it still prints 42, not 2. The catch is, the value under `y` is made up of two things: the expression `x + 2`, and a key-value store, which will tell you the value of that `x` in the expression (here, that would be [("x", 40)]). Those two things do not change when you define a new x. And when you need to compute `y`, then you compute the expression, knowing that the above key-value store has the priority (you will see `x = 40` first, and won't bother to look up the other `x`, which has value `0`).
Functions work in exactly the same way. No trap. No trick. For instance, the program
will still print 42. Also, the two interpretations above still hold. Just remember that functions are values like any other.Now there are some caveats. If you allow side effects and it is not clear whether you enclose over values or over variables (or references), then some things may indeed be modified behind your back. (Imagine that the closure uses a key variable store where you thought it would be a key value store. Then it would be possible to change the content of the variables behind your back.) Anyway, my advice is to avoid side effects as much as you can[1].
[1]: http://www.loup-vaillant.fr/articles/assignment