Clojure 2024调查问卷中分享您的看法!

欢迎!请参阅关于页面以获取有关此工作方式的更多信息。

0
Spec
重标记

大家好,自从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

我认为您应该像这样持有var:

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

或者使用包装函数进行另一个间接引用。否则,因为var def-impl在创建impl时已经解引用,所以对var进行监控没有效果。

感谢 @borkdude,让我试试看 - 你在某个地方找到这个信息了吗?

编辑:它工作了!

如果值得一写,在哪里记录这个信息呢?
也许这能有所帮助

```
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
```

正如你所见,改变变量 f 的根对 map f 没有任何影响,因为 map 已经不再包含对该变量的引用。
这是我曾经忘记的一种魔法,现在我明白了为什么它有效。然而——这可能不是很容易理解,我认为应该把它记录在某个地方。
...