Разница в F # и Clojure при вызове переопределенных функций - PullRequest
12 голосов
/ 03 апреля 2010

В 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

В 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

Обратите внимание, что в Clojure самая последняя версия f вызывается в последней строке. Однако в F # старая версия f называется. Почему это и как это работает?

Ответы [ 3 ]

12 голосов
/ 03 апреля 2010

В Clojure символ f фиксирует имя f, а в F # символ f фиксирует значение f. Поэтому в Clojure каждый раз, когда вы вызываете g, он ищет f, чтобы выяснить, к какому имени относится данный момент, тогда как в F # каждый вызов g использует значение, которое f имело, когда g Функция была изначально создана.

8 голосов
/ 03 апреля 2010

Как сказал gabe , F # интерактивный использует теневое копирование значений при вводе функции с именем, которое уже существует (для получения дополнительной информации о теневом копировании см., Например, ТАК вопрос ). Это означает, что компилятор F # видит что-то подобное при запуске вашего кода:

> 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 # использует искаженное имя (например, @), которое вы не можете использовать напрямую, чтобы различать версии значения. С другой стороны, поведение Clojure, вероятно, лучше всего понимать как большой словарь функций. Используя псевдосинтаксис, что-то вроде этого:

> 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

Это должно сделать различие совершенно ясным.

В качестве дополнительного примечания есть одна возможная проблема с подходом Clojure (по крайней мере, для такого языка, как F #). Вы можете объявить функцию некоторого типа, использовать ее, а затем следующая команда может изменить тип функции. Если F # использовал подход Clojure, как должен работать следующий пример?

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

Функция g использует f, как если бы она имела два параметра типа int, но третья строка меняет тип функции. Это делает подход Clojure немного сложным для языков с проверкой типов.

5 голосов
/ 03 апреля 2010

Гейб и Томас хорошо изучили основы. Обратите внимание, что если вы хотите, чтобы F # вел себя так же, как Clojure, вы можете использовать изменяемую привязку и переназначить 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
...