2017-02-04 6 views
2

В Clojure представлены переменные, определяемые через with-local-vars, доступными через закрытие?Clojure with-local-vars в закрытии

Рассмотрим следующий пример:

(defn foo [] 
     (with-local-vars [bar 10] 
      (fn [] @bar))) 

((foo)) 

Результат выглядит следующим образом: (. Вместо этого, я ожидал получить 10)

#object[clojure.lang.Var$Unbound 0x131f68d8 "Unbound: #<Var: --unnamed-->"] 

C.F. в том числе:

(defn foo [] 
    (with-local-vars [bar 10] @bar)) 
(foo) 

Результат: 10.

Основываясь на documentation, мне не ясно, если это действительно так, чтобы использовать местные вары в Clojure, как указано выше, но я бы подозревал, что ответ отрицательный. Можете ли вы подтвердить это (или опровергнуть и объяснить, что я делаю неправильно в первом примере)? И если мое предположение ясно (т. Е. Что локальные вары не могут быть использованы в закрытии), то объясните, в чем причина этого?

EDIT: для записей, this is the problem I was trying to solve.

ответ

6

Документация для with-local-vars здесь не выглядит особенно ясной. Он просто говорит, что создает потоковые локальные привязки для переменных, но ничего не говорит о том, что происходит с ними при выходе из области with-local-vars.

В отличие от этого, with-bindings documentation явно заявляет, что при выходе из этой области локально привязаны к потоку.

Если посмотреть на исходный код, вы можете увидеть, что оба with-local-vars и with-bindings реализуются с использованием тех же основных механизмов (pushThreadBindings и popThreadBindings), который предполагает, что они должны иметь почти идентичное поведение.

Итак, вы правы, вы не можете ожидать значения with-local-vars, зафиксированные в закрытии, для работы вне области with-local-vars. Тем не менее, Clojure предоставляет механизм bound-fn специально для создания такого типа закрытия, захватывая все текущие внутрипотоковые привязки:

(def foo (with-local-vars [bar 10] (fn [] @bar))) 
(foo) 
; => #object[clojure.lang.Var$Unbound 0x60063e12 "Unbound: #<Var: --unnamed-->"] 

(def baz (with-local-vars [bar 10] (bound-fn [] @bar))) 
(baz) 
; => 10 
+0

Спасибо за быстрый ответ, это, кажется, действительно правильный способ использования локальных переменных в закрытие. Приятно узнать что-то новое :) К несчастью, хотя это не решает мою оригинальную проблему, так как кажется, что значение локальной переменной «сбрасывается» с каждым вызовом возвращаемой функции (см. Также связанный с ним вопрос CodeReview в EDIT для деталей). – Attilio

+1

@Attilio - Мне непонятно, каково ваше ожидаемое поведение (в вашем другом вопросе). Что делать, если вы просто замените 'with-local-vars' в определении' logfun' на '(пусть [depth (atom 0)] ...)' вместо? Это приводит к поведению, которое вы ищете? Если нет, вы можете попытаться переместить 'let' * вне *' defn logfun' (что позволит 'logfun' закрываться над' depth', не делая его глобальным определением). – DaoWen