Ответ 1
Почему Clojure let
и for
обе монады?
Это не так.
Clojure let
и for
не являются монадами, потому что они не полностью раскрывают свою монадическую общую структуру. Они больше похожи на монады в сладкой тюрьме.
Что такое монады?
В выражении Clojure монада может быть описана как переопределение протокола Monad, функции которого, как ожидается, будут вести себя друг с другом и по типу reified определенными определенными способами. Это не означает, что монады должны быть реализованы с помощью 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])))
Сахар
Монады могут быть беспорядочными, чтобы использовать
(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)
и
(do-monad id-monad
[a 6
b (inc a)]
(* a b))
;=> 42
Похож на
(let
[a 6
b (inc a)]
(* a b))
;=> 42
Итак, да, for
похож на монаду последовательности, а let
похож на монодальную идентичность, но в рамках выраженного sugared выражения.
Но это не все монады.
Структура/контракт Monads могут использоваться другими способами. Многие полезные монадические функции могут быть определены только в терминах 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
не полностью раскрывают эту общую структуру и поэтому не могут в полной мере участвовать (в общем виде).