Como ClojureScript compilar encerramentos?
-
14-11-2019 - |
Pergunta
Quando utilizar ClojureScript tentei definir uma função que é um fechamento de mais de uma variável como este:
(let [x 42]
(defn foo [n] (+ x n)))
Que imprime a seguinte origem no Rhino REPL:
function foo(n){
return cljs.core._PLUS_.call(null,x__43,n);
}
A função funciona como eu esperava, mas ao tentar chegar a variável com o nome x__43
Eu não posso fazê-lo.Para onde foi?
Solução
(let [x 42]
(defn foo [n] (+ x n)))
atualmente é compilado para
var x__1311 = 42;
cljs.user.foo = (function foo(n){
return (x__1311 + n);
});
O número exato anexado ao x
podem, é claro, variam de compilação para compilação e cljs.user
poderia ser substituído por o apropriado nome de espaço para nome.
Não há nenhuma tentativa de esconder a variável gerado a partir relacionado código em JavaScript de encerramento, de modo que, em princípio, ela poderia ser modificada se um saiu de uma maneira para fazer isso.Colisões acidentais são extremamente improvável e simplesmente não vai acontecer com regular ClojureScript.
Para descobrir coisas como o acima, você pode chamar o compilador com {:optimizations :simple :pretty-print true}
entre as opções, ou peça para que emitem um pouco de JavaScript no REPL (como o fornecido pelo script/repl
no ClojureScript árvore de origem ou lein repl
em um Leiningen projeto com ClojureScript declarado como uma dependência):
(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))))))
Outras dicas
a variável x é definido fora do foo função, no deixe de ligação.você não pode "pegar" porque você não está no escopo do let ligação.isso é mais ou menos o ponto de utilização de fechamentos.
conceitualmente, deixe enlaces são implementados como chamadas de função:
(let [x 2] ...)
é equivalente a
((fn [x] ...) 2)
o que é provavelmente semelhantes let
é implementado em ClojureScript - como uma macro de transformação para fn
ou directamente para (function(x){...})(2)
.