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 deref转换为IFn并调用,但在其他情况下(原始函数,直接链接,其他?)这不会有效

0

评论者:kennyjwilli

嗯。这至少应该是有文档的。所以,spec不能用于带有类型提示的函数?

0

评论者:seancorfield

Spec不能用于带有原始类型提示的参数或返回值的函数 - 非原始类型提示似乎是可行的。

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

0
by

评论者:hiredman

是的,我在说明问题的根本原因,而不是为问题辩解。

了解根本原因可以预测其他会出现问题的位置:无论在哪里使用一些非默认函数链接策略。

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

另一个案例,我们还没有听到太多的是协议函数。

0
by

评论者:seancorfield

你关于直接链接的评论让我想知道对 {{clojure.core}} 函数进行 spec 和 Instrument 的有效性。例如显示了 {{clojure.core/symbol}},但 Clojure 的核心库自 1.8.0 起作为直接链接提供,不是吗?

0
by

评论者:hiredman

修改调用约定的是编译这个函数时启用了直接链接,而调用该函数的人编译时启用了直接链接。

此代码将在关闭直接链接时抛出不符合规格的错误,并在打开直接链接时返回 symbol foo

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

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

(defn foo
[]
(symbol 'foo))

(s/instrument-all)

(foo)
`

0
by

评论者: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
by

评论者: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 报告)
...