为什么Clojure的'let`和`for`都是monad?

haw*_*eye 12 monads clojure let

在这次讨论 布赖恩·马里克使得该点letfor有单子Clojure中:

也就是说,真正通用的monad倾向于以特殊形式写入语言.Clojure是letformonad,但你不需要知道使用它们.

这是 let

user=> (let [c (+ 1 2)
         [d e] [5 6]]
     (-> (+ d e) (- c)))
8
Run Code Online (Sandbox Code Playgroud)

这是 for

user=> (for [x [0 1 2 3 4 5]
             :let [y (* x 3)]
             :when (even? y)]
         y)
(0 6 12)
Run Code Online (Sandbox Code Playgroud)

我的问题是:为什么Clojure letfor两个monad?

A. *_*ebb 35

为什么Clojure letfor两个monad?

他们不是.

Clojure的letfor没有单子,因为他们并不完全暴露自己的单子共同的结构.他们更像是一个含糖监狱的单子.

什么是monads?

在Clojure的说法中,monad可以被描述为Monad协议的具体化,Monad协议的功能预期彼此之间以及在某些明确定义的方式上的具体类型.这并不是说monad必须用defprotocol,reify和朋友一起实现,但是这给出了这个想法,而不必讨论类型类或类别.

(defprotocol Monad 
  (bind [_ mv f]) 
  (unit [_ v]))

(def id-monad
  (reify Monad 
    (bind [_ mv f] (f mv))
    (unit [_ v] v)))

(def seq-monad 
  (reify Monad 
    (bind [_ mv f] (mapcat f mv)) 
    (unit [_ v] [v])))
Run Code Online (Sandbox Code Playgroud)

Monads可能很乱使用

(bind seq-monad (range 6) (fn [a] 
(bind seq-monad (range a) (fn [b] 
(unit seq-monad (* a b))))))
;=> (0 0 2 0 3 6 0 4 8 12 0 5 10 15 20)
Run Code Online (Sandbox Code Playgroud)

没有一些糖

(defn do-monad-comp 
  [monad body return] 
  (reduce 
    (fn [a [exp sym]] (list 'bind monad exp (list 'fn [sym] a))) 
    (list 'unit monad return) 
    (partition 2 (rseq body))))

(defmacro do-monad [monad body return] 
  (do-monad-comp monad body return))
Run Code Online (Sandbox Code Playgroud)

这更容易编写

(do-monad seq-monad 
  [a (range 6) 
   b (range a)] 
  (* a b))
;=> (0 0 2 0 3 6 0 4 8 12 0 5 10 15 20)
Run Code Online (Sandbox Code Playgroud)

但这不仅仅是......?

这看起来很像

(for
  [a (range 6) 
   b (range a)] 
  (* a b))
;=> (0 0 2 0 3 6 0 4 8 12 0 5 10 15 20)
Run Code Online (Sandbox Code Playgroud)

(do-monad id-monad 
  [a 6 
   b (inc a)] 
  (* a b))
;=> 42
Run Code Online (Sandbox Code Playgroud)

看起来很像

(let
  [a 6 
   b (inc a)] 
  (* a b))
;=> 42
Run Code Online (Sandbox Code Playgroud)

所以,是的,for一样的序列单子和let一样的身份单子,但在界限加糖的表达.

但那不是所有的monad都是.

莫纳德的结构/合同可以通过其他方式加以利用.例如,可以仅根据bind和定义许多有用的monadic函数unit

(defn fmap 
  [monad f mv] 
  (bind monad mv (fn [v] (unit monad (f v)))))
Run Code Online (Sandbox Code Playgroud)

这样它们就可以和任何monad一起使用

(fmap id-monad inc 1)
;=> 2

(fmap seq-monad inc [1 2 3 4])
;=> (2 3 4 5)
Run Code Online (Sandbox Code Playgroud)

这可能是一个相当简单的例子,但由于它们的共同结构,更通用/有力的monad可以以统一的方式组合,转换等.Clojure letfor没有完全暴露这种常见的结构,因此无法完全参与(以通用的方式).


ama*_*loy 9

我会说分别为单身monad和list monad 调用letfordo-notation 更为正确- 它们本身不是monad,因为它们是语法而不是具有相关函数的数据结构.

monad出现的原因是,这for是一个很好的符号,利用列表的monadic行为(或在clojure,序列中)来轻松编写执行一些有用的东西的代码.