2010-07-25 8 views
7

Ich versuche Clojure in einem Compiler zu verwenden und muss daher Aufrufe an deftype parametrisieren; Allerdings habe ich Schwierigkeiten, die Typhinweise durchzubringen. Betrachten Sie den folgenden Code ein:Verwendung von Clojure deftype als parametrisierte Funktion

(defn describe [x] 
    (let [fields (.getDeclaredFields x) 
     names (map #(.getName %) fields) 
     types (map #(.getType %) fields)] 
    (interleave types names))) 

(defn direct [] (deftype direct-type [^int x])) 
(defn indirect-helper [] (list ^int (symbol "x"))) 
(defn indirect [] (eval `(deftype ~(symbol "indirect-type") ~(indirect-helper)))) 

und die folgende Sitzung aus dem REPL:

Clojure 1.2.0-master-SNAPSHOT 
1:1 user=> #<Namespace dataclass> 
1:2 dataclass=> (direct) 
dataclass.direct-type 
1:3 dataclass=> (indirect) 
dataclass.indirect-type 
1:4 dataclass=> (describe direct-type) 
(int "x") 
1:5 dataclass=> (describe indirect-type) 
(java.lang.Object "x") 

Beachten Sie, dass die generierte Klasse für indirekten Typen des^int verloren hat Hinweise, dass Direkttyp hat. Wie bekomme ich diese Hinweise durch?

Antwort

7

Sie müssen indirect-helper ändern

(defn indirect-helper [] [(with-meta (symbol "x") {:tag 'int})]) 

Der Grund zu lesen ist, dass ^int als ^ von int gefolgt analysiert; ^, in Clojure 1.2, führt Lesermetadaten ein (in 1.1 würden Sie #^ verwenden, was immer noch funktioniert, aber in 1.2 veraltet ist). So ^int x in direct bekommt Lese in als clojure.lang.Symbol dessen Name "x" und deren Metadaten Karte ist {:tag int} (mit der int hier ist selbst ein Symbol). (Die letzte Komponente eines Symbol - sein Namensraum - ist nil in diesem Fall.)

In der Version von indirect-helper aus dem Fragetext ^int zu (symbol "x") angebracht wird - die Liste, die das Symbol symbol und die Zeichenfolge "x" (Das bedeutet insbesondere, dass zu einer Liste von 1 Element ausgewertet wird). Dieser "Typhinweis" ist verloren, wenn (symbol "x") ausgewertet wird. Um Dinge zu beheben, ist eine Art und Weise erforderlich, Metadaten an das tatsächliche Symbol anzuhängen, das von (symbol "x") erzeugt wird.

In diesem Fall wird das Symbol zur Laufzeit generiert. Daher können Sie die Metadaten des Readers nicht verwenden, um den Typhinweis an den Text anzuhängen. Geben Sie with-meta, die Metadaten zur Laufzeit legt (und ist in dem Schreiben von Makros aus dem gleichen Grunde häufig nützlich, da es hier) und der Tag gerettet wird:

user> (indirect) 
user.indirect-type 
user> (describe indirect-type) 
(int "x") 

(BTW, dachte ich deftype einen Vektor von Feldnamen erwartet , aber anscheinend funktioniert auch eine Liste ... Ein Vektor ist sicherlich noch mehr idiomatisch.)