动态方法调用Clojure宏?

npa*_*pad 14 clojure

我正在尝试编写一个宏,它将根据给定的参数调用java setter方法.

所以,例如:

(my-macro login-as-fred {"Username" "fred" "Password" "wilma"})
Run Code Online (Sandbox Code Playgroud)

可能会扩展到类似以下内容:

(doto (new MyClass)
  (.setUsername "fred")
  (.setPassword "wilma"))
Run Code Online (Sandbox Code Playgroud)

你会怎么建议解决这个问题?

具体来说,我无法找到构造setter方法名称的最佳方法,并让它将其解释为宏的符号.

Art*_*ldt 19

关于宏的好处是你实际上不必深入研究类或类似的东西.您只需要编写生成正确的s表达式的代码.

首先是一个生成s表达式的函数 (.setName 42)

(defn make-call [name val]
  (list (symbol (str ".set" name) val)))
Run Code Online (Sandbox Code Playgroud)

然后是一个宏来生成表达式并将它们插入(〜@)doto表达式中.

(defmacro map-set [class things]
  `(doto ~class ~@(map make-call things))
Run Code Online (Sandbox Code Playgroud)

因为它是一个宏,它永远不必知道它所调用的东西是什么类,甚至是它所使用的类存在.


Bri*_*per 5

有人(我相信 Arthur Ulfeldt)发布了一个几乎正确的答案,但现在已被删除。如果他再次发帖,请接受它而不是我的。(或接受 pmf。)这是一个工作版本:

(defmacro set-all [obj m]
  `(doto ~obj ~@(map (fn [[k v]]
                       (list (symbol (str ".set" k)) v))
                     m)))

user> (macroexpand-1 '(set-all (java.util.Date.) {"Month" 0 "Date" 1 "Year" 2009}))
(clojure.core/doto (java.util.Date.) (.setMonth 0) (.setDate 1) (.setYear 2009))

user> (set-all (java.util.Date.) {"Month" 0 "Date" 1 "Year" 2009})
#<Date Fri Jan 01 14:15:51 PST 3909>
Run Code Online (Sandbox Code Playgroud)


kot*_*rak 5

请不要list为宏构造s表达式.这将严重损害宏观的卫生.犯错很容易,很难追查.请始终使用语法报价!虽然,在这种情况下这不是问题,但养成只使用语法引用的习惯是好的!

根据地图的来源,您可能还会考虑使用关键字作为键,使其看起来更像clojure.这是我的看法:

(defmacro configure
  [object options]
  `(doto ~object
     ~@(map (fn [[property value]]
              (let [property (name property)
                    setter   (str ".set"
                                  (.toUpperCase (subs property 0 1))
                                  (subs property 1))]
                `(~(symbol setter) ~value)))
            options)))
Run Code Online (Sandbox Code Playgroud)

这可以用作:

user=> (macroexpand-1 '(configure (MyClass.) {:username "fred" :password "wilma"}))
(clojure.core/doto (MyClass.) (.setUsername "fred") (.setPassword "wilma"))
Run Code Online (Sandbox Code Playgroud)