这是Clojure中可变状态的合理monad吗?

时间:2010-10-20 16:08:25

标签: java functional-programming clojure monads mutable

我一直在Clojure中尝试使用monad,并提出了以下代码,其中monadic值/状态对由可变Clojure deftype对象表示。

由于对象是可变的,因此优点似乎是您可以编写monadic代码而无需一直构造新的结果对象。

但是,我对monads很新,所以很想知道:

  • 这个结构有意义吗?
  • 它实际上是否可以作为monad工作?

以下代码:

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

1 个答案:

答案 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