我正在通过一个宠物项目学习Clojure。该项目将由几个工作者组成,这些成员将从其他功能中调用。
每个工作程序在其自己的名称空间中均定义为一组函数(当前有两个:get-data
用于收集数据,write-data
用于将收集的数据写入文件)。
为了使代码更干燥,我决定编写一个宏,该宏将名称空间中的函数收集到可以传递的映射中:
(ns clojure-bgproc.workers)
(defmacro gen-worker-info []
(let [get-data (ns-resolve *ns* 'get-data)
write-data (ns-resolve *ns* 'write-data)]
`(def ~(quote worker-info)
{:get-data ~get-data
:write-data ~write-data}
)
)
)
在我的工作程序代码中,我使用我的宏(为清楚起见,节略了代码):
(ns clojure-bgproc.workers.summary
(:require [clojure-bgproc.workers :refer [gen-worker-info]]))
(defn get-data [params]
<...>
)
(defn write-data [data file]
;; <...>
)
(gen-worker-info)
虽然它确实起作用(我在get-data
中获得了write-data
和clojure-bgproc.workers.summary/worker-info
函数,但我发现它有些棘手,特别是因为如果我将宏调用移到文件,它不起作用。
我的问题是,还有其他惯用的方法吗?这是惯用的Clojure吗?
谢谢。
答案 0 :(得分:6)
我认为您处在一个怪异的位置,因为您的程序结构错误:
每个工作程序都在其自己的命名空间中定义为一组函数
这是真正的问题。命名空间是放置您将在手写代码中引用的函数和值的好地方。对于您要以编程方式访问的内容,它们不是一个好的存储空间。取而代之的是,将您想要访问的数据放入普通的适当数据结构中,使其成为一流的,然后易于操作。
例如,您正在考虑从名称空间派生的这张worker-info
映射很棒!实际上,这应该是工作人员表示的 only 方式:作为具有工作人员功能键的映射。然后,您只需在某处定义此类工作人员列表的列表(或矢量或地图),这就是您的工作人员列表。无需弄乱名称空间。
答案 1 :(得分:3)
我定义工作人员的首选方法是协议。我还将一些经过良好考验的框架应用于系统生命周期管理。
Protocols提供了一种定义一组方法及其签名的方法。您可能会认为它们与面向对象编程中的接口类似,但更灵活。
您的工人可能会有一些状态和生命周期,例如,工人可能正在运行或停止,获取和释放资源等等。我建议您看一下Integrant来管理具有状态组件(即工作程序)的系统。
在这种情况下,我会避免使用宏。格言data over functions over macros似乎在这里适用。宏在运行时不可用,使调试更加困难,并迫使所有其他查看您的代码的程序员学习一种新的特定于域的语言,即您使用宏定义的语言。