Clojure let допускает несколько привязок с одним и тем же именем - PullRequest
11 голосов
/ 28 марта 2012

Я пытаюсь понять поведение, которое я заметил в Clojure.

Возможно создать привязку let с одним и тем же именем привязки, повторяющимся несколько раз:

(let [a 1 a 2 a b] a)
; (= a 2)

(let [a 1 a 2 a 3] a)
; (= a 3)

Iпонимать, что пусть привязки оцениваются, а это все в основном имеет смысл.

Насколько я понимаю из документов, «Локальные объекты, созданные с помощью let, не являются переменными. После создания их значения никогда не меняются!»

Изменил ли приведенный выше синтаксис значение привязок?

Такое ощущение, что должно появиться сообщение об ошибке.

В качестве своего рода примечания:

Интересно, что вы можете вывести вышеприведенное как JS с clojurescript:

var a__36584 = 1, b__36585 = 2, a__36586 = b__36585;
var a__30671 = 1, a__30672 = 2, a__30673 = 3;

Здесь мы можем видеть, что все значения на самом деле являются различными переменными, что указывает на то, что происходит под покровом, но некоторые пояснения были бы очень полезны.

Ответы [ 3 ]

23 голосов
/ 28 марта 2012

(let [a 1, a 2] a) функционально эквивалентно (let [a 1] (let [a 2] a)), что может быть проще для понимания.В последнем случае относительно легко понять, что вы не «модифицируете» значение a, а вводите новую, не связанную переменную с именем a с другим значением.Вы можете увидеть эффект этого с чем-то вроде (let [a 1] (let [a 2] (println a)) a) - он печатает 2, а затем возвращает 1, потому что внешний a никогда не изменяется, только временно скрывается.(let [a 1, a 2] a) просто вводит значение с именем a, которое сразу выходит из области видимости.Конечно, внешний a доступен, пока внутренний a не получит значение, поэтому вы можете сделать что-то вроде (let [a 1, a (inc a)] a).

9 голосов
/ 28 марта 2012

let в clojure ведет себя как let* из Common Lisp, то есть позволяет более поздним привязкам использовать раньше. В сочетании с переплетом это может быть полезно, например, когда вам нужно удалить некоторые слои данных чистым способом:

(let [a some-vector, a (first a), a (:key a)] a)

И, конечно, это не ошибка. Как вы заметили, эти привязки внутренне влияют на разные переменные. По сути, это неизменность лексических переменных clojure. Из-за этого повторное связывание лексических переменных имеет чистую семантику (последняя привязка «выигрывает»), и нет никаких причин ее запрещать.

6 голосов
/ 28 марта 2012

В других ответах правильно отмечалось, что синтаксис let эффективно создает новые привязки для a, которые скрывают старую привязку.

Еще один интересный дополнительный момент, который следует отметить, - это может быть очень полезно для оптимизации кода Clojure, когда вызнать, что значение будет иметь определенный тип, например:

(let [d (double d)]
  ......)

В блоке let будет приведено d, а затем использовано в качестве примитивного двойного числа, которое может существенно ускорить многие математические операции.

...