2024 Clojure 状态调查 中分享您的想法!

欢迎!请查看 关于 页面,了解更多关于这个网站的信息。

0
core.cache

我在使用 core.cache,特别是在 clojure.core.cache.wrapped 命名空间。

我发现即使对命名空间工厂提供的原子之一进行解引用,获得的映射也不会与外观相同的映射相等

(= {1 1} @(clojure.core.cache.wrapped/lru-cache-factory {1 1})) ;; => false

原因似乎是因为 defcache LRUCache(这等同于 deftype)没有实现相等性。

这个问题可以修复吗?

1 个答案

+1
by
selected by
 
最佳答案

这并不是一个错误。缓存可能会以哈希表的形式打印,但它并不是一个哈希表。

相同类型的两个缓存可以用于比较相等性(但不同类型的两个缓存不行)——根据 https://clojure.org/guides/equality,因为缓存实现了 clojure.lang.IPersistentCollectionequiv 方法。

您可以通过互操作访问缓存中的底层基本字段,但您将依赖于实现细节(字段名)
user=> (= {1 1} (.cache @(clojure.core.cache.wrapped/lru-cache-factory {1 1}))) true

by
我对用法案例很好奇。缓存能减轻程序处理一些它(按设计)不感兴趣的细节。但另一方面,...
by
非常感谢回答。然而,在我原始的代码片段中,如果你改变等号(`=`)的顺序(例如,计算 `(= @(clojure.core.cache.wrapped/lru-cache-factory {1 1}) {1 1})`),结果将是 `true`。你不会期望 `=` 的结果取决于操作数的放置位置吧?
by
缓存API的独特之处在于,由于它们是不可变缓存,因此任何通常会在可变缓存中更改某些元数据(TTL、使用数据等)的操作都必须返回新的缓存实例。

除了实际的缓存API之外,还构建了一个“映射类似”API,以便您可以对它们使用Clojure的获取、关联、拆除操作,但这个层很难处理一些奇特的边缘情况,例如在检查项是否在缓存中以及随后查找它(发现它已“消失”)之间的竞态条件 – 尽管缓存本身是不可变的。这给上游消费者带来了问题,例如core.memoize,它必须包含自旋/重试逻辑才能避免错误地返回nil。

这就是为什么我添加了包装命名空间,它提供了更直观的“可变缓存”(通过在原子中包装不可变缓存),但这些缓存的“安全”API并不真的是“映射类似”:查找-如果不存在是最安全的函数,它提供了一种避免竞态条件和重复执行的方法,同时仍然可以在需要时按需进行查找以重新计算所需值。

创建后能够用新的哈希表值“初始化”缓存是一种内在的突变操作,同样“驱逐”也是。提供“映射类似”的获取操作是一种便利,但你必须接受如果请求的缓存条目已过期,则 خواهد nhận được nil(或“丢失”值)。

如readme注释所述:“core.cache API难以正确使用。”并且它链接到了这个https://dev.to/dpsutton/exploring-the-core-cache-api-57al,其中讨论了将缓存当作“仅仅是映射”处理所引起的野外错误。
@vemv 关于参数顺序的切换——这与在两个事物上尝试执行操作(只有一个实际上是集合)时看到的情况相同:尝试一种顺序时它(意外地)可以工作,尝试另一种顺序时它失败(因为你在非集合上进行集合操作)。

更深入地回顾源代码后,我认为我上面的答案在细节上是不正确的,但从总体意义上说,Clojure声称两个deftype实例只有它们是相同类型时才是相等的。

正如我对@pbwolf的更长时间响应所表明的,core.cache的“类似映射”API是不稳定的,并导致人们在使用它时出错:|
感谢!这些见解很有用。

因此,我最终实现了一个薄的包装器,以确保人们使用正确的 方法和仅以安全的方式访问底层数据。

仅仅作为一个观察,我想知道改变deftype->defrecord是否会有所帮助?
...