Как ClojureScript компилирует замыкания? - PullRequest
3 голосов
/ 01 марта 2012

При использовании ClojureScript я пытался определить функцию, которая является замыканием над переменной, следующим образом:

(let [x 42] 
  (defn foo [n] (+ x n)))

Это печатает следующий источник в REPL Rhino:

function foo(n){
  return cljs.core._PLUS_.call(null,x__43,n);
}

Функция работает, как я ожидаю, но при попытке получить переменную с именем x__43 я не могу ее получить. Куда это делось?

Ответы [ 2 ]

5 голосов
/ 01 марта 2012

переменная x определена вне функции foo в привязке let.вы не можете "получить это", потому что вы не находитесь в области действия привязки let.это более или менее весь смысл использования замыканий.

концептуально, пусть привязки реализованы как вызовы функций:

(let [x 2] ...)

эквивалентно

((fn [x] ...) 2)

, что вероятно аналогично let реализовано в ClojureScript - либо в виде макропреобразования в fn, либо непосредственно в (function(x){...})(2).

4 голосов
/ 02 марта 2012
(let [x 42]
  (defn foo [n] (+ x n)))

в настоящее время компилируется в

var x__1311 = 42;

cljs.user.foo = (function foo(n){
return (x__1311 + n);
});

Точное число, прикрепленное к x, может, конечно, варьироваться от компиляции к компиляции, и cljs.user будет заменено соответствующим именем пространства имен.

Не делается попытка скрыть сгенерированную переменную от несвязанного кода в замыкании JavaScript, поэтому, в принципе, она все еще может быть изменена, если кто-то попытается сделать это. Случайные столкновения крайне маловероятны и просто не произойдут с обычным ClojureScript.

Чтобы обнаружить подобные вещи, вы можете либо вызвать компилятор с {:optimizations :simple :pretty-print true} среди опций, либо попросить его выдать некоторый JavaScript при REPL (как предусмотрено script/repl в дереве исходного кода ClojureScript или lein repl в проект Leiningen с ClojureScript, объявленным как зависимость):

(require '[cljs.compiler :as comp])

(binding [comp/*cljs-ns* 'cljs.user]
  (comp/emit
   (comp/analyze {:ns {:name 'cljs.user} :context :statement :locals {}}
                 '(let [x 42] (defn foo [n] (+ x n))))))
...