在defrecord构造函数中未强制执行类型提示

Abh*_*kar 9 clojure type-hinting

我使用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"}
Run Code Online (Sandbox Code Playgroud)

显示的构造函数签名与提供的类型提示不匹配(它们Object用于StringInteger),我能够构造具有错误字段类型的对象.

我的代码有问题还是Clojure中的错误?

我在Clojure 1.2.0-beta1上.

Ale*_*art 9

类型提示用于避免反射; 它们(当前)不用于静态类型函数或构造函数args(例外是原语,因为它们不能包含在其中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传递给函数的运行时对象的确切含义(并且该过程在传递时失败)一个整数).


Mic*_*zyk 6

据我所知,键入提示deftypedefprotocol字段目前仅在涉及基元类型时强制执行:

(deftype Foo [^int x])

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

;; ... and likewise with defprotocol
Run Code Online (Sandbox Code Playgroud)

我对这是一个公认的问题有一个非常模糊的回忆,虽然我不确定该计划是记录这种行为还是强制执行非原始提示......我会试着找出答案.