带有可选参数的DSL语法

时间:2014-10-20 18:56:52

标签: clojure dsl

我试图处理以下DSL:

(simple-query 
  (is :category "car/audi/80")
  (is :price 15000))

非常流畅,所以我又添加了一个东西 - 传递给查询的选项:

(simple-query {:page 1 :limit 100}
  (is :category "car/audi/80")
  (is :price 15000))

现在我有一个问题,如何以最文明的方式处理这个案子。正如您所看到的,simple-query可能会将哈希映射作为第一个元素(后面跟着很长的条件列表),或者根本没有哈希映射选项。此外,我希望将默认值作为默认选项集,以防在查询中没有提供明确的部分(或全部)选项。

这就是我想到的:

(def ^{:dynamic true} *defaults* {:page 1 
                                  :limit 50})

(defn simple-query [& body]
  (let [opts (first body) 
        [params criteria] (if (map? opts) 
                             [(merge *defaults* opts) (rest body)]
                             [*defaults* body])]
       (execute-query params criteria)))

我觉得它有点乱。任何想法如何简化这种结构?

2 个答案:

答案 0 :(得分:2)

为了在我自己的代码中解决这个问题,我有一个方便的功能,我希望你能见到...... take-when

user> (defn take-when [pred [x & more :as fail]]
        (if (pred x) [x more] [nil fail]))
#'user/take-when
user> (take-when map? [{:foo :bar} 1 2 3])
[{:foo :bar} (1 2 3)]
user> (take-when map? [1 2 3])
[nil [1 2 3]]

因此我们可以使用它为您的可选地图第一个参数...

实现解析器
user> (defn maybe-first-map [& args]
        (let [defaults         {:foo :bar}
              [maybe-map args] (take-when map? args)
              options          (merge defaults maybe-map)]
          ... ;; do work
          ))

因此,就我而言,您提出的解决方案或多或少都有点,我只是通过将解析器分解为抓取选项图(这里是我的take-when帮助器)来清理它。并将默认值合并到自己的绑定语句中。

一般情况下,使用动态var存储配置是一种反模式,因为在懒惰地评估时可能存在错误行为。

答案 1 :(得分:1)

这样的事情怎么样?

(defn simple-query
  [& body]
  (if (map? (first body))
    (execute-query (merge *defaults* (first body)) (rest body))
    (execute-query *defaults* body)))