2009-06-12 11 views
19

Zdaję sobie sprawę, że pierwszą zasadą Macro Club jest Nie używaj makr, więc poniższe pytanie jest przeznaczone raczej do nauki Clojure niż cokolwiek innego (Rozumiem, że niekoniecznie najlepsze jest użycie makr).Pomóż mi napisać makro Clojure, które automatycznie dodaje metadane do definicji funkcji

Chcę napisać proste makro, które działa jak opakowanie wokół zwykłego makro (defn) i kończy dodawanie niektórych metadanych do zdefiniowanej funkcji. Więc chciałbym mieć coś takiego:

(defn-plus f [x] (inc x)) 

... rozwinąć się coś takiego:

(defn #^{:special-metadata :fixed-value} f [x] (inc x)) 

W zasadzie to nie wydaje mi się, że trudno, ale jestem Mam problem ze zbieraniem szczegółów pobierania i innych formularzy w zdefiniowanej funkcji, które mają być poprawnie przeanalizowane.

Jako bonus, jeśli to możliwe, chciałbym, aby makro mogło obsłużyć wszystkie rozbieżne formy defn (tj. Z lub bez docstrukcji, wielu definicji arii, itp.). Widziałem pewne rzeczy w pakiecie clojure-contrib/def, które wyglądały na pomocne, ale trudno było znaleźć przykładowy kod, który z nich korzystał.

+0

Dobre intro ... główne rekwizyty do tego. – Kekoa

+0

Dlaczego nie używać makr? Czy myślisz o preprocesorze C? – Svante

Odpowiedz

18

Aktualizacja:

Poprzednia wersja moją odpowiedź nie była zbyt mocna. To wydaje się prostszy i bardziej właściwy sposób to robi, skradziony z clojure.contrib.def:

(defmacro defn-plus [name & syms] 
    `(defn ~(vary-meta name assoc :some-key :some-value) [email protected])) 

user> (defn-plus ^Integer f "Docstring goes here" [x] (inc x)) 
#'user/f 
user> (meta #'f) 
{:ns #<Namespace user>, :name f, :file "NO_SOURCE_PATH", :line 1, :arglists ([x]), :doc "Docstring goes here", :some-key :some-value, :tag java.lang.Integer}

#^{} i with-meta nie to samo. Aby wyjaśnić różnicę między nimi, zobacz dyskusję Richa na temat Clojure mailing list. To wszystko jest trochę mylące i pojawiło się kilka razy na liście mailingowej; patrz na przykład here.

Należy pamiętać, że def jest specjalną formą i obsługuje nieco dziwne metadane w porównaniu z niektórymi innymi częściami tego języka. Ustawia metadane interfejsu def dla metadanych symbolu, który nazywa var; to jedyny powód, dla którego powyższe działa, tak myślę. Zobacz klasę DefExpr w Compiler.java w źródle Clojure, jeśli chcesz zobaczyć odwagę tego wszystkiego.

Wreszcie, strona 216 Programming Clojure mówi:

Należy ogólnie unikać makr czytelnika w ekspansji makr, ponieważ makra czytniki są oceniane w czasie odczytu, przed rozpoczęciem ekspansji makro.

+0

Interesujące! Ale czy istnieje sposób, aby zrobić to bardziej funkcjonalnie? Zawijanie 'defn' w' do', a następnie destrukcyjne modyfikowanie jego metadanych wydaje mi się dziwne. Z drugiej strony, moje eksperymenty z użyciem składni '#^{: k: v}' z wnętrza 'defmacro' wszystkie były jak dotąd zupełnymi niepowodzeniami ... –

+0

Wykonywanie wszelkiego rodzaju" def "nie jest rzeczą bardzo funkcjonalną zrobić, ponieważ destruktywnie mutuje stan globalnej tablicy wysyłkowej. :) Ale masz rację i zaktualizowałem swoją odpowiedź. Moją poprzednią odpowiedzią było odrzucenie metadanych znacznika #^Integer, które normalnie przyjmowałaby 'def'. –

+0

Dziękuję, jest to o wiele bardziej zrozumiałe i te linki wyglądają bardzo pomocniczo, chociaż wciąż jestem trochę zdziwiony, dlaczego (macroexpand-1 '(defn-plus foo [bar] (baz)) nie wyświetla z -meta tag. –

Powiązane problemy