为什么Clojure';两个单子的“let”和“for”?

为什么Clojure';两个单子的“let”和“for”?,clojure,monads,let,Clojure,Monads,Let,指出和是Clojure中的单子: 也就是说,真正通用的单子往往以特殊形式写入语言。Clojure的let和for都是单子,但使用它们不需要知道这一点 这是let user=> (let [c (+ 1 2) [d e] [5 6]] (-> (+ d e) (- c))) 8 这是 user=> (for [x [0 1 2 3 4 5] :let [y (* x 3)] :when (even?

指出和是Clojure中的单子:

也就是说,真正通用的单子往往以特殊形式写入语言。Clojure的
let
for
都是单子,但使用它们不需要知道这一点

这是
let

user=> (let [c (+ 1 2)
         [d e] [5 6]]
     (-> (+ d e) (- c)))
8
这是

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

我的问题是:为什么Clojure的
let
都是单子?
let
是身份单子。没有特殊的机制,每个绑定只是提供给后续的计算阶段


for
是带有保护的列表/序列单子,外加一些额外的东西。这里,计算中的每一步都会产生一个序列;单子的机器负责连接序列。警卫可以通过
:when
引入,而
:let
引入中间助手绑定(就像Haskell中的
let
一样)。“额外的东西”以
的形式出现:虽然

我想说,更正确的做法是分别调用
let
for
为标识单子和列表单子做标记-它们本身不是单子,因为它们是语法位,而不是带有相关函数的数据结构


之所以出现单子,是因为
for
是一种很好的表示法,它利用了列表(或clojure中的序列)的单子行为来轻松编写代码,从而完成一些有用的事情。

为什么clojure的
let
for
都是单子?

他们不是

Clojure的
let
for
不是单子,因为它们没有完全公开它们的一元公共结构。他们更像是被关在甜蜜监狱里的单子

什么是单子?

用Clojure的话来说,monad可以被描述为monad协议的具体化,其功能应该以某种定义良好的方式在具体化类型上相互作用。这并不是说monad必须使用
defprotocol
reify
和friends来实现,但这提供了一个不必讨论类型类或类别的想法

(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])))

单子使用起来可能很混乱

(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)
不加糖

(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))
这更容易写

(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)
但这不只是…?

这看起来很像

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

看起来很像

(let
  [a 6 
   b (inc a)] 
  (* a b))
;=> 42
因此,是的,
for
类似于序列单子,
let
类似于身份单子,但在糖化表达式的范围内

但这并不是单子的全部。

monad的结构/契约可以通过其他方式加以利用。例如,许多有用的一元函数只能根据
bind
unit
来定义

(defn fmap 
  [monad f mv] 
  (bind monad mv (fn [v] (unit monad (f v)))))
因此,它们可以与任何单子一起使用

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

(fmap seq-monad inc [1 2 3 4])
;=> (2 3 4 5)

这可能是一个相当简单的例子,但由于它们的共同结构,更普遍/强大的单子可以以统一的方式组合、转换等。Clojure的
let
for
没有完全公开此公共结构,因此无法完全参与(以通用方式)。

我不同意“for loop”标记。因为在Clojure中是列表理解,而不是for循环。只是说……嗯,这比我刚才写的怪物要短得多,而且切中要害@韦伯:不过,你的回答很酷。您有一个错误需要纠正:第一次编写“这与…”时,您实际上包含了不同的输出。
(fmap id-monad inc 1)
;=> 2

(fmap seq-monad inc [1 2 3 4])
;=> (2 3 4 5)