Правильно работать с разрешением символов в макросах - PullRequest
5 голосов
/ 02 августа 2011

Скажем, я хочу создать макрос Clojure, который выполняет следующие действия:

If x is a list calling the function "bar"
  return :foobar
else
  return x as a string

Однако bar не определено; скорее он используется только внутри макроса, например:

(foo (bar))
:foobar

(foo 1)
"1"

Можно сделать что-то вроде этого:

(defmacro foo [x] 
  (if (and (coll? x) (= (first x) 'bar)) 
      :foobar 
      (str x)))

Это прекрасно работает для случая (bar), а также для литералов. Однако символы не работают должным образом, давая имя символа вместо его соответствующего значения:

user=> (def y 2)
#'user/y
user=> (foo y)
"y"

Можно вызвать функцию eval на x перед передачей ее на str, но это вызывает проблемы при использовании функции в let:

user=> (let [a 3 b (foo a)] b)
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:89)

Предположительно, проблема связана с разрешением символов, поэтому, возможно, мы попытаемся что-то решить с помощью синтаксической кавычки:

(defmacro foo [x] 
  `(if (and (coll? '~x) (= (first '~x) '~'bar)) 
    :foobar 
    (str ~x)))

Теперь проблема в (foo (bar)), поскольку это расширяет предложение else до (clojure.core/str (bar)), которое выдает исключение, так как bar не определено. Затем я попытался сделать некоторые махинации с eval:

(defmacro foo [x] 
  `(if (and (coll? '~x) (= (first '~x) '~'bar)) 
    :foobar 
    (eval '(str ~x))))

Но это не работает с let привязками снова:

user=> (let [a 1 b (foo a)] b)
java.lang.Exception: Unable to resolve symbol: a in this context (NO_SOURCE_FILE:153)

Так что я действительно в растерянности. Кажется, что решение одной проблемы ломает другую. Есть ли лучший, более простой способ сделать этот макрос таким, чтобы он работал в следующих случаях:

  • В let привязок
  • С (bar)
  • С символами

P.S. Если кому-то интересно , почему Я хочу это сделать, я работаю над DSL для службы YQL Yahoo и хочу иметь возможность делать что-то вроде (select (table :t) ...), но мне нужно передавать в символах, а также литералах.

1 Ответ

4 голосов
/ 02 августа 2011

Я считаю, что это должно работать.

(defmacro foo [x]
  (if (and (coll? x) (= (first x) 'bar))
    :foobar
    `(str ~x)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...