author Jim Pryor Sun, 21 Nov 2010 12:27:13 +0000 (07:27 -0500) committer Jim Pryor Sun, 21 Nov 2010 12:27:13 +0000 (07:27 -0500)
Signed-off-by: Jim Pryor <profjim@jimpryor.net>
 week9.mdwn patch | blob | history

index 644e946..20c464d 100644 (file)
@@ -20,20 +20,21 @@ This should seem entirely familiar:

[B] let x be 1 + 2 in
let y be 10 in
-                       (x + y, x + 20)       ==> (13, 23)
+                       (x + y, x + 20)
+                                                               ; evaluates to (13, 23)

In fragment [B], we bound the variables `x` and `y` to `int`s. We can also bind variables to function values, as here:

[C] let f be (lambda (x, y) -> x + y + 1) in
(f (10, 2), f (20, 2))
-                                                                 ==> (13, 23)
+                                                               ; evaluates to (13, 23)

If the expression that evaluates to a function value has a free variable in it, like `y` in the next fragment, it's interpreted as bound to whatever value `y` has in the surrounding lexical context:

[D] let y be 3 in
let f be (lambda (x) -> x + y) in
(f (10), f (20))
-                                                                 ==> (13, 23)
+                                                               ; evaluates to (13, 23)

Other choices about how to interpret free variables are also possible (you can read about "lexical scope" versus "dynamic scope"), but what we do here is the norm in functional programming languages, and seems to be easiest for programmers to reason about.

@@ -41,7 +42,8 @@ In our next fragment, we re-use a variable that had been bound to another value

[E] let x be 4 in
let x be 3 in
-                       (x + 10, x + 20)      ==> (13, 23)
+                       (x + 10, x + 20)
+                                                               ; evaluates to (13, 23)

As you can see, the narrowest assignment is what's effective. This is just like in predicate logic: consider <code>&exist;x (Fx and &exist;x Gx)</code>. The computer-science terminology to describe this is that the narrower assignment of `x` to the value 3 **shadows** the wider assignment to 4.

@@ -56,7 +58,8 @@ Sometimes the shadowing is merely temporary, as here:
x + y
) in
; here the assignment of 3 to y has expired
-                       (f (10), y, f (20))   ==> (13, 2, 23)
+                       (f (10), y, f (20))
+                                                               ; evaluates to (13, 2, 23)

OK, now we're ready for our main event, **mutable variables.** We'll introduce new syntax to express an operation where we're not shadowing a wider assignment, but *changing* the original assignemnt:

@@ -68,7 +71,8 @@ OK, now we're ready for our main event, **mutable variables.** We'll introduce n
; here the change in what value y was assigned *sticks*
; because we *updated* the value of the original variable y
; instead of introducing a new y with a narrower scope
-                       (f (10), y, f (19))   ==> (13, 3, 23)
+                       (f (10), y, f (19))
+                                                               ; evaluates to (13, 3, 23)

In languages that have native syntax for this, there are two styles in which it can be expressed. The *implicit style* is exemplified in fragment [G] above, and also in languages like C: