gpt4 book ai didi

macros - 使用 clojure 宏在 reify 调用中自动创建 getter 和 setter

转载 作者:行者123 更新时间:2023-12-04 13:35:05 24 4
gpt4 key购买 nike

我正在尝试使用许多(约 50 个)getter 和 setter 方法(有些名称不规则)来实现一个巨大的 Java 接口(interface)。我认为使用宏来减少代码量会很好。所以而不是

(def data (atom {:x nil}))
(reify HugeInterface
(getX [this] (:x @data))
(setX [this v] (swap! data assoc :x v)))

我希望能够写作
(def data (atom {:x nil}))
(reify HugeInterface
(set-and-get getX setX :x))

这个设置和获取宏(或类似的东西)可能吗?我无法让它工作。

最佳答案

(更新了第二种方法——见下面的第二条水平规则——以及一些解释性评论:第一种。)

我想知道这是否可能是朝着正确方向迈出的一步:

(defmacro reify-from-maps [iface implicits-map emit-map & ms]
`(reify ~iface
~@(apply concat
(for [[mname & args :as m] ms]
(if-let [emit ((keyword mname) emit-map)]
(apply emit implicits-map args)
[m])))))

(def emit-atom-g&ss
{:set-and-get (fn [implicits-map gname sname k]
[`(~gname [~'this] (~k @~(:atom-name implicits-map)))
`(~sname [~'this ~'v]
(swap! ~(:atom-name implicits-map) assoc ~k ~'v))])})

(defmacro atom-bean [iface a & ms]
`(reify-from-maps ~iface {:atom-name ~a} ~emit-atom-g&ss ~@ms))

注意。该 atom-bean宏传递 emit-atom-g&ss 的实际编译时值转至 reify-from-maps .一次特定的 atom-bean表格已编译,对 emit-atom-g&ss 的任何后续更改对创建对象的行为没有影响。

来自 REPL 的示例宏扩展(为清楚起见添加了一些换行符和缩进):
user> (-> '(atom-bean HugeInterface data
(set-and-get setX getX :x))
macroexpand-1
macroexpand-1)
(clojure.core/reify HugeInterface
(setX [this] (:x (clojure.core/deref data)))
(getX [this v] (clojure.core/swap! data clojure.core/assoc :x v)))

两个 macroexpand-1 s 是必要的,因为 atom-bean是一个扩展为进一步宏调用的宏。 macroexpand不会特别有用,因为它将一直扩展为对 reify* 的调用。 ,后面的实现细节 reify .

这里的想法是您可以提供 emit-map喜欢 emit-atom-g&ss上面,由名称(以符号形式)将触发 reify-from-maps 中的魔术方法生成的关键字作为键来电。魔术是由存储为给定 emit-map 中的函数的函数执行的。 ;函数的参数是“隐式”的映射(基本上是任何和所有信息,所有方法定义都应该以 reify-from-maps 形式访问,例如在这种特殊情况下的原子名称),然后是给出的任何参数到 reify-from-maps 中的“魔术方法说明符”形式。如上所述, reify-from-maps需要查看实际的关键字 -> 函数映射,而不是其符号名称;所以,它只能用于文字映射、在其他宏中或在 eval 的帮助下使用。 .

普通方法定义仍然可以包括在内,并将被视为常规 reify 中的定义。形式,前提是与其名称匹配的键不在 emit-map 中出现.发射函数必须以 reify 所期望的格式返回方法定义的 seqables(例如向量)。 : 这样一来,一个“魔术方法说明符”返回多个方法定义的情况就比较简单了。如果 iface参数被替换为 ifaces~iface~@ifacesreify-from-maps ' body,可以指定多个接口(interface)来实现。

这是另一种方法,可能更容易推理:
(defn compile-atom-bean-converter [ifaces get-set-map]
(eval
(let [asym (gensym)]
`(fn [~asym]
(reify ~@ifaces
~@(apply concat
(for [[k [g s]] get-set-map]
[`(~g [~'this] (~k @~asym))
`(~s [~'this ~'v]
(swap! ~asym assoc ~k ~'v))])))))))

这会在运行时调用编译器,这有点昂贵,但每组要实现的接口(interface)只需要执行一次。结果是一个函数,它接受一个原子作为参数,并具体化一个围绕原子的包装器,使用 get-set-map 中指定的 getter 和 setter 实现给定接口(interface)。争论。 (这样写,比以前的方法更不灵活,但上面的大部分代码都可以在这里重用。)

这是一个示例界面和一个 getter/setter 映射:
(definterface IFunky
(getFoo [])
(^void setFoo [v])
(getFunkyBar [])
(^void setWeirdBar [v]))

(def gsm
'{:foo [getFoo setFoo]
:bar [getFunkyBar setWeirdBar]})

还有一些 REPL 交互:
user> (def data {:foo 1 :bar 2})
#'user/data
user> (def atom-bean-converter (compile-atom-bean-converter '[IFunky] gsm))
#'user/atom-bean-converter
user> (def atom-bean (atom-bean-converter data))
#'user/atom-bean
user> (.setFoo data-bean 3)
nil
user> (.getFoo atom-bean)
3
user> (.getFunkyBar data-bean)
2
user> (.setWeirdBar data-bean 5)
nil
user> (.getFunkyBar data-bean)
5

关于macros - 使用 clojure 宏在 reify 调用中自动创建 getter 和 setter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4434902/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com