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

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

+1
Spec

`
(require '[clojure.spec :as s] '[clojure.spec.test :as st])
(defn foo [^double val] val)
(s/fdef foo :args (s/cat :val double?))
(st/instrument `foo)
(foo 5.2)

user=> (foo 5.2)
ClassCastException clojure.spec.test$spec_checking_fn$fn__13069 无法转换为 clojure.lang.IFn$DO

   	user/eval6 (NO_SOURCE_FILE:5)
   	user/eval6 (NO_SOURCE_FILE:5)
   	clojure.lang.Compiler.eval (Compiler.java:6951)
   	clojure.lang.Compiler.eval (Compiler.java:6914)
   	clojure.core/eval (core.clj:3187)
   	clojure.core/eval (core.clj:3183)
   	clojure.main/repl/read-eval-print--9704/fn--9707 (main.clj:241)
   	clojure.main/repl/read-eval-print--9704 (main.clj:241)
   	clojure.main/repl/fn--9713 (main.clj:259)
   	clojure.main/repl (main.clj:259)
   	clojure.main/repl-opt (main.clj:323)
   	clojure.main/main (main.clj:422)

`

原因:spec用仪器化的函数替换了var值,这些函数在处理原始函数接口时可能不起作用

方法:考虑原始接口并使其正常工作,或者记录/失败说明仪器安装不适用于这些接口。

11 答案

0

评论由:hiredman 提出

spec用仪器化的函数替换var值,这在默认链接情况下效果很好,将var解引用转换为ifn,调用,但在其他情况下(原始函数,直接链接等)则不起作用

0

评论由:kennyjwilli 提出

嗯,至少应该记录这个。所以,不能在具有类型提示参数的函数上使用spec?

0

评论者:seancorfield

不能在参数或返回类型为基本类型的函数上使用Spec,而非基本类型的类型提示似乎没有问题。

但是这里文档不够:如果一个函数(碰巧有基本类型提示)在命名空间加入instrument后出问题,这是不可接受的。如果instrument不起作用,函数应该被跳过(最好能产生一个警告)。

0

评论由:hiredman 提出

是的,我在指出问题的根本原因,并不是在姑息问题。

了解根本原因可以预测其他会有问题的地方:任何使用非默认函数链接策略的地方。

这种地方之一是直接链接的函数,但我怀疑对于直接链接的函数,Clojure/Core会建议你只为测试代码进行instrument,并且在生产中只开启直接链接。

另一个例子,令我有些惊讶的是,我们还没有听到更多关于协议函数的案例。

0

评论者:seancorfield

你关于直接链接的评论让我怀疑spec'ing和instrumenting {{clojure.core}} 函数的有效性。示例显示了 {{clojure.core/symbol}},但Clojure的核心库自1.8.0版本以来就作为直接链接的发送,对吗?

0

评论由:hiredman 提出

改变调用约定的是:编译时启用直接链接的函数,而不是编写该函数的调用者编译时启用直接链接。

在关闭直接链接的情况下,这段代码会对虚假的符号spec抛出一个非符合错误,在开启直接链接的情况下返回符号foo

`
(require '[clojure.spec :as s])

(s/fdef symbol
:args string?
:ret symbol?)

(defn foo
[]
(symbol 'foo))

(s/instrument-all)

(foo)
`

0

评论由:hiredman 提出

此代码返回true是因为m是一个协议函数,如果你用一个普通函数替换它,它将抛出一个非符合错误

`
(require '[clojure.spec :as s])

(defprotocol P
(m [_]))

(deftype T []
P
(m [_] true))

(s/fdef m
:args (s/cat :p (constantly false))
:ret string?)

(defn foo
[]
(m (T.)))

(s/instrument-all)

(foo)
`

0

评论者:alexmiller

@Sean 对核心函数进行仪器化,将适用于从您的代码调用核心(假设不是直接链接的),但不会影响从核心函数到另一个核心函数的调用,因为它们是直接链接的,且不经过 var。我们考虑很久的一件事是构建一个非直接链接的核心开发版本,这样就可以打开仪器化或其他有助于开发时的工具。

0

评论者:seancorfield

感谢你的回答 @alexmiller -- 我们将开发设置为非直接链接,而 QA/生产设置为直接链接,所以我只关心使用 {{(s/instrumental-all)}} 的开发中可能存在的问题,并想确保“代码不会出错”。如果仪器化不会影响核心中的现有(直接链接)调用,那就对我很好。我担心原始提示和协议(以及从木材中爬出来的一切),因为你不希望被迫阅读你包含的每个库的源代码,只是为了看看是否安全或在你开发期间以某种奇怪的方式咬到你。

0

评论者:borkdude

在指定 clojure.core/partition-all 时遇到这个问题。

鉴于这个错误似乎要留下来,也许我们可以让 Kondo 注意到这个问题?
0
参考:https://clojure.atlassian.net/browse/CLJ-1941(由 alex+import 报告)
...