2016-09-23 5 views
4

Ich habe versucht, xor Makro zu implementieren und kam mit einem Problem.Warum unterstützt Clojure nicht private Funktionen im Makro?

Ich konnte private Funktion in einem Makro nicht verwenden. Hier

ist das Beispiel:

private Funktion

Makro

(defmacro xor 
    ([] true) 
    ([x] x) 
    ([x & next] 
    `(let [first# ~x 
      second# ~(first next)] 
     (if (= (count '~next) 1) 
     (xor-result first# second#) 
     (xor (xor-result first# second#) [email protected](rest next)))))) 

Hier ist die Fehler:

CompilerException java.lang.IllegalStateException: var: #'kezban.core/xor-result is not public 

Problem löst wenn ich ^entfernen: privat Flagge.

Frage ist: Was ist der Grund für dieses Verhalten?


UPDATE: Ich private Funktion mit folgenden Ansatz verwenden kann.

private Funktion

(defn ^:private xor-result 
    [x y] 
    (if (and x y) 
    false 
    (or x y))) 

neues Makro

(defmacro xor 
    ([] true) 
    ([x] x) 
    ([x & next] 
    (let [first x 
     second `(first '([email protected])) 
     result (xor-result (eval first) (eval second))] 
    `(if (= (count '~next) 1) 
     ~result 
     (xor ~result [email protected](rest next)))))) 

Antwort

5

Wenn Sie einen Makro in ns1:

(ns ns1) 

(defn- my-fun [x] (first x)) 

(defmacro my-macro [x] (my-fun ~x)) 

Und es verwendet in einem anderen Namensraum:

(ns ns2 
    (:require [ns1 :refer [my-macro]])) 

(my-macro [1 2]) 

Der Compiler wird das Makro während der Kompilierung Phase aufrufen und es wird Code in ns2 Namensraum erzeugen und werden:

(ns ns2 
    (:require [ns1 :refer [my-macro]])) 

(ns1/my-fun [1 2]) 

und dieser Code Byte schließlich kompiliert werden Code.

Wie Sie sehen können, wird der Compiler die Verwendung einer ns1 privaten Funktion im ns2 Namensraum sehen und sich darüber beschweren.

Um Ihre Makros zu debuggen, können Sie macroexpand verwenden, um das Ergebnis der Anwendung Ihres Makros zu sehen.

Sie müssen auch daran denken, dass Ihre Makros mit Ihren Programmdaten arbeiten: Datenstrukturen, die Ihren Code darstellen (Symbole, Listen, Vektoren usw.). Zum Beispiel in der zweiten Version des Makros funktioniert es Symbole, wie sie sind, nicht Werte Laufzeit an sich gebunden:

(macroexpand '(xor true false)) 
;; => (if (clojure.core/= (clojure.core/count (quote (false))) 1) true (boot.user/xor true)) 

(macroexpand '(xor (zero? 1) (zero? 0))) 
;; => (if (clojure.core/= (clojure.core/count (quote ((zero? 0)))) 1) false (boot.user/xor false)) 

Wie Sie Ihre xor-result Funktion sehen nicht die tatsächlichen Laufzeitwerte aufgerufen werden, sondern mit die Daten, die Ihren Code darstellen. xor-result wird in Ihrem Makro direkt während der Kompilierzeit aufgerufen. In der ersten Version Ihres Makros wird es innerhalb des vom Makro erzeugten Codes verwendet und nicht während der Kompilierung aufgerufen.

+0

[ns1: beziehen [xor]] in diesem Codebeispiel. Worauf bezieht sich xor? –

+0

Ahh, Tippfehler, behoben. –

+0

Ich habe einen neuen Ansatz hinzugefügt, der private Funktionen verwendet. Kannst du bitte dieses Verhalten erklären? –