Как `let` реализован в Clojure и каковы его накладные расходы?
Я вижу два способа реализации привязок let
. Во-первых, как известно из SICP, let
может быть реализовано как лямбда-функция. Это удобно и просто, но с учетом того факта, что каждая лямбда (fn
) переводится в отдельный класс в JVM, а количество раз let
используется в средней программе, это кажется очень и очень дорогостоящим.
Во-вторых, привязки let
могут быть переведены непосредственно в локальные переменные Java . Это дает очень небольшие накладные расходы, но сохранение привязок в стеке нарушает семантику языка: в этом случае создание закрытий просто невозможно - сохраненные значения будут уничтожены сразу после разворачивания стека.
Итак, какова фактическая реализация, используемая в Clojure? Понятно, что указывается на соответствующие строки в Clojure.
Ответы
Ответ 1
let
-переменные переменные сохраняются как окончательные локальные значения в стеке.
Поскольку они являются окончательными, они могут быть привязаны к закрытию, если это необходимо (это аналогично тому, как вы можете использовать конечную локальную переменную в анонимном внутреннем классе в Java). Под капотом JVM копирует значение в объект, представляющий замыкание (где он хранится как окончательное поле). В результате закрытие все еще работает даже после того, как рама стека уходит.
В целом, ограниченные переменные чрезвычайно низки накладные расходы, вы не должны вообще колебаться от их использования с точки зрения производительности. Вероятно, на JVM не может быть лучше.
Ответ 2
Локальными переменными являются указатели, выделенные в стеке, указывающие на значения/объекты в куче. Указатель выходит из области видимости, но объект остается в живых, пока у него остается указатель.