我很想听听这里的Clojure专家在管理层级状态方面有什么建议。 我发现自己经常使用{:structures {:like {:this {:with {:many 'levels}} } } }
,如果我希望在多个级别跟踪状态更改,则通过将原子值传递给它们 (atom {:like (atom 'this)})
,我会觉得这样做可能是错误的。 通常情况下,是在顶层只使用一个原子,并且在映射中没有原子作为值更好吗?
尽量避免在数据结构中使用嵌套的原子(atom)。
主要原因是不可变性会对你有所帮助。Clojure是一种以不可变数据结构为基础的函数式语言。大多数库都默认使用不可变数据结构,而Clojure的STM假定使用不可变数据结构可以获得最佳的并发性能。不可变性使你有机会在任何一个瞬间对整个状态进行一致性快照。基于不可变数据操作的纯函数易于开发和测试。
如果在数据结构中放置原子,则会失去所有不可变性优势,并且可能使代码非常复杂。如果数据结构包含许多可变组件,则很难推理其意义。
一些替代方法建议如下:
您可以使用assoc-in
、get-in
、update-in
和dissoc-in
函数来处理嵌套结构。
这些函数非常方便,但我不知道它们是否能直接处理原子等。在最坏的情况下,您应该能够将它们嵌套到deref中,例如:
(def m (atom {:like {:this {:nested (atom {:value 5})}}}))
@(get-in @m [:like :this :nested])
; => {:value 5}
(get-in @(get-in @m [:like :this :nested]) [:value])
; => 5
你可以使用->
使其更易读:(-> @m
(get-in [:like :this :nested])
deref
(get-in [:value]))
; => 5
关于嵌套的原子/引用/代理等问题,我认为这取决于您想要实现的目标。如果顶部只有一个,而且更改是同步的,那么事情会更容易理解。
另一方面,如果您不需要这种同步,那么做这件事将浪费时间,使用嵌套的原子/引用/代理会更好。
总之,我认为两种方式都没有“正确的方法”,它们都有其用途。
我更喜欢在顶层使用一个原子,因为这会使事情变得非常简单,也表明数据表示的是一种状态,该状态通过操作一次性修改。如果在每个级别上都放置原子,则会变得过于复杂,无法弄清楚正在发生什么。此外,如果在您的情况下嵌套过深,则建议您坐下来仔细考虑是否需要这样的结构,或者是否有更好的替代可能,因为这肯定会导致复杂性,直到嵌套数据是递归的(即每个级别上具有相同的结构)。