在 clojure 上保存状态

Save state on clojure

我现在开始学习函数式编程,我对没有变量的工作感到非常着迷。

我读过的每个教程都说重新定义变量并不酷,但我不知道如何在不保存变量状态的情况下解决我的实际问题。

例如:我正在处理 API,我想在整个请求中保留这些值。假设我有一个添加 person 的端点,并且我有一个 persons 的列表,我想 redefine 或更改我的 persons 的值列表添加新的 person。我该怎么做?

可以使用var-setalter-var-rootconj!吗?

(对于 api,我使用 compojure-api,每个 person 将是一个 Hash

您可能需要在大型应用程序的某处使用可变状态,但并非在所有情况下都需要。

我不熟悉 compojure,但这里有一个使用不可变性的小例子,可能会给你一个更好的主意:

(loop [requests []
       people []

   (let [request (receive-request)]
     ; Use requests/people

     ; Then loop again with updated lists
     (recur (conj requests request)
            (conj people (make-person request))))])

我在这里使用假设的 receive-requestmake-person 函数。

loop 创建一对绑定,并在每个 recur 更新它们。这是 "redefine a variable" 的简单方法。这与纯递归相当,您在任何时候都不会改变最终结果,您只需更改传递给下一次迭代的值。

当然,这非常简单,而且不切实际,因为您一次只能收到一个请求。如果您同时接收来自多个线程的请求,这将是一个原子的合理情况:

(defn listen [result-atom]
  (Thread.
    (fn []
      (while true ; Infinite listener for simplicity
        (let [request (receive-request)]
          (swap! result-atom #(conj % (make-person request))))))))

(defn listen-all []
  (let [result-atom (atom [])]
    (listen result-atom)
    (listen result-atom)))
    ; result-atom now holds an updating list of people that you can do stuff with

swap! 通过 conj 加入它持有的列表来改变原子。原子内部的列表没有发生变化,它只是被自身的修改版本所取代。 持有对旧人员列表的引用的任何人都不会受到调用 swap! 的影响。

更好的方法是使用像 core/async 这样的库,但这样就偏离了问题。

关键是,您可能需要在某处使用可变变量,但对它们的需求比您习惯的要少得多。在大多数情况下,几乎所有事情都可以像第一个示例一样使用不变性来完成。

Clojure 将值与身份区分开来。您可以使用 atoms 来管理 compojure 应用程序中的状态。

(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value

您可以在文档中找到更多信息:

https://clojure.org/reference/atoms

https://clojure.org/reference/data_structures

https://clojuredocs.org/clojure.core/atom