Динамически впусти в Clojure? - PullRequest
4 голосов
/ 20 января 2012

У меня происходит следующее в REPL:

mathematics.core> (let [zebra 1] (resolve 'zebra))
nil
mathematics.core> (def zebra 1)
#'mathematics.core/zebra
mathematics.core> (let [zebra 2] (when (resolve 'zebra) (eval 'zebra))) 
1

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

mathematics.core> (def ^:dynamic zebra 1)
#'mathematics.core/zebra   
mathematics.core> (binding [zebra 2] (when (resolve 'zebra) (eval 'zebra))) 
2

binding, кажется, делает то, что я хочу, но AFAIK требует, чтобы сначала была определена переменная с метаданными :dynamic. Я хочу иметь возможность использовать переменные, которые никогда не были определены ранее на лету, и иметь выражения в форме, чтобы иметь доступ к этой переменной, как если бы она была фактически определена.

Чтобы проиллюстрировать, я хочу что-то вроде этого:

mathematics.core> (let-dynamic [undefined-variable 1]
                    (when (resolve 'undefined-variable) (eval 'unresolved-variable)))
1

Есть ли простой способ сделать это? Или способ сделать это с помощью макросов?

Ответы [ 3 ]

4 голосов
/ 20 января 2012

Это не сработает особенно хорошо.Если символ не определен, то компилятор Clojure не может скомпилировать любой код, который его использует.Возможно, вам удастся получить какой-нибудь хак, работающий с макросами, которые лениво вызывают def при необходимости, но это будет довольно неприятный код .....

Я бы предложил просто использовать связывание и определить ваши переменныезаблаговременно.Вы должны быть в состоянии написать свой код так, как это работает.

Я считаю плохой идеей определять переменные "на лету".Я не думаю, что вам когда-либо это действительно нужно - если вы используете переменную в коде, конечно, достаточно просто сделать (def ^:dynamic ...) заранее для каждой переменной, которую вы используете?

3 голосов
/ 20 января 2012

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

Это не похоже на хорошее совпадение с переменными clojure или значениями let-bound, и если вы генерируете и оцениваете всю форму на лету в любом случае , почему бы не использовать простая карта для хранения символа -> отображения значений и замены всей схемы разрешения / оценки с поиском карты? Таким образом, вы можете генерировать произвольные символы на лету без каких-либо непонятных хитростей в пространстве имен, которые могут нарушить ваш код некоторыми труднодоступными способами:

(let [my-resolve {'zebra 1}]
   (println "zebra is " (my-resolve 'zebra)))
2 голосов
/ 20 января 2012

Хотя это не полное решение, вот попытка:

(defmacro let-dynamic 
      ([[sym val & more] & body]
       `(do (when (not (:dynamic (meta (resolve '~sym))))
              (def ~(with-meta sym {:dynamic true}) ~sym))
            (binding [~sym ~val] 
              ~@(if (empty? more)
                    body
                    `((let-dynamic ~more ~@body)))))))

Небольшой тест:

blub> (def ^:dynamic already-dynamic 'dynamic)
#'blub/already-dynamic
blub> (def not-dynamic 'not-dynamic)
#'blub/not-dynamic
blub> (let-dynamic [already-dynamic 2] already-dynamic)
2
blub> (let-dynamic [not-dynamic 2] not-dynamic)
2
blub> (let-dynamic [not-dynamic-and-not-defined 2] not-dynamic-and-not-defined)
2
blub> 

Есть несколько проблем с этим:

  1. По некоторым причинам передача нескольких пар символ-значение в LET-DYNAMIC не работает должным образом.Они будут связаны только во время второго вызова LET-DYNAMIC.Вероятно, это можно решить, изменив макрос для передачи всех пар символ-значение в первую форму привязки, вместо того чтобы рекурсивно создавать привязки, как я делал в приведенном выше коде.быть убранным.Вы можете исправить это, сохранив карту того, что было определено, а что нет.
  2. Я понятия не имею, как это будет происходить в многопоточной среде.Вам придется оценить это самостоятельно.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...