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解除引用转换成IFn,调用,但在其他情况下(原始函数,直接链接,其他?)则不会工作

0 投票

评论者:kennyjwilli

嗯。至少应该记录下来。因此,在带有类型提示参数的函数上不能使用spec?

0 投票

评论者:seancorfield

Spec不能用于具有原始类型提示参数或返回值的函数 -- 原始类型提示似乎没有问题。

但是在这里,仅仅提供文档是不够的:在对命名空间进行检测后再发现它破坏了一个函数(恰好带有原始类型提示)是不可接受的。如果检测无法工作,则应跳过该函数(并发出警告,希望如此)。

0 投票

评论者:hiredman

是的,我正在提供问题的根本原因,而不是为问题辩护。

了解根本原因可以预测其他可能存在问题的位置: wherever some non-default function linking strategy is used。

一个这样的地方就是直接链接的函数,但我怀疑对于直接链接的函数,Clojure.Core可能会建议你仅针对测试进行代码检测,以及在生产中仅启用直接链接。

还有一个案例,我们对它尚未听到更多的消息,那就是协议函数。

0 投票

评论者:seancorfield

您对直接链接的评论让我对为{{clojure.core}}函数提出规范和检测的有效性产生了疑问。示例显示了{{clojure.core/symbol}},但自1.8.0以来,Clojure的核心库是以直接链接的形式交付的,对吗?

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

Sean的调试核心函数将对从您代码调用核心(可能不是直接链接)的功能起作用,但不会影响一个核心函数对另一个核心函数的调用,因为它们是直接链接的,并且不会经过变量。我们考虑了好久的一件事是构建一个非直接链接的开发版本的核心,这样就可以打开调试或其他有益的开发时工具。

0 投票

评论者:seancorfield

感谢您的回答 @alexmiller —— 我们将开发设置为非直接链接,将质量保证/生产设置为直接链接,所以我只担心 {{(s/instrumental-all)}} 在开发中可能出现的问题,并想确保“代码不会中断”。如果调试不会影响核心中现有的(直接链接)调用,那就足够好了。我关注原始的指示和协议(以及可能出现的任何问题),因为您不希望被迫阅读每个库的源代码,只是想知道 {{(s/instrument-all)}} 是否安全,或者它是否会在开发时期以奇怪的方式损害您。

0 投票

评论者:borkdude

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

鉴于这个错误似乎会一直存在,也许Kondo应该知道这一点?
0 投票
参考:https://clojure.atlassian.net/browse/CLJ-1941(由 alex+import 报告)
...