Clojure中功能状态更新的模式/习语

时间:2011-05-13 17:29:08

标签: design-patterns functional-programming clojure

我决定尝试在Clojure中编写一个模拟程序(作为概念证明),其中:

  • 模拟的整个状态存储在单个不可变数据结构中
  • 状态更新表示为将状态映射到下一个状态的函数

这意味着我可以编写更新函数,如:

(defn example-update-function [old-state]
  (let [state (atom old-state)]
    (swap! state some-other-update-function-1)
    (if (some-condition @state)
      (swap! state some-conditional-update-function))
    (swap! state some-other-update-function-2)
    (reset! state (some-function @state some-other-param))
    @state))

这种方法似乎有效但下面给出了两个引起关注的原因:

  • 我不得不使用一个可变原子来管理中间状态......看起来不太实用!
  • 代码看起来有点难看,所有的swap!s和@state dereferences无处不在。

有更好/更优雅的方法吗?

3 个答案:

答案 0 :(得分:5)

你可以这样写:

  (defn change-when 
    "If (test val) is truethy, returns (fun val), else returns val"
    [val test fun] (if (test val) (fun val) val))

  (defn example-update-function [old-state]
    (-> old-state
        some-other-update-function-1
        (change-when some-condition some-conditional-update-function)
        some-other-update-function-2
        (some-function some-other-param)
        identity))

可能在单身之路的中途......

答案 1 :(得分:3)

这就是the State monad被发明的原因,它以一种看似惯用的方式对状态修改模式进行编码,但引擎则使用了不可变数据结构的共享。您通常会获得三个操作,两个核心,一个派生:

get :: m s   
  -- Return the state from the internals of the monad.

put :: s -> m ()  
  -- Replace the state inside the monad.

modify :: (s -> s) -> m ()   
  -- Maps an old state to a new state inside a state monad. The old state is thrown away.

所以也许编码那些原语,使你的状态更容易管理。

答案 2 :(得分:1)

单子?每个人都对国家monad感到兴奋,我收集的是为这类事物设计的。