Próbuję napisać makro, które mogą być wykorzystywane zarówno w globalnym i zagnieżdżonego sposób, jak w przykładzie:Podania stan kompilacji czas pomiędzy zagnieżdżonych makr w Clojure
;;; global:
(do-stuff 1)
;;; nested, within a "with-context" block:
(with-context {:foo :bar}
(do-stuff 2)
(do-stuff 3))
Przy stosowaniu w zagnieżdżonej sposób, do-stuff
powinien mieć dostęp do {:foo :bar}
zestawu przez with-context
.
byłem w stanie realizować to tak:
(def ^:dynamic *ctx* nil)
(defmacro with-context [ctx & body]
`(binding [*ctx* ~ctx]
(do [email protected])))
(defmacro do-stuff [v]
`(if *ctx*
(println "within context" *ctx* ":" ~v)
(println "no context:" ~v)))
Jednak ja już próbuje przesunąć if
ciągu do-stuff
od wykonywania na czasie kompilacji, bo czy do-stuff
jest nazywany od wewnątrz treść with-context
lub globalnie jest informacją, która jest już dostępna podczas kompilacji.
Niestety, nie udało się znaleźć rozwiązanie, gdyż zagnieżdżone makra wydają się rozszerzył w wielu seriach „makr”, więc wiązanie *ctx*
(jak określono w ciągu with-context
) dynamiczny nie jest dostępny już przy do-stuff
zostaje rozszerzony. Więc to nie działa:
(def ^:dynamic *ctx* nil)
(defmacro with-context [ctx & body]
(binding [*ctx* ctx]
`(do [email protected])))
(defmacro do-stuff [v]
(if *ctx*
`(println "within context" ~*ctx* ":" ~v)
`(println "no context:" ~v)))
Jakieś pomysły, jak to osiągnąć?
Czy moje podejście jest całkowicie szalone i istnieje wzorzec, w jaki sposób przekazywać stan w taki sposób, od jednego makra do zagnieżdżonego?
EDIT:
Ciało with-context
powinien być w stanie pracować z dowolnych wyrażeń, nie tylko z do-stuff
(lub innym kontekście świadomych funkcji/makr). Więc coś jak to powinno być również możliwe:
(with-context {:foo :bar}
(do-stuff 2)
(some-arbitrary-function)
(do-stuff 3))
(Jestem świadomy, że some-arbitrary-function
chodzi o skutki uboczne, to może napisać coś do bazy danych, na przykład).
Fantastyczny, właśnie tego szukałem, dziękuję! Ciekawostka: rzeczywiście próbowałem tego z 'macroexpand' i' macroexpand-1', ale to nie działało. Po prostu nie wiedziałem o 'macroexpand-all'. Wielkie dzięki. Zastanawiam się jednak, czy mój pomysł przekazania takiego stanu między makrami jest koncepcyjnie właściwy. Wywołanie czegoś takiego jak 'macroexpand' z kodu wewnątrz zawsze wydaje się niezręczne. Czy brakuje mi tu idiomu, który rozwiązałby mój problem w lepszy (bardziej idiomatyczny, czystszy) sposób? – Oliver
Nie mam nic przeciwko temu: w końcu funkcja jest dostępna, dzięki czemu można z niej korzystać. Jest to trochę niezwykłe, więc musisz dostarczyć dobrą dokumentację 'with-context',' * ctx * ', a także dowolne makro, które od tego zależy. Jest to forma sprzężenia, ale jeśli jej potrzebujesz, działa. Nie znam niczego "czystszego" niż to. – coredump