Ну, это как бы возможно:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(throw (RuntimeException. "no such local")))))
... и теперь мы можем делать странные вещи вроде этого:
user> (defn example [s]
(letfn [(foo [x] {:foo x})
(bar [x] {:bar x})]
((voodoo s) :quux)))
#'user/example
user> (example "foo")
{:foo :quux}
user> (example "bar")
{:bar :quux}
user> (example "quux")
; Evaluation aborted.
user> *e
#<RuntimeException java.lang.RuntimeException: no such local>
То, что "Evaluation aborted" означает, что исключение былоБрошенный.
Вы также можете заменить throw
ветвь if
в voodoo
на (resolve (symbol ~s))
для переноса на глобальные переменные, если не найдено локальное:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(resolve (symbol ~s)))))
... и теперь это работает с определением example
, как указано выше (хотя учтите, что если вы экспериментируете с REPL, вам потребуется перекомпилировать example
после переопределения voodoo
):
user> (defn quux [x] {:quux x})
#'user/quux
user> (example "quux")
{:quux :quux}
Так вот, это злоупотребление возможностями Clojure, без которых было бы неплохо попытаться обойтись.Если вы не можете, то, вероятно, следует обратиться к evalive от Michael Fogus;это библиотека, которая предоставляет средство eval-with-localals в виде функции evil
и нескольких утилит.Кажется, что функциональность тоже хорошо продумана, например, что-то вроде ~(zipmap ...)
выше инкапсулировано как макрос, и evil
кажется почти заменой eval
(добавьте параметр env, и выхорошо пойти).Я не прочитал источник должным образом, но я, вероятно, сейчас, похоже, весело.: -)