views:

99

answers:

1

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  "indirect-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?

+6  A: 

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.)

Michał Marczyk