1

“推敲”这个词来源于唐朝诗人贾岛的故事,说他为诗中的“推”“敲”一字之差,反复思索。

Clojure 程序的基础是函数,函数和使用者唯一的合同是它的参数表,我们 Clojure 对待它也应该有推敲的精神!

不应该有不被引用的参数

这看起来是显然的,但我们经常就在反复改动一个函数实现的时候,忘记检查它有没有没有用的参数。

参数要求越简单精确越好

不要为了一个值引入整个结构。例如:

(defn hello [user]
  (str "Hello" (:name user)))

这个函数既然只使用 name ,就不如改成这样更精确:

(defn hello [name]
  (str "Hello" name))

这个原则的例外在于,有时为了连续调用,我们需要一些参数的统一性。这时可以使用 destructure 这个强大的语言特性来明示我们的需求:

(defn hello [{name :name}]
  (str "Hello" name))

这样的调用形式既可以满足要求,也更为精确地说明了我们需要的不是一个特别的 user 的 map,而仅仅是一个有 :name 值的 map。

尽可能避免“过路参数”

所谓“过路参数”:函数对参数值不感兴趣,把他放进参数表仅仅为了调用其他函数。这样的参数在程序中很常见,例如上例中的函数实际就仅仅是 str 函数的包装。即使 name 参数都是过路参数,其实,第二个函数定义写成这样:

(def hello (partial str "Hello"))

根本不引入一个额外的参数表更加精确。而最后一种形式写成:

(def hello (comp (partial str "Hello") :name))

用函数组合来代替新建函数更为合适。

当然,这条规则实际上用在 hello 这么简单的函数上缺乏说明性,我们用任何一种写法都很容易理解。可是在实际的工作中,随意引入过路参数却会严重影响我们的大设计。

(defn order-description [{:keys [amount]} username]
  (let [client-type (if (> amount 10000) "big" "small"]
    (str (call-name username) " is a " client-type " client."))) 

(order-description {:amount 15000} "Robert")

这个函数中的 username 又是一个过路参数,它在参数表中出现,将我们函数实际上仅仅关心 call-name 的结果的逻辑掩盖了起来,并且还造成了我们对 call-name 这个函数的依赖。改成这样:

(defn order-description [{:keys [amount]} call-name]
  (let [client-type (if (> amount 10000) "big" "small"]
    (str call-name " is a " client-type " client.")))

(order-description {:amount 15000} (call-name "Robert")) 

就将 call-name 的调用责任放在了它的调用者,让 order-description 函数的使用范围更广和更加稳定。


robertluo
738 声望21 粉丝

引用和评论

1 篇内容引用
0 条评论