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)

用户=> (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 deref 显式转换为 ifn 并调用,但在其他情况下(原始函数、直接链接等)则不行

0

评论者:kennyjwilli

嗯。至少这也是应该被记录下来的。因此,不能在具有类型提示参数的函数上使用 spec 了吗?

0

评论者:seancorfield

Spec 不能在具有原始类型提示参数或返回类型的函数上使用——非原始类型提示似乎没有问题。

但是在这里,文档是不够的:对一个命名空间进行仪器化,然后发现它破坏了一个函数(恰好有一个原始类型提示),这是不可接受的。如果仪器化不起作用,则应该跳过该函数(并生成一个警告,希望是如此)。

0

评论者:hiredman

是的,我正在给出问题的根源,而不是为问题辩解。

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

其中一个这样的地方是直接链接的函数,但我怀疑对于直接链接的函数,Clojure/Core会建议你只对测试代码进行仪器化,你只应该在生产中对直接链接进行开启。

另一个令我们感到意外的案例是协议函数。

0

评论者:seancorfield

您关于直接链接的评论让我对指定和仪器化 {{clojure.core}} 函数的有效性感到困惑。示例显示了 {{clojure.core/symbol}},但是Clojure的核心库自1.8.0版本以来都是直接链接的分发的,对吧?

0

评论者:hiredman

不是用直接链接编译的函数改变了调用约定,而是用直接链接编译的这个函数的调用者改变了调用约定。

此代码将在直接链接关闭时对错误的符号规范抛出非合规错误,并在直接链接打开时返回符号 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
P
:args (s/cat :p (constantly false))

(defn foo
[]
P

(s/instrument-all)

(foo)
`

0

评论人:alexmiller

@Sean 修改核心函数可以通过你的代码调用核心(可能是间接链接的),但不能影响一个核心函数调用另一个核心函数,因为它们是直接链接的,并且不通过变量传递。我们考虑了很长时间的一个问题是构建一个不直接链接的核心开发版本,这个版本的内核可以开启仪表化或其他的开发时工具。

0

评论者:seancorfield

感谢你的回答 @alexmiller -- 我们将开发设置为了非直接链接,而QA/生产设置为直接链接,所以我只关注开发中{{(s/instrumental-all)}}可能的问题,并确保“代码不会中断”。如果仪表化不会影响核心中的现有(直接链接)调用,对我而言就已经足够好了。我关注的是原始提示符和协议(以及可能的任何东西),因为你不想被强迫阅读你包含的每个库的源代码,只是为了看到{{(s/instrument-all)}}是否安全,或者在开发过程中以某种奇怪的方式咬你。

0

评论人:borkdude

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

这个错误似乎会一直存在,也许Kondo应该意识到这个问题?
0
参考:https://clojure.atlassian.net/browse/CLJ-1941 (由 alex+import 提出)
...