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,而非原始类型提示看起来是可以的。

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

0

评论者:hiredman

是的,我正在给出问题的根本原因,而不是原谅问题。

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

这样一个地方就是直接链接的函数,但我怀疑对于直接链接的函数,Clojure/Core会只是说你应该只为测试对代码进行仪器化,你应当只为生产打开直接链接。

另一个例子,这让我有点惊讶我们还没有听到更多关于这个的问题是协议函数。

0

评论者:seancorfield

你关于直接链接的评论让我想了解spec和仪器化{{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

对于从您的代码调用核心功能(假设不是直接链接的),进行仪器化是有效的,但不会影响核心功能之间的调用,因为它们是直接链接的,并且不通过变量进行。我们考虑已久的另一件事是构建不直接链接且可能打开仪器化或其他实用的开发时工具的内核开发者版本。

0

评论者:seancorfield

感谢你的回答 @alexmiller -- 我们已将开发设置为非直接链接,但QA/生产设置为直接链接,所以我只关注开发中可能存在的(关于instrument-all的)问题,并希望确保“代码不会出错”。如果仪器化不会影响核心中的现有(直接链接)调用,这对我已经足够好。我担心原始提示符和协议(以及从其中出现的任何东西),因为您不希望被迫阅读您包含的每个库的源代码,只是为了确定instrument-all是否安全或者它是否会在开发过程中以某种奇怪的方式伤害你。

0

评论人:borkdude

在为 clojure.core/partition-all 规范时遇到此问题。

由于这个错误似乎将持续存在,也许Kondo应该通知此事?
0
参考:https://clojure.atlassian.net/browse/CLJ-1941(由 alex+import 报告)
...