前言
習慣了Ramda.js就會潛意識地認為函式均已柯里化,然後就可以隨心所欲的用函式生成函式,或者使用compose組合多個函式來生成一個新函式。如下
const f = a => b => a + b
const g = c => d => c - d
const compose = f => g => x => f(g(x))
const add1 = f(1)
add1(2) // 返回3
const addThenMinus = compose(g(2), f(1))
addThenMinus(3) // 返回-2
ES6的arrow function讓我們輕易寫出柯里化的函式(當然使用Ramda.js會更輕鬆),若換成ES5就蛋痛很多了。而不幸的是cljs採納和js一樣能夠接受可變引數的函式特性,這使得其必須拋棄如haskell函式自動柯里化的特性。若用cljs實現上述程式碼將會如此地醜陋
(defn f [a]
(fn [b] (+ a b)))
(defn g [c]
(fn [d] (- c d)))
(def add1 (f 1))
那麼要如何才能在cljs中優美地實現柯里化呢?答案是兩步走:
- 實現Ramda.js中
R.curry
函式的cljs版 - 藉助curry函式實現macro
實現curry函式
;; 定義
(defn curry
[f n & args]
(fn [& more]
(let [a (vec (concat args more))]
(if (> n (count a))
(apply curry (reduce conj [f n] a))
(apply f (take n a))))))
;; 使用
(defn f [a b]
(+ a b))
(def fc (curry f 2))
(def add1 (fc 1))
實現defnc巨集
;; 定義
(defmacro defnc [name args & body]
{:pre [(not-any? #{\&} args)]}
(let [n (count args)]
`(def ~name
(curry
(fn ~args ~@body)
~n))))
;; 使用
(defnc f [a b]
(+ a b))
(def add1 (f 1))
總結
cljs中的macro讓我們可以靈活擴充套件語言特性,真是越用越酸爽啊!
尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohnhuang/p/7103601.html ^_^肥仔John