Clojure 库:类 / 实例 / 纯功能

Clojure library: classes / instances / purely functional

我正在用 clojure 开发一个库,它在某种程度上需要是有状态的。为了不在抽象描述中花费太多文字,这里将是一个 OOP 示例,说明我如何想象可以使用库的 API。

mylib = new lib();

state1 = mylib.getState();
mylib.continue();
state2 = mylib.getState();
mylib.continue();
state3 = mylib.getState();
[..]

(显然 state1 != state2 != state3)

好的,这怎么能用像 Clojure 这样的函数式语言来完成呢?

我想到的一种方法:

(require '(lib.core :as mylib))

(def state1 (mylib/start-state))
(def state2 (mylib/continue state1))
(def state3 (mylib/continue state2))
[..]

这种方法不会将状态保持分配给库端。我遇到的问题如下:我的状态确实保留了信息,应该是 public 给 API 用户。但它也保留了对下一个状态的生成很重要的信息,但是与 public.

无关

好吧,可能还有另一个函数 (mylib/extract-relevant-data state1) 可以按顺序对 "clean usage" 的状态进行后处理 "clean usage"。

我真的很想知道在 Clojure 中我可以用哪些方法来处理这个问题。

方法 1:重新实现 OOP。我将 last-time-called 视为私人数据,将 relevant state 视为您想向人们展示的内容。

(defn start-state []
  (let [last-time-called (atom (new java.util.Date))
        relevant-state (atom 1)
        private-update (fn [] (swap! last-time-called (fn [_] (new java.util.Date))))
        get-state (fn [] (do (private-update)
                             @relevant-state))
        continue (fn [] (do (private-update)
                            (swap! relevant-state inc)
                            nil))]
    {:get-state get-state :continue continue}))

示范:

stack-prj.hiddenState> (def mylib (start-state))
#'stack-prj.hiddenState/mylib
stack-prj.hiddenState> ((mylib :get-state))
1
stack-prj.hiddenState> ((mylib :continue))
nil
stack-prj.hiddenState> ((mylib :get-state))
2
stack-prj.hiddenState> ((mylib :continue))
nil

请注意 get-statecontinue 可以访问 last-time-called,如果他们需要的话。

方法 2:简单数据上的纯函数

(defn new-lib []
  {:relevant-state 1
   :last-time-called (new java.util.Date)})

(defn get-state [lib]
  (lib :relevant-state))

(defn lib-continue [lib]
  {:relevant-state (inc (lib :relevant-state))
   :last-time-called (new java.util.Date)})

示范:

stack-prj.noHiddenState> (def mylib (new-lib))
#'stack-prj.noHiddenState/mylib
stack-prj.noHiddenState> (get-state mylib)
1
stack-prj.noHiddenState> (def ml2 (lib-continue mylib))
#'stack-prj.noHiddenState/ml2
stack-prj.noHiddenState> (get-state ml2)
2

请注意,使用方法 2,当用户通过 get-state 访问状态时,您无法更新对象的私有变量。如果你需要这个功能,那么方法一更能满足你的需求;如果你不这样做,那么方法 2 提供了比第一种方法更干净、惯用和可维护的代码。