views:

191

answers:

3

In F#:

> let f x = x + 2;;

val f : int -> int

> let g x = f x;;

val g : int -> int

> g 10;;
val it : int = 12
> let f x = x + 3;;

val f : int -> int

> g 10;;
val it : int = 12

In Clojure:

1:1 user=> (defn f [x] (+ x 2))
#'user/f
1:2 user=> (defn g [x] (f x))
#'user/g
1:3 user=> (g 10)
12
1:4 user=> (defn f [x] (+ x 3))
#'user/f
1:5 user=> (g 10)
13

Note that in Clojure the most recent version of f gets called in the last line. In F# however still the old version of f is called. Why is this and how does this work?

+11  A: 

In Clojure the f symbol captures the name f, while in F# the f symbol captures the value of f. So in Clojure every time you call g it looks up f to find out what the name refers to at that moment, while in F# every call to g uses the value that f had when the g function was originally created.

Gabe
+7  A: 

As gabe said, F# interactive uses shadowing of values when you enter a function with a name that already exists (for more info on shadowing, see for example this SO question). This means that the F# compiler sees something like this when you run your code:

> let f@1 x = x + 2;; 
> let g@1 x = f x;; 
> g@1 10;; 
val it : int = 12
> let f@2 x = x + 3;; 
> g@1 10;; 
val it : int = 12 

F# uses some mangled name (like @) that you cannot use directly to distinguish between versions of the value. On the other hand, Clojure behavior can be probably best understood as a big dictionary of functions. Using psudo-syntax, something like this:

> symbols[f] = fun x -> x + 2;; 
> symbols[g] = fun x -> symbols[f] x;; 
> symbols[g] 10;; 
val it : int = 12
> symbols[f] = fun x -> x + 3;; 
> symbols[g] 10;; 
val it : int = 13

This should make the distinction quite clear.

As a side-note, there is one possible problem with the Clojure approach (at least for language like F#). You can declare a function of some type, use it and then, the next command can change the type of the function. If F# used the Clojure approach, how should the following example work?

> let f a b = a + b;;
> let g x = f x x;;
> let f () = printf "f!";;
> g 0;;

The function g uses f as if it had two parameters of type int, but the thrid line changes the type of the function. This makes the Clojure approach a bit tricky for type-checked languages.

Tomas Petricek
Do you mean with shadowing that a variable in a lower scope with the same name 'shadows' those of higher scopes? In the F# interactive, should we read successive let statements as nested scopes? That would explain it! In the Clojure case it's not a matter of scope here, but really changing the root binding of the var f (vars are mutable).
Michiel Borkent
@Michiel - Yes, that's exactly right. Using the non-lightweight syntax for F#, your example would be `let f = ... in (let g = ... in (g 10; let f = ... in g 10))`, where the creation of new scopes is a bit more apparent.
kvb
+5  A: 

Gabe and Tomas have covered the basics well. Note that if you want F# to behave as Clojure does, you can use a mutable binding and reassign f:

let mutable f = fun x -> x + 2
let g x = f x

g 10;; // 12

f <- fun x -> x + 3 // note, assign new value, don't create new binding

g 10;; //13
kvb