2010-07-27 8 views
7

Creé un tipo usando defrecord con sugerencias de tipo para los campos. Sin embargo, descubrí que estos consejos tipo no se aplican en los constructores y puedo hacer algunas cosas extrañas con ellos. Mira el siguiente fragmento de código, por ejemplo:La sugerencia de tipo no se aplica en los constructores de 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"} 

Las firmas constructoras mostrados no coinciden con los consejos proporcionados tipo (utilizan tanto para ObjectString y Integer) y yo soy capaz de construir objetos con los tipos de campo equivocadas.

¿Hay algún problema con mi código o es un error en Clojure?

Estoy en Clojure 1.2.0-beta1.

+3

Como un lado, si su código depende de las características 1.2 de todos modos, debe preferir '^' a '# ^' para introducir metadatos del lector; el antiguo significado de '# ^' está en desuso en 1.2. –

Respuesta

8

Las sugerencias de tipo se utilizan para evitar la reflexión; no se utilizan (actualmente) para escribir de forma estática los argumentos de la función o del constructor (la excepción son primitivos ya que no se pueden incluir en Object). Como tales, no hacen mucho para un registro simple, pero sí importa cuando se trata de añadir implementación del protocolo, por ejemplo:

 
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) 

La diferencia entre las dos versiones es que este último emite el código de bytes llamando String.getBytecode() (por lo tanto, la ClassCastException cuando pasa un entero), mientras que la primera necesita buscar qué significa exactamente .getBytes con respecto al objeto de tiempo de ejecución pasado a la función (y ese proceso falla al pasar un entero).

5

Por lo que yo puedo decir, consejos de tipos sobre deftype y defprotocol campos están actualmente sólo aplican cuando un tipo primitivo está involucrada:

(deftype Foo [^int x]) 

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

;; ... and likewise with defprotocol 

Tengo un recuerdo muy vago de que esto sea un problema reconocido, aunque No estoy seguro si el plan es documentar este comportamiento o forzar sugerencias no primitivas ... Trataré de averiguarlo.

Cuestiones relacionadas