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 cannot be cast to 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

不能在具有原始类型提示的参数或返回值的函数上使用规范 -- 非原始类型提示看起来没问题。

但在这种情况下,文档不足以说明:如果一个命名空间被仪表化后导致一个函数破坏(巧合的是该函数具有原始类型提示),这是不可接受的。如果仪表化不起作用,应跳过该函数(并希望能够产生一个警告)。

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)
:args (s/cat :p (constantly false))
:ret string?)

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

(s/instrument-all)

(foo)
`

0

评论者:alexmiller

利用Seam 为核心功能进行instrumenting 将适用于从您的代码调用核心(这些假设不是直接链接的),但不会影响从一个核心函数到另一个核心函数的调用,因为它们是直接链接的,不会经过变量。我们考虑了很长时间的一件事是构建一个不直接链接的开发版本的核心,这样就可能开启instrumenting 或其他有帮助的开发时工具。

0

评论者:seancorfield

感谢您的回答 @alexmiller -- 我们已将开发环境设置为非直接链接,但QA / 生产环境设置为直接链接,因此我担心的是开发环境中{{(s/instrumental-all)}}可能出现的潜在问题,并想确保“代码不会出错”。如果instrumenting不会对现有(直接链接)的核心内的调用产生影响,那就足够好了。我担心原始提示和协议(以及其他可能冒出来的东西),因为你不希望被迫阅读你包含的每个库的源代码,只是为了看看{{(s/instrument-all)}}是否安全,是否会在你开发时以奇怪的方式咬你。

0

评论者:borkdude

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

既然这个错误似乎会一直存在,也许这就应该是Kondo应该注意到的问题吗?
0
参考: https://clojure.atlassian.net/browse/CLJ-1941 (由alex+import报告)
...