haw*_*eye 12 monads clojure let
在这次讨论 布赖恩·马里克使得该点let并for有单子Clojure中:
也就是说,真正通用的monad倾向于以特殊形式写入语言.Clojure是
let和formonad,但你不需要知道使用它们.
这是 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 let和for两个monad?
A. *_*ebb 35
为什么Clojure let和for两个monad?
他们不是.
Clojure的let和for没有单子,因为他们并不完全暴露自己的单子共同的结构.他们更像是一个含糖监狱的单子.
什么是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 let并for没有完全暴露这种常见的结构,因此无法完全参与(以通用的方式).
我会说分别为单身monad和list monad 调用let和fordo-notation 更为正确- 它们本身不是monad,因为它们是语法而不是具有相关函数的数据结构.
monad出现的原因是,这for是一个很好的符号,利用列表的monadic行为(或在clojure,序列中)来轻松编写执行一些有用的东西的代码.