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发布

你关于直接链接的评论让我想到了对 {{clojure.core}} 函数进行Spec和仪器化的有效性。示例显示了 {{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 [_]))

定义类型 T []
函数 P
(m [_] true)

(s/fdef m
:参数 (s/cat :p (constantly false))
:返回值 string?)

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

(s/instrument-all)

(foo)
`

0

评论者:alexmiller

Sean,向核心函数添加监控将在您的代码调用核心时生效(假定它们不是直接链接的),但不会影响一个核心函数调用另一个核心函数,因为它们直接链接且不通过 var。我们考虑了很久的一件事情是构建一个不会直接链接的开发版本的核心,这样就可以开启监控或其他有用的开发时工具。

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 报告)
...