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

欢迎!请参阅关于页面以了解更多有关如何操作的信息。

0
规范
重新标记

你好,自从引入以来,我们的代码库就采用了 extend-with-metadata 协议,我们更喜欢使用映射组件而不是记录。

然而,代码库和团队都是大的 Clojure spec 用户,我们原本期望以下最后一个形式会抛出异常

(ns defproto-spec.test
  (:require [clojure.spec.alpha :as s]
            [clojure.spec.test.alpha :as st]))

(defprotocol SpecTest
  :extend-via-metadata true
  (delete [impl id]))

(s/fdef delete-impl
  :args (s/cat :impl any? :id string?))

(defn delete-impl [_ id]
  (println (str "This deletes " id))
  id)

(def impl (with-meta {} {`delete delete-impl}))

(st/instrument)

(delete impl 1) ;; should throw but it doesn't

为什么我们期望它能正常工作呢?

也许是因为这个功能比较新(1.10)以及以下原因

defproto-spec.test> (st/instrument)
[defproto-spec.test/delete-me defproto-spec.test/delete-impl]

不幸的是,情况并非如此。如果这还不是别人的工作流程,我愿意编写补丁/记录这个问题。

谢谢,
Andrea.

1 答案

+2

我认为你应该像这样保留变量

(def impl (with-meta {} {`delete #'delete-impl}))

或者使用包装函数进行其他间接引用。否则,由于变量 def-impl 已在创建 impl 时被解引用,因此对变量的仪器化没有效果。

by
感谢@borkdude,让我试试 - 你在某个地方找到过这吗?

编辑:成功了!

如果可以,值得记录下来吗?在哪里记录?
by
或许这有帮助

```
user=> (def f (fn [] :foo))
#'user/f
user=> (def m {:f f})
#'user/m
user=> ((:f m))
:foo
user=> (alter-var-root #'f (constantly (fn [] :bar)))
#object[user$eval143$fn__144 0x31e72cbc "user$eval143$fn__144@31e72cbc"]
user=> ((:f m))
:foo
user=> (f)
:bar
```

正如你所见,改变 var f 的根并没有对 map f 产生任何影响,因为 map 已不再包含对这个 var 的引用。
by
这是我忘记的一点小魔法,但现在我明白为什么它会生效。然而,我认为这可能不是很简单易懂,并且应该值得记录在某个地方。
...