2017-07-04 2 views
2

Ich habe ein Problem mit einem Paar Clojure-Makros, wenn ein Standardargument definiert ist.Clojure-Makros und Standardargumente, wenn ein Makro einen anderen aufruft

In der folgenden Situation mit zwei Makros, wo mm02 ruft mm01:

(defmacro mm01 
    [ & [ { :keys [ f1 ] :or { f1 long } :as opts } ]] 
    `(let [] 
    (println "(2) ~f1" ~f1))) 

(defmacro mm02 
    [ & [ { :keys [ f1 ] :as opts } ]] 
    `(let [] 
    (println "(1) ~f1" ~f1) 
    (mm01 [email protected]))) 

Die Bewertung von:

(mm02 { :f1 byte }) 

druckt:

(1) ~f1 #function[clojure.core/byte] 
(2) ~f1 #function[clojure.core/long] 

Allerdings hätte ich erwartet:

(1) ~f1 #function[clojure.core/byte] 
(2) ~f1 #function[clojure.core/byte] 

Mache ich etwas falsch oder verpasse ich etwas?

By the way, die Auswertung von:

(mm01 { :f1 byte }) 

druckt:

(2) ~f1 #function[clojure.core/byte] 

Ihnen sehr danken.

Antwort

5

[email protected] erweitert eine Reihe von Dingen in mehrere einzelne Dinge innerhalb eines Syntax-Zitat-Kontextes. Ihre opts Bindung ist eine Karte, die konzeptionell eine Folge von Karteneinträgen darstellt. Sie können dies in Aktion sehen, indem Sie im repl mit den Ausdrücken experimentieren, die Ihre Makros generieren: Dies ist oft eine nützliche Methode, um die Zwischenschritte eines Makros zu betrachten, im Vergleich zum Debuggen durch Trial-and-Error für das Makro als ganze. Siehe

user=> (let [opts {:f1 'long}] 
    #_=> `(foo [email protected])) 
(user/foo [:f1 long]) 

die eckigen Klammern um :f1 long? Das ist das Problem: Ihr anderes Makro erwartet, dass es mit einer Karte und nicht mit einem Vektor aufgerufen wird. Infolgedessen findet die Destrukturierung nicht den Schlüssel, nach dem Sie gesucht haben. Um dies zu beheben, entfernen Sie einfach die @ und verwenden Sie eine gewöhnliche unquote, nicht eine Splicing unquote.

user=> (let [opts {:f1 'long}] 
    #_=> `(foo ~opts)) 
(user/foo {:f1 long}) 

Als zusätzliche Verbesserung, sollten Sie die ablenkende [& [{...}]] Argument Strophe ersetzen mit nur [{...}]. Sie verhalten sich gleich, außer dass der erstere dem Aufrufer erlaubt, Null-Argumente (das Einfüllen mit Nils) oder eine beliebige Anzahl von zusätzlichen Argumenten zu übergeben, die alle ignoriert werden. Ihre Version ist für den Anrufer etwas bequemer, wenn sie gemeint haben, um ein Argument wegzulassen und Standardwerte zu erhalten, aber unvermeidlich werden sie zu einem großen Aufwand des Debuggens führen, wenn sie ein Argument auslassen oder zu viele versehentlich bereitstellen.

+0

Große und klare Erklärung und nützliche Kommentare, danke! Ich sehe, dass optionale Argumente zu einigen obskuren Fehlern führen können. Ich denke, dass Stuart Sierra einige Aspekte dazu in [https://stuartsierra.com/2015/06/01/clojure-donts-optional-arguments-with-varargs] kommentiert. –

Verwandte Themen