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 替换变量值使用仪表化函数,这对默认链接情况有效,变量解引用转换为 ifn,调用,但在其他情况下(原始函数,直接链接,其他?)则不起作用

方法: 考虑原始接口并使其工作,或记录/失败已知 instrumentalization 不会与这些接口一起工作。

11 答案

0 投票

评论者:hiredman

spec 替换 var 值使用仪表化函数,默认情况下,这个操作是有效的,var 解引用转换成 ifn,调用,但在其他情况下(原始函数,直接链接,其他?)则不起作用

0 投票

评论者:kennyjwilli

嗯。至少应该有文档说明。那么,spec 不能用于有类型提示参数的函数吗?

0 投票

评论者:seancorfield

Spec 不能用于具有 原始类型 类型提示的参数或返回值的功能 —— 非原始类型提示看起来是可以的。

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

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

使用原生类型提示进行核心函数的代码生成将适用于从您的代码调用核心的情况(这些调用可能是间接链接的),但不会影响从核心中的一个函数调用另一个函数,因为这些调用是直接链接的,且没有通过变量进行。我们考虑了很久的一件事是为核心构建一个非直接链接的开发版本,这样就可以打开代码生成器或其他有用的开发时间工具。

0 投票

评论者:seancorfield

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

0 投票

评论由:borkdude

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

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