请分享您的想法,参加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 用 instrumented 函数替换 var 值,这些函数与原始的函数接口不兼容

方法:考虑原始接口并使其正常工作,或者在文档中说明/instrumentation 不能与这些函数一起工作。

11 答案

0 点赞

评论者:hiredman

spec 用 instrumented 函数替换 var 值,这在默认链接情况下工作,var deref 转换为 ifn,调用,但在其他情况下(原始函数,直接链接,其他?)则不工作

0 点赞

评论者:kennyjwilli

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

0 点赞

评论者:seancorfield

不能在具有 原始类型提示 的函数或返回值上使用规范 — 非原始类型提示看起来可以。

但是这里的文档还不够充分:在某个命名空间上进行Instrumentation操作,然后发现它破坏了一个函数(碰巧使用了原始类型提示),这是不可接受的。如果仪器校验无法工作,应该跳过该函数(并产生一个警告,希望如此)。

0 点赞

评论者:hiredman

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

理解根本原因可以预测未来可能存在问题的其他地方:凡是使用非默认函数链接策略的地方。

这样的一个例子是直接链接的函数,但我怀疑对于直接链接的函数,Clojure/Core会建议你只为测试而instrument代码,并且在生产中只启用直接链接。

另一个案例,让我有点惊讶我们还没有更多关于它的讨论,是协议函数。

0 点赞

评论者:seancorfield

你关于直接链接的评论让我对spec'ing和instrumenting {{clojure.core}} 函数的有效性产生了怀疑。示例显示了 {{clojure.core/symbol}},但是自从Clojure的核心库自1.8.0以来是直接链接的,对吗?

0 点赞

评论者:hiredman

改变调用约定的是调用函数的编译启用了直接链接,而不是被编译启用了直接链接的函数本身。

此代码将对关闭直接链接时的无效符号spec投掷非规范错误,并在打开直接链接时返回符号foo

`
(要求 '[clojure.spec :作为 s])

(s/fdef 符号
:参数 字符串?
:返回 符号?)

(defn foo
[]
(符号 'foo))

(s/instrument-all)

(foo)
`

0 点赞

评论者:hiredman

此代码返回true,因为m是一个协议函数,如果您将其替换为普通函数,则会抛出非合规错误

`
(要求 '[clojure.spec :作为 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

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