【问题标题】:Access clojure type hints at runtime在运行时访问 clojure 类型提示
【发布时间】:2012-07-04 20:53:44
【问题描述】:

假设我定义了一个包含一些类型提示的记录,如下所示:

(defrecord person [name sex ^Integer age city])

有什么方法可以在运行时使用person 类或person 的实例来确定指定了哪些类型提示?目的是改变使用的gui组件,取决于字段的类型(注意字段的值可能是nil,所以我不能使用值的类型来确定字段的类型)。

我尝试了一些显而易见的事情,但没有成功:

; no metadata on the class, an instance, or the keys or vals of an instance
=> (meta person)
nil
=> (meta (person. "Geoff" "male" 30 "Moon base"))
nil
=> (map meta (keys (person. "Geoff" "male" 30 "Moon base")))
(nil nil nil nil)
=> (map meta (vals (person. "Geoff" "male" 30 "Moon base")))
(nil nil nil nil)
; the field is of type Object
=> (filter (fn [x] (= "age" (.getName x))) (.getFields person))
(#<Field public final java.lang.Object matt.clarity.scratch.person.age>)
; no metadata on the fields of the class
=> (map meta (.getFields person))
(nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)

【问题讨论】:

    标签: types clojure


    【解决方案1】:

    我认为你应该从不同的角度来处理这个问题:

    • 创建一个定义字段和数据类型的映射
    • 使用此映射生成适当的 defrecord 和任何查看器/属性编辑器

    地图可能类似于:

      {:name java.lang.String
       :sex  java.lang.String
       :age  java.lang.Integer}
    

    然后,此地图将用作驱动系统其余部分的元数据.....

    【讨论】:

    • 认为我可能不得不做这样的事情。正如@Ankur 所建议的那样,无论如何这可能是一种更好的方法。我仍然有点惊讶在运行时无法访问类型提示。
    • @OpenSause,请注意元数据应用于 Var 而不是它指向的对象 .. 因此要获取 Var 的元数据,我们需要使用 (meta #'myvar)
    【解决方案2】:

    似乎在记录字段上应用类型提示对反射没有任何影响,因此它们被忽略了。

    user=> (set! *warn-on-reflection* true)
    user=> (defrecord user [^String name])     
    user=> (.indexOf "z" (:name (user. "Superman")))
    Reflection warning, NO_SOURCE_PATH:16 - call to indexOf can't be resolved.
    -1
    

    另外,我不建议对与您的业务逻辑相关的内容使用类型提示,我更愿意使用带有数据的自己的标签来描述要在 UI 控件中显示的数据类型。可以定义一个协议,该协议具有获取特定类型字段类型的方法,然后在定义记录时在您的记录上实现该协议。

    类似:

    user=> (defprotocol DescribeData (getFieldTypes [this]))
    user=> (defrecord User [name age] DescribeData (getFieldTypes [_] {:name String :age Integer}))
    user=> (def u (User. "Superman" 1000))
    user=> (getFieldTypes u)
    {:name java.lang.String, :age java.lang.Integer}
    

    这样您可以将任何对象传递给 UI 层,并且 UI 层将与任何实现该协议的对象一起使用。

    【讨论】:

      【解决方案3】:

      没有简单的方法可以在运行时访问类型提示。您必须将 defrecord 包装在您自己的宏中,该宏可以访问这样的提示:

      (->> (-> '(defrecord Foo [^Integer a b c]) 
               macroexpand-1
               (nth 2)
               (nth 3))
           (drop-last 2)
           (map #(vec [% (-> % meta :tag)]))) ; => ([a Integer] [b nil] [c nil])
      

      但是,我宁愿建议使用 @mikera 的答案并构建一个宏,该宏发出一个 defrecord 和一个包含字段->类型映射的 defvar。

      【讨论】:

        猜你喜欢
        • 2011-10-01
        • 2017-07-27
        • 2011-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-14
        • 2011-03-02
        • 1970-01-01
        相关资源
        最近更新 更多