2010-07-25 3 views
7

Я пытаюсь использовать clojure в компиляторе и поэтому вам нужно параметризовать вызовы на deftype; однако у меня возникают трудности с переносом типов подсказок. Рассмотрим следующий код:Использование Clojure deftype в качестве параметризованной функции

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

И следующую сессию из РЕПЛ:

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

Обратите внимание, что созданный класс для непрямого типа потерял^Int намекает, что прямой тип имеет. Как мне получить эти подсказки?

ответ

7

Вам нужно изменить indirect-helper читать

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

Причина заключается в том, что ^int разбирает, как ^ с последующим int; ^, в Clojure 1.2, вводит метаданные читателя (в 1.1 вы использовали бы #^, который все еще работает, но устарел в 1.2). Таким образом ^int x в direct получает читать в как clojure.lang.Symbol, имя которого "x" и чьи метаданные карта {:tag int}int здесь является само по себе является символом). (Последний компонент символа - его пространство имен - в этом случае nil.)

В версии indirect-helper из текста вопроса ^int получает прикрепленный к (symbol "x") - список, содержащий символ symbol и строку "x" (что означает, в частности, что (list ^int (symbol "x")) оценивает список из 1 элемента). Этот «тип подсказки» теряется после оценки (symbol "x"). Чтобы исправить ситуацию, необходим способ привязки метаданных к фактическому символу, генерируемому (symbol "x").

Теперь в этом случае символ генерируется во время выполнения, поэтому вы не можете использовать метаданные для чтения, чтобы привязать к нему подсказку типа. Введите with-meta, который крепит метаданные во время выполнения (и часто полезно в написании макросов по той же причине, как и здесь) и на следующий день сохраняется:

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

(Кстати, я думал deftype ожидал вектор имен полей , но, видимо, список работает также ... Вектор все еще определенно более идиоматичен.)

Смежные вопросы