为什么Clojure';两个单子的“let”和“for”?
指出和是Clojure中的单子: 也就是说,真正通用的单子往往以特殊形式写入语言。Clojure的为什么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?
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)