2012-09-25 35 views
5

Jest to podobne do problemu omówionego w Treat Clojure macro as a function, ale podczas próby podejścia w górnej odpowiedzi dostałem błąd. Mam nadzieję, że zbyt wiele informacji na temat mojego konkretnego wniosku nie jest konieczne, ponieważ jest to dość skomplikowane, ale tutaj jest to wersja destylowana, co starałem się zrobić:Clojure macro jako funkcja/"Partial" dla makr?

(defmacro make-fn [m arg1] 
    `(fn [& args#] 
     (eval `(~'~m ~'~arg1 [email protected]#)))) 

użyłem makro w tym kontekście:

(let [columns (make-columns table-width) 
     table-name (keyword (str "table_" n))] 
    (apply (make-fn helpers/tbl table-name) columns)) 

"helpers/tbl" to makro, które oczekuje słowa kluczowego nazwa tabeli i zmiennej liczby list zawierających specyfikacje kolumn (np. [: Varchar 100] lub coś podobnego). Próbuję tworzyć losowe specyfikacje tabel bazy danych w locie, aby ułatwić niektóre testy. W każdym razie, gdy próbuje wykonać powyższy kod, pojawia się następujący błąd:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: table-name in this context, compiling:(NO_SOURCE_PATH:1) 

ja rodzaj uchwycenia problemu: makro ekspansja odbywa się w czasie kompilacji, a ja staram się zawierać wartość wykonania w ekspansja makr, stąd dziwne użycie cytowania i nieumiejętności, aby wszystko było ustawione poprawnie. Zasadniczo chcę częściowego dla makr i potrzebuję móc ponownie użyć tego mechanizmu dla różnych makr w różnych przestrzeniach nazw i mieć wszystkie zmienne rozdzielczości wyjść dobrze. Czy to możliwe?

Odpowiedz

2

Problem jest spowodowany sposobem, w jaki Clojure rozpoznaje symbole w wyrażeniu składni cudzysłowu (backtick). Aby uniknąć niezamierzonego przechwytywania zmiennych, Clojure zawsze interpretuje symbole w wyrażeniu z cytatem składni jako odnoszące się do Vars (, nie locals).

Można obejść ten problem przez "zwijanie własnego" kodu budowania formularzy, równoważnego z wygenerowanym przez cytat składniowy. To tak brzydki jak grzech, ale działa ... tylko nie mów, że nie ostrzegałem:

(defmacro make-fn [m arg1] 
    (let [g (gensym)] 
    (list 'fn ['& g] 
     (list 'eval (list 'concat (list 'list m arg1) g))))) 

Wow, to jest jak flashback do moich Common Lisp dni ...

+0

BTW , interesujący fakt o 'składni-cytacie': implementowany jest całkowicie w czytniku. Kiedy czytelnik zobaczy odsyłacz, odczytuje następującą formę, a następnie przekształca ją rekurencyjnie w kod, który używa "seq", "concat" i "list". Kompilator widzi tylko wynikowy kod 'concat' /' list'. –