简化这个monad表达式

时间:2012-09-07 23:47:22

标签: clojure

在观看本教程后,我正在研究一些自己的示例:http://www.infoq.com/presentations/Why-is-a-Monad-Like-a-Writing-Desk并阅读http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html

我提出了以下功能:

(defn unit [v s]
  (fn [] [v s]))

(defn bind [mv f]
  (f (mv)))

(defn inc+ [mv]
  (bind
   mv
   (fn [[v s]]
     (let [r (inc v)]
       (unit r (apply str (concat s " inc+(" (str r) ")")))))))

(defn double+ [mv]
  (bind
   mv
   (fn [[v s]]
     (let [r (* 2 v)]
       (unit r (apply str (concat s " double+(" (str r) ")")))))))

(defn triple+ [mv]
  (bind
   mv
   (fn [[v s]]
     (let [r (* 3 v)]
       (unit r (apply str (concat s " triple+(" (str r) ")")))))))

;; Testing:

((-> (unit 1 "1 ->") inc+))
;; => [2 "1 -> inc+(2)"]


((-> (unit 3 "3 ->") inc+ double+ inc+))
;; => [27 "3 -> inc+(4) double+(8) inc+(9) triple+(27)"]

我希望重写bind来封装方法inc + double +和triple +的模式,并获得与以前相同的输出。怎么会这样做?

1 个答案:

答案 0 :(得分:1)

我分两个阶段想出来:

(defn unit [v s]
  (fn [] [v s]))

(defn bind [mv f]
  (let [[iv is] (mv)
        [av as] (f iv)]
    (unit av (str is " " as))))

(defn inc+ [mv]
  (bind
   mv
   (fn [v]
     [(inc v) (str " inc+(" (inc v) ")")])))

(defn double+ [mv]
  (bind
   mv
   (fn [v]
     [(inc v) (str " double+(" (inc v) ")")])))

(defn triple+ [mv]
  (bind
   mv
   (fn [v]
     [(inc v) (str " triple+(" (inc v) ")")])))

然后:

(defn unit [v s]
  (fn [] [v s]))

(defn bind [mv f]
  (let [[v s] (mv)
        r     (f v)
        xs    (->> (str (type f))
                   (re-find #"\$([^\$]*)\$?")
                   second) ]
    (unit r (str s " " (str xs "(" r ")")))))

(defn inc+ [mv]
  (bind mv inc))

(defn double+ [mv]
  (bind mv #(* 2 %)))

(defn triple+ [mv]
  (bind mv #(* 3 %)))

((-> (unit 3 "3 ->") inc+ double+ inc+ triple+))
;;=> [27 "3 -> inc_PLUS_(4) double_PLUS_(8) inc_PLUS_(9) triple_PLUS_(27)"]

所以看看其他Monad教程,特别是http://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads,我想我现在理解核心原则。 'Monads'真的就是能够重用我们手头的功能。 unitbind必须设计为协同工作。然后,将函数组合在一起几乎是微不足道的。

然后再写一个do-m运算符的抽象:

(defn unit [v s]
  (fn [] [v s]))

(defn bind [mv f]
  (let [[v s] (mv)
        r     (f v)
        xs    (->> (str (type f))
                   (re-find #"\$([^\$]*)\$?")
                   second) ]
    (unit r (str s " " (str xs "(" r ")")))))

(defn double [v] (* 2 v))

(defn triple [v] (* 3 v))

(defn do-m [v & fs]
  (let [fn-ms (map #(fn [mv] (bind mv %)) fs)]
    (((apply comp (reverse fn-ms)) (unit v (str v "->"))))))

(do-m 3 inc double triple)
;;=> [24 "3 -> inc(4) double(8) triple(24)"]

这是另一种写入实现相同结果的方法,请注意,更改是取出unit中的lambda函数以及binddo-m的相关调用。

(defn unit [v s] [v s])

(defn bind [mv f]
  (let [[v s] mv
        r     (f v)
        xs    (->> (str (type f))
                   (re-find #"\$([^\$]*)\$?")
                   second) ]
    (unit r (str s " " (str xs "(" r ")")))))


(defn double [v] (* 2 v))
(defn triple [v] (* 3 v))
(defn sqrt [v] (Math/sqrt v))

(defn do-m [v & fs]
  (let [fn-ms (map #(fn [mv] (bind mv %)) fs)]
    ((apply comp (reverse fn-ms)) (unit v (str v " ->")))))

(do-m 3 inc double  double triple triple sqrt)
;; => [12.0 "3 -> inc(4) double(8) double(16) triple(48) triple(144) sqrt(12.0)"]