Using Clojure deftype as a parameterized function
I am trying to use clojure in a compiler and thus need to parameterize calls to deftype
; however, I am having difficulty making the type hints carry through. Consider the following code:
(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 "indirec开发者_Python百科t-type") ~(indirect-helper))))
And the following session from the 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")
Notice that the generated class for indirect-type has lost the ^int hints that direct-type has. How do I get those hints to carry through?
You'll need to change indirect-helper
to read
(defn indirect-helper [] [(with-meta (symbol "x") {:tag 'int})])
The reason is that ^int
parses as ^
followed by int
; ^
, in Clojure 1.2, introduces reader metadata (in 1.1 you'd use #^
, which still works, but is deprecated in 1.2). Thus ^int x
in direct
gets read in as a clojure.lang.Symbol
whose name is "x"
and whose metadata map is {:tag int}
(with the int
here being itself a symbol). (The final component of a symbol -- its namespace -- is nil
in this case.)
In the version of indirect-helper
from the question text ^int
gets attached to (symbol "x")
-- the list comprising the symbol symbol
and the string "x"
(meaning in particular that (list ^int (symbol "x"))
evaluates to a list of 1 element). This "type hint" is lost once (symbol "x")
is evaluated. To fix things, some way to attach metadata to the actual symbol generated by (symbol "x")
is needed.
Now in this case, the symbol is generated at runtime, so you can't use reader metadata to attach the type hint to it. Enter with-meta
, which attaches metadata at runtime (and is frequently useful in writing macros for the same reason as it is here) and the day is saved:
user> (indirect)
user.indirect-type
user> (describe indirect-type)
(int "x")
(BTW, I thought deftype
expected a vector of field names, but apparently a list works as well... A vector is still certainly more idiomatic.)
精彩评论