在clojure中,我想在函数内创建一个记录.
我试过了:
(defn foo []
(defrecord MyRecord [a b])
(let [b (MyRecord. "1" "2")]))
Run Code Online (Sandbox Code Playgroud)
但它会导致异常:
java.lang.IllegalArgumentException: Unable to resolve classname: MyRecord
Run Code Online (Sandbox Code Playgroud)
任何的想法?
Mic*_*zyk 11
你应该只defrecord在顶级使用.1
因此,如果您确实需要自定义记录类型,则应在foo(在代码中某些位置foo定义之前处理)之外定义它.
否则,您可以使用常规地图.特别是,如果foo要创建多个"类型"的实体(在概念层面),尝试为每个创建一个记录类型(Java类)可能没有意义; 自然的解决方案是使用持有:type密钥的地图来表示所代表的实体的种类.
问题中的代码无法编译,因为Clojure的编译器会解析new在编译时作为表单的第一个参数提到的类名.(在宏扩展过程中(MyRecord. "1" "2")扩展为(new MyRecord "1" "2").)这里名称MyRecord尚未解析为适当的类,因为后者尚未定义(它将defrecord在foo首次调用之后由表单创建).
为了解决这个问题,你可以做一些可怕的事情
(defn foo []
(eval '(defrecord MyRecord [x y]))
(let [b (clojure.lang.Reflector/invokeConstructor
;; assuming MyRecord will get created in the user ns:
(Class/forName "user.MyRecord")
(into-array Object ["1" "2"]))]
b))
Run Code Online (Sandbox Code Playgroud)
这导致小猫finalize通过反射调用其方法,导致可怕的死亡.
作为最后的评论,上面这个可怕的版本会起作用,但它也会在每次调用时以相同的名称创建一个新的记录类型.这会导致奇怪的事情发生.尝试以下示例来获得风味:
(defprotocol PFoo (-foo [this]))
(defrecord Foo [x]
PFoo
(-foo [this] :foo))
(def f1 (Foo. 1))
(defrecord Foo [x])
(extend-protocol PFoo
Foo
(-foo [this] :bar))
(def f2 (Foo. 2))
(defrecord Foo [x])
(def f3 (Foo. 3))
(-foo f1)
; => :foo
(-foo f2)
; => :bar
(-foo f3)
; breaks
;; and of course
(identical? (class f1) (class f2))
; => false
(instance? (class f1) f2)
; => false
Run Code Online (Sandbox Code Playgroud)
此时,(Class/forName "user.Foo")(再次假设所有这些都发生在user命名空间中)返回的类f3,其中既不是f1也不f2是实例.
1宏有时可能会输出一个defrecord包含在do其中的形式; do然而,顶级s是特殊的,因为它们表现得好像它们包装的表单在顶层单独处理.
| 归档时间: |
|
| 查看次数: |
693 次 |
| 最近记录: |