Difference in F# and Clojure when calling redefined functions
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=&g开发者_如何学Got; (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?
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.
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@1 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's behavior can be probably best understood as a big dictionary of functions. Using pseudo-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 a 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.
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
精彩评论