用于封装的Clojure对象和高阶函数

用于封装的Clojure对象和高阶函数,clojure,encapsulation,higher-order-functions,Clojure,Encapsulation,Higher Order Functions,这是我的后续行动 我从我的阅读中想到了一个奇怪的对象方案,我能想到没有比协议的优势,但我想得到一些意见。我只是在探索高阶函数和封装的使用 (defn new-person "Construct a new Person object and return a Map of accessor methods." [init-first-name init-last-name init-age] (let [first-name (ref init-first-name)

这是我的后续行动

我从我的阅读中想到了一个奇怪的对象方案,我能想到没有比协议的优势,但我想得到一些意见。我只是在探索高阶函数和封装的使用

(defn new-person
  "Construct a new Person object and return a Map of accessor methods."
  [init-first-name init-last-name init-age]
  (let [first-name (ref init-first-name)
        last-name (ref init-last-name)
        age (ref init-age)]
    {:get-first-name #(@first-name)
     :set-first-name #(dosync (ref-set first-name %1))
     :get-last-name #(@last-name)
     :set-last-name #(dosync (ref-set last-name %1))
     :get-age #(@age)
     :set-age #(dosync (ref-set age %1))}))
我可以像这样使用对象:

(def fred (new-person "fred" "flintstone" 42))
并通过以下方式检索访问器方法:

(fred :get-age)
但我不知道如何调用访问器

创建的对象是线程安全的,因为“实例”变量的所有变异都发生在STM中

更新:新的和改进的版本:

(defn new-person
  "Construct a new Person object and return a Map of accessor methods."
  [init-first-name init-last-name init-age]
  (let [first-name (ref init-first-name)
        last-name (ref init-last-name)
        age (ref init-age)]
    {:first-name 
        (fn
          ([] @first-name)
          ([val] (dosync (ref-set first-name val))))
     :last-name
        (fn
          ([] @last-name)
          ([val] (dosync (ref-set last-name val))))
     :age
        (fn
          ([] @age)
          ([val] (dosync (ref-set age val))))}))

也许不是你问题的100%答案,但你会尝试做什么对Clojure来说不是很习惯。“标准”解决方案类似于:

(defrecord Person [first-name last-name age])
(def fred (Person. "fred" "flintstone" 42))
(fred :age)

看起来您正在将OO可变状态强制到Clojure“对象”中。

Clojure的理念不是封装对记录字段本身的访问。封装应该发生在更高的级别上,例如在处理该记录的函数集中。见:

信息的封装是愚蠢的,如果字段是公共的,请使用协议/接口来避免依赖关系


这与你的后续问题相同,用另一组括号将表格括起来。当内部窗体返回函数时,它与返回函数的符号相同。该规则是括号中的第一个形式,它将始终作为特殊形式、宏或函数进行查找。您需要类似quote的内容来防止这种行为

user=> (fred :get-age)
#<user$new_person$fn__531 user$new_person$fn__531@c4afc4>
user=> ((fred :get-age))
42
user=> (let [age-getter (fred :get-age)] (age-getter))
42