Clojure:如何在函数内创建记录? [英] Clojure: how to create a record inside a function?

查看:114
本文介绍了Clojure:如何在函数内创建记录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在clojure中,我想在函数中创建一个记录。

In clojure, I would like to create a record inside a function.

我试过:

(defn foo []
  (defrecord MyRecord [a b])
  (let [b (MyRecord. "1" "2")]))

但它会导致异常:

java.lang.IllegalArgumentException: Unable to resolve classname: MyRecord

有什么想法吗?

推荐答案

要点



您应该只在顶层使用 defrecord 1

因此,如果您需要自定义记录类型,则应在 foo

So, if you do need a custom record type, you should define it outside of foo (at some point in your code which gets processed before foo's definition).

否则,你可以在你的代码中的某一点获取 foo 只需使用常规地图。特别是,如果 foo 将创建多个类型的实体(在概念级别),尝试创建记录类型(Java类);自然的解决方案是使用包含:type 键的映射来表示所表示的实体的类型。

Otherwise, you could just use a regular map. In particular, if foo will be creating entities of multiple "types" (at the conceptual level), it probably makes no sense to try to create a record type (a Java class) for each; the natural solution would be to use maps holding a :type key to indicate the sort of entity represented.

问题的代码不能编译,因为Clojure的编译器解析了作为第一个参数 new 表单在编译时。 ((MyRecord。12)扩展为(new MyRecord12) )在这里,名称 MyRecord 还不能解析到合适的类,因为后者还没有被定义(它将由 defrecord 后第一次调用时)。

The code from the question doesn't compile, because Clojure's compiler resolves class names mentioned as first arguments to new forms at compile time. ((MyRecord. "1" "2") is expanded to (new MyRecord "1" "2") during the macro expansion process.) Here the name MyRecord cannot yet be resolved to the appropriate class, because the latter has not yet been defined (it would be created by the defrecord form after foo was first called).

在这个时候,你可以做一些很可怕的事情。

To get around this, you could do something horrible like

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


$ b b

这导致小猫通过反射调用 finalize 方法,导致可怕的死亡。

which causes a kitten to have its finalize method invoked through reflection, resulting in a gruesome death.

作为最后一句话,上述可怕的版本将排序的工作,但它也将创建一个新的记录类型在同一名称在每次调用。这导致奇怪的结果。尝试以下风格的示例:

As a final remark, the above horrible version would sort of work, but it would also create a new record type under the same name at each invocation. This causes weirdness to ensue. Try the following examples for a flavour:

(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

此时,(Class / forNameuser.Foo) (再次假设所有这些发生在 user 命名空间中)返回 f3 ,其中 f1 f2 都是一个实例。

At this point, (Class/forName "user.Foo") (assuming again that all this happens in the user namespace) returns the class of f3, of which neither f1 nor f2 is an instance.

1 宏有时可能会输出一个 defrecord do 以及其他一些表单;顶层 do 是特殊的,因为它们的行为好像它们包装的表单在顶层单独处理。

1 Macros occasionally might output a defrecord form wrapped in a do along with some other forms; top-level dos are special, though, in that they act as if the forms they wrap were individually processed at top level.

这篇关于Clojure:如何在函数内创建记录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆