开发者

Difference in F# and Clojure when calling redefined functions

开发者 https://www.devze.com 2022-12-25 06:56 出处:网络
In F#: > let f x = x + 2;; val f : int -> int > let g x = f x;; val g : int -> int > g 10;;

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
0

精彩评论

暂无评论...
验证码 换一张
取 消