Подтвердить что ты не робот

Разница в F # и Clojure при вызове переопределенных функций

В 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. Почему это и как это работает?

4b9b3361

Ответ 1

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

> let [email protected] x = x + 2;; 
> let [email protected] x = [email protected] x;; 
> [email protected] 10;; 
val it : int = 12
> let [email protected] x = x + 3;; 
> [email protected] 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, но строка thrid меняет тип функции. Это делает подход Clojure немного сложным для языков с проверкой типов.

Ответ 2

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

Ответ 3

Гейб и Томас хорошо освещали основы. Обратите внимание: если вы хотите, чтобы 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