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不能用于具有原始类型提示参数或返回值的函数 -- 非原始类型提示应该没问题。

但是在这里,文档是不够的:给一个命名空间进行仪表面板后发现它破坏了一个函数(恰好有一个原始类型提示),这是不可接受的。如果仪表面板不起作用,则应跳过该函数(并生成一个警告,如果可能的话)。

0

评论者:hiredman

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

理解根本原因可以预测其他可能出现问题的位置:无论何时使用非默认的函数链接策略。

一个这样的地方是直接链接的函数,但我怀疑对于直接链接的函数,Clojure/Core只是建议您只为测试代码进行仪表面板,您应该只为生产环境启用直接链接。

另一个案例,我有点惊讶我们还没有听到更多关于它的消息,那就是协议函数。

0

评论者:seancorfield

您关于直接链接的评论让我对为{{clojure.core}}函数进行规范化和仪表面板的可行性产生了疑问。示例显示了{{clojure.core/symbol}},但 Clojure 的核心库自 1.8.0 版本起就是直接链接的,不是吗?

0

评论者:hiredman

不会改变调用约定的是编译时启用直接链接的函数,而是调用该函数的编译时也启用了直接链接。

此代码会在直接链接关闭的情况下抛出一个非兼容的错误,并在直接链接开启的情况下返回symbo 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 对核心函数进行instrumenting(时序安排)不会影响您的代码调用核心函数(可能不存在直接连接),但不会影响一个核心函数调用另一个核心函数,因为它们是直接连接并且不通过var进行。我们长期以来一直在考虑构建一个不直接连接的核心dev版本,这样可以开启instrumentation或其它有帮助的开发工具。

0

评论者:seancorfield

感谢你的回答 @alexmiller -- 我们已将dev设置为非直接连接,而QA/生产设置为直接连接,所以我只关心dev中可能出现的问题,担心与{{(s/instrumental-all)}}有关,并希望确保“代码不会出错”。如果instrumentation不会影响核心中的现有(直接连接)调用,那么对我来说就足够好了。我担心原始类型提示和协议(以及这里冒出来的任何东西),因为你不希望被迫阅读你包含的每个库的源代码,只是为了看看{{(s/instrument-all)}}是否安全,或者它会在你开发的过程中以一种奇特的方式伤害到你。

0

评论者:borkdude

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

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