这是我earlier question的后续行动。
我在阅读Let Over Lambda时提出了一个奇怪的对象方案,可以想到没有优于协议的优势,但想获得意见。我正在探索使用高阶函数和封装。
(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))))}))
答案 0 :(得分:5)
也许不是100%回答你的问题,但你试图做的不是Clojure非常惯用。 “标准”解决方案类似于:
(defrecord Person [first-name last-name age])
(def fred (Person. "fred" "flintstone" 42))
(fred :age)
看起来你正在强迫OO可变状态进入Clojure'对象'
答案 1 :(得分:3)
与您的后续问题相同,将表单包装在另一组括号中。当内部表单返回函数时,它与返回函数的符号相同。规则是括号中的第一个表单将始终被查找为特殊形式,宏或函数。你需要像引用这样的东西来阻止这种行为。
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
答案 2 :(得分:1)
Clojure哲学不是封装对记录字段本身的访问。封装应该在更高级别上进行,例如在与该记录一起使用的函数集中。见Clojure - datatypes:
信息封装是愚蠢的字段是公共的,使用协议/接口来避免依赖