문제

In this discussion Brian Marick makes the point that let and for are monads in Clojure:

That said, the really general-purpose monads tend to get written into the language as special forms. Clojure's let and for are both monads, but you don't need to know that to use them.

This is let

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

This is for

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

My question is: Why are Clojure's let and for both monads?

도움이 되었습니까?

해결책

Why are Clojure's let and for both monads?

They aren't.

Clojure's let and for are not monads because they do not fully expose their Monadic common structure. They are more like monads in a sugary prison.

What are monads?

In Clojure parlance, a monad could be described as reification of a Monad protocol whose functions are expected to behave with each other and on the reified type in certain well defined ways. This is not to say that monads have to be implemented with defprotocol, reify, and friends, but this gives the idea without having to talk about typeclasses or categories.

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

Sugar

Monads can be messy to use

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

Without some sugar

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

This is easier to write

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

But isn't that just...?

This looks a lot like

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

And

(do-monad id-monad 
  [a 6 
   b (inc a)] 
  (* a b))
;=> 42

Looks a lot like

(let
  [a 6 
   b (inc a)] 
  (* a b))
;=> 42

So, yes, for is like the sequence monad and let is like the identity monad, but in the confines of a sugared expression.

But that's not all monads are.

Monads' structure/contract can be exploited in other ways. Many useful monadic functions can be defined in terms of only bind and unit, for example

(defn fmap 
  [monad f mv] 
  (bind monad mv (fn [v] (unit monad (f v)))))

So that they can be used with any monad

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

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

This might be a rather trivial example, but more generally/powerfully monads can be composed, transformed, etc. in a uniform way due to their common structure. Clojure's let and for don't fully expose this common structure, and so cannot fully participate (in a generic fashion).

다른 팁

I would say it's more correct to call let and for do-notation for the identity monad and the list monad, respectively - they're not themselves monads, since they're bits of syntax rather than data structures with associated functions.

The reason monads come up at all is that for is a nice notation that exploits the monadic behavior of lists (or in clojure, sequences) to easily write code that does some useful things.

let is the identity monad. There is no special machinery, each binding is just made available to the subsequent stages of the computation.

for is the list / sequence monad with guards, plus a little something extra. Here each step in the computation is expected to produce a sequence; the machinery of the monad takes care of concatenating sequences. Guards can be introduced with :when, while :let introduces intermediate helper bindings (as let does in Haskell). The "something extra" comes in the form of :while.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top