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}))

或者使用包装器函数进行另一个间接引用。否则,因为当你创建impl时变量已经被解引用,所以对变量`def-impl`的instrumentation没有效果。

谢谢 @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的根不会对映射f产生影响,因为映射已经不再包含对该变量的引用。
这是一些我已经忘记的魔法,但我现在明白为什么它会起作用。虽然如此,我认为这并不容易理解,应该有人记录下来。
...