我一直在Clojure中尝试使用monad,并提出了以下代码,其中monadic值/状态对由可变Clojure deftype对象表示。
由于对象是可变的,因此优点似乎是您可以编写monadic代码而无需一直构造新的结果对象。
但是,我对monads很新,所以很想知道:
以下代码:
(defprotocol PStateStore
(set-store-state [ss v])
(get-store-state [ss])
(set-store-value [ss v])
(get-store-value [ss]))
(deftype StateStore [^{:unsynchronized-mutable true} value
^{:unsynchronized-mutable true} state]
PStateStore
(get-store-state [ss] (.state ss))
(get-store-value [ss] (.value ss))
(set-store-state [ss v] (set! state v))
(set-store-value [ss v] (set! value v))
Object
(toString [ss] (str "value=" (.value ss) ", state=" (.state ss))))
(defn state-store [v s] (StateStore. v s))
(defmonad MStoredState
[m-result (fn [v]
(fn [^StateStore ss]
(do
(set-store-value ss v)
ss)))
m-bind (fn [a f]
(fn [^StateStore ss]
(do
(a ss)
((f (get-store-value ss)) ss))))])
; Usage examples
(def mb
(domonad MStoredState
[a (m-result 1)
b (m-result 5)]
(+ a b)))
(def ssa (state-store 100 101))
(mb ssa)
; => #<StateStore value=6, state=101>
答案 0 :(得分:3)
不,它作为monad无法正常工作,因为你使用了可变状态。
想象一下,你有一个monadic值m
(一个携带状态的值),你称之为StateStore
。您希望能够这样做:
(let
[a (incr-state m)
b (decr-state m)]
(if some-condition a b))
我期望这个计算返回monadic m
,其状态根据some-condition
递增或递减。如果使用可变状态,则在评估此代码期间, 将递增和递减。
关于monad的一个好处是,虽然它们代表效果,但它们表现为普通的纯不可变值。你可以传递它们,复制它们(你可以扩展任何let
- monadic值的定义,用它在每个使用站点的定义替换它的名称)。唯一需要注意的地方是使用m-bind
实际链效果的地方。否则,在通常的命令式编程中,不会在代码的不相关部分中隐式链接效果。在你想要限制副作用的情况下,这使得monad的推理更容易,更舒适。
您可能听说过 monadic law ,这是任何monad实现应该尊重的等式。但是,你的问题不在于你违法,因为法律没有谈到这一点。实际上,monadic定律通常用纯语言Haskell表示,因此不考虑副作用。
如果你愿意,你可以将它视为第四个未说出的monad法则:好的monad应该尊重referential transparency