Difference in F# and Clojure when calling redefined functions

时光毁灭记忆、已成空白 提交于 2019-12-22 03:51:35

问题


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?


回答1:


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.




回答2:


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.




回答3:


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


来源:https://stackoverflow.com/questions/2570628/difference-in-f-and-clojure-when-calling-redefined-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!