Подсказването на типа не се прилага в конструкторите на defrecord

Създадох тип с помощта на defrecord със съвети за тип за полетата. Открих обаче, че тези съвети за типове не се прилагат в конструкторите и мога да правя някои странни неща с тях. Вижте например фрагмента по-долу:

user=> (defrecord Person [#^String name #^Integer age])
user.Person
user=> (seq (.getConstructors Person))
(#<Constructor public user.Person(java.lang.Object,java.lang.Object,
java.lang.Object,java.lang.Object)>
#<Constructor public user.Person(java.lang.Object,java.lang.Object)>)
user=> (Person. (Integer. 123) "abhinav")
#:user.Person{:name 123, :age "abhinav"}

Показаните подписи на конструктора не съвпадат с предоставените подсказки за тип (те използват Object както за String, така и за Integer) и мога да конструирам обекти с грешни типове полета.

Има ли нещо нередно с моя код или е грешка в Clojure?

Аз съм на Clojure 1.2.0-beta1.


person Abhinav Sarkar    schedule 27.07.2010    source източник
comment
Като настрана, ако кодът ви така или иначе зависи от характеристиките на 1.2, трябва да предпочетете ^ пред #^ за въвеждане на метаданни за четец; старото значение на #^ е отхвърлено в 1.2.   -  person Michał Marczyk    schedule 27.07.2010


Отговори (2)


Използват се съвети за тип, за да се избегне отражението; те не се използват (в момента) за статично въвеждане на аргументи на функция или конструктор (изключение са примитивите, тъй като те не могат да бъдат включени под Object). Като такива, те не правят много за прост запис, но имат значение, когато става въпрос за добавяне на внедряване на протокол, например:

user=> (set! *warn-on-reflection* true)
true
user=> (defprotocol P (foo [p]))
P
user=> (defrecord R [s] P (foo [_] (.getBytes s)))  ; getBytes is a method on String
Reflection warning, NO_SOURCE_PATH:6 - reference to field getBytes can't be resolved.
user.R
user=> (foo (R. 5))
java.lang.IllegalArgumentException: No matching field found: getBytes for class java.lang.Integer (NO_SOURCE_FILE:0)
user=> (defrecord R [^String s] P (foo [_] (.getBytes s)))
user.R
user=> (foo (R. 5))
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String (NO_SOURCE_FILE:0)

Разликата между двете версии е, че последната излъчва байткод, извикващ String.getBytecode() (следователно ClassCastException, когато се предава цяло число), докато първата трябва да търси какво точно означава .getBytes по отношение на обекта за изпълнение, предаван на функцията (и този процес се проваля при предаване на цяло число).

person Alex Taggart    schedule 27.07.2010

Доколкото мога да кажа, подсказките за тип в полетата deftype и defprotocol в момента се прилагат само когато е включен примитивен тип:

(deftype Foo [^int x])

(Foo. 5)    ; => OK
(Foo. :foo) ; => no go

;; ... and likewise with defprotocol

Имам много смътни спомени, че това е признат проблем, въпреки че не съм сигурен дали планът е да документирам това поведение или да наложа не-примитивни намеци... Ще се опитам да разбера.

person Michał Marczyk    schedule 27.07.2010