2024 Clojure 状态调查! 中分享您的想法。

欢迎!请参见 关于 页面,了解更多关于如何使用本网站的信息。

0
Spec
编辑
$ clj
Clojure 1.10.1
user=> (-> :hmm (fn [x] x))
Syntax error macroexpanding clojure.core/fn at (REPL:1:1).
:hmm - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
:hmm - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body


$ clj --main cljs.main --repl
cljs.user=>  (-> :hmm (fn [x] x))
Unexpected error (IllegalArgumentException) macroexpanding cljs.core/fn at (<cljs repl>:1:11).
Parameter declaration :hmm should be a vector

一种解决方案是将 lambda 嵌套在其他括号中,例如 ((fn [x] x))

我尝试使用 git 中的最新版 spec.alpha 运行 clojure,但它没有启动。所以我不确定如何尝试调试这个问题。

deps.edn

{:deps {org.clojure/clojure       {:mvn/version "1.10.1"
                                   :exclusions  [org.clojure/spec.alpha]}
        org.clojure/clojurescript {:git/url "https://github.com/clojure/clojurescript"
                                   :sha     "dcc8e61c79bfc701fe9e1414fe5db93edf6f1853"}
        org.clojure/spec.alpha    {:git/url "https://github.com/clojure/spec.alpha"
                                   :sha     "cd4aeb7edccbf7a881dcdc3c947313359508db3a"}}}

REPL

$ clj
Exception in thread "main" java.lang.ExceptionInInitializerError
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:398)
	at clojure.lang.RT.classForName(RT.java:2211)
	at clojure.lang.RT.classForName(RT.java:2220)
	at clojure.lang.RT.loadClassForName(RT.java:2239)
	at clojure.lang.RT.load(RT.java:449)
	at clojure.lang.RT.load(RT.java:424)
	at clojure.core$load$fn__6839.invoke(core.clj:6126)
	at clojure.core$load.invokeStatic(core.clj:6125)
	at clojure.core$load.doInvoke(core.clj:6109)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5908)
	at clojure.core$load_one.invoke(core.clj:5903)
	at clojure.core$load_lib$fn__6780.invoke(core.clj:5948)
	at clojure.core$load_lib.invokeStatic(core.clj:5947)
	at clojure.core$load_lib.doInvoke(core.clj:5928)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$load_libs.invokeStatic(core.clj:5985)
	at clojure.core$load_libs.doInvoke(core.clj:5969)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$require.invokeStatic(core.clj:6007)
	at clojure.core.server$loading__6721__auto____8842.invoke(server.clj:9)
	at clojure.core.server__init.load(Unknown Source)
	at clojure.core.server__init.<clinit>(Unknown Source)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:398)
	at clojure.lang.RT.classForName(RT.java:2211)
	at clojure.lang.RT.classForName(RT.java:2220)
	at clojure.lang.RT.loadClassForName(RT.java:2239)
	at clojure.lang.RT.load(RT.java:449)
	at clojure.lang.RT.load(RT.java:424)
	at clojure.core$load$fn__6839.invoke(core.clj:6126)
	at clojure.core$load.invokeStatic(core.clj:6125)
	at clojure.core$load.doInvoke(core.clj:6109)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5908)
	at clojure.core$load_one.invoke(core.clj:5903)
	at clojure.core$load_lib$fn__6780.invoke(core.clj:5948)
	at clojure.core$load_lib.invokeStatic(core.clj:5947)
	at clojure.core$load_lib.doInvoke(core.clj:5928)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$load_libs.invokeStatic(core.clj:5985)
	at clojure.core$load_libs.doInvoke(core.clj:5969)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$require.invokeStatic(core.clj:6007)
	at clojure.core$require.doInvoke(core.clj:6007)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.lang.Var.invoke(Var.java:384)
	at clojure.lang.RT.doInit(RT.java:491)
	at clojure.lang.RT.init(RT.java:467)
	at clojure.main.main(main.java:38)
Caused by: Syntax error macroexpanding clojure.core/defn at (clojure/spec/alpha.clj:85:1).
	at clojure.lang.Compiler.checkSpecs(Compiler.java:6972)
	at clojure.lang.Compiler.macroexpand1(Compiler.java:6988)
	at clojure.lang.Compiler.macroexpand(Compiler.java:7075)
	at clojure.lang.Compiler.eval(Compiler.java:7161)
	at clojure.lang.Compiler.load(Compiler.java:7636)
	at clojure.lang.RT.loadResourceScript(RT.java:381)
	at clojure.lang.RT.loadResourceScript(RT.java:372)
	at clojure.lang.RT.load(RT.java:459)
	at clojure.lang.RT.load(RT.java:424)
	at clojure.core$load$fn__6839.invoke(core.clj:6126)
	at clojure.core$load.invokeStatic(core.clj:6125)
	at clojure.core$load.doInvoke(core.clj:6109)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5908)
	at clojure.core$load_one.invoke(core.clj:5903)
	at clojure.core$load_lib$fn__6780.invoke(core.clj:5948)
	at clojure.core$load_lib.invokeStatic(core.clj:5947)
	at clojure.core$load_lib.doInvoke(core.clj:5928)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$load_libs.invokeStatic(core.clj:5985)
	at clojure.core$load_libs.doInvoke(core.clj:5969)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$require.invokeStatic(core.clj:6007)
	at clojure.main$loading__6721__auto____8974.invoke(main.clj:11)
	at clojure.main__init.load(Unknown Source)
	at clojure.main__init.<clinit>(Unknown Source)
	... 55 more
Caused by: java.lang.Exception: #object[clojure.spec.alpha$and_spec_impl$reify__1049 0x1292071f "clojure.spec.alpha$and_spec_impl$reify__1049@1292071f"] is not a fn, expected predicate fn
	at clojure.spec.alpha$dt.invokeStatic(alpha.clj:769)
	at clojure.spec.alpha$dt.invoke(alpha.clj:759)
	at clojure.spec.alpha$dt.invokeStatic(alpha.clj:760)
	at clojure.spec.alpha$dt.invoke(alpha.clj:759)
	at clojure.spec.alpha$deriv.invokeStatic(alpha.clj:1534)
	at clojure.spec.alpha$deriv.invoke(alpha.clj:1528)
	at clojure.spec.alpha$deriv.invokeStatic(alpha.clj:1542)
	at clojure.spec.alpha$deriv.invoke(alpha.clj:1528)
	at clojure.spec.alpha$deriv$fn__1291.invoke(alpha.clj:1544)
	at clojure.core$map$fn__5866.invoke(core.clj:2755)
	at clojure.lang.LazySeq.sval(LazySeq.java:42)
	at clojure.lang.LazySeq.seq(LazySeq.java:51)
	at clojure.lang.RT.seq(RT.java:535)
	at clojure.core$seq__5402.invokeStatic(core.clj:137)
	at clojure.core$map$fn__5873.invoke(core.clj:2763)
	at clojure.lang.LazySeq.sval(LazySeq.java:42)
	at clojure.lang.LazySeq.seq(LazySeq.java:51)
	at clojure.lang.RT.seq(RT.java:535)
	at clojure.core$seq__5402.invokeStatic(core.clj:137)
	at clojure.core$filter$fn__5893.invoke(core.clj:2809)
	at clojure.lang.LazySeq.sval(LazySeq.java:42)
	at clojure.lang.LazySeq.seq(LazySeq.java:51)
	at clojure.lang.RT.seq(RT.java:535)
	at clojure.core$seq__5402.invokeStatic(core.clj:137)
	at clojure.core$map$fn__5866.invoke(core.clj:2746)
	at clojure.lang.LazySeq.sval(LazySeq.java:42)
	at clojure.lang.LazySeq.seq(LazySeq.java:51)
	at clojure.lang.RT.seq(RT.java:535)
	at clojure.core$seq__5402.invokeStatic(core.clj:137)
	at clojure.core$seq__5402.invoke(core.clj:137)
	at clojure.spec.alpha$filter_alt.invokeStatic(alpha.clj:1431)
	at clojure.spec.alpha$filter_alt.invoke(alpha.clj:1425)
	at clojure.spec.alpha$alt_STAR_.invokeStatic(alpha.clj:1435)
	at clojure.spec.alpha$alt_STAR_.invoke(alpha.clj:1434)
	at clojure.spec.alpha$deriv.invokeStatic(alpha.clj:1544)
	at clojure.spec.alpha$deriv.invoke(alpha.clj:1528)
	at clojure.spec.alpha$deriv.invokeStatic(alpha.clj:1542)
	at clojure.spec.alpha$deriv.invoke(alpha.clj:1528)
	at clojure.spec.alpha$deriv.invokeStatic(alpha.clj:1543)
	at clojure.spec.alpha$deriv.invoke(alpha.clj:1528)
	at clojure.spec.alpha$deriv.invokeStatic(alpha.clj:1543)
	at clojure.spec.alpha$deriv.invoke(alpha.clj:1528)
	at clojure.spec.alpha$re_conform.invokeStatic(alpha.clj:1669)
	at clojure.spec.alpha$re_conform.invoke(alpha.clj:1660)
	at clojure.spec.alpha$regex_spec_impl$reify__1375.conform_STAR_(alpha.clj:1710)
	at clojure.spec.alpha$conform.invokeStatic(alpha.clj:171)
	at clojure.spec.alpha$conform.invoke(alpha.clj:167)
	at clojure.spec.alpha$macroexpand_check.invokeStatic(alpha.clj:708)
	at clojure.spec.alpha$macroexpand_check.invoke(alpha.clj:704)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.lang.Compiler.checkSpecs(Compiler.java:6970)
	... 82 more

2 个答案

0

被选中
 
最佳答案

您遇到的错误不是因为一个错误,而是因为线程宏以这种方式展开您的表达式。调试此问题的一种方法是在 REPL 中使用 macroexpand-1 来查看生成的代码。

user=> (macroexpand-1 '(-> :hmm (fn [x] x)))
(fn :hmm [x] x)

thread-first 宏将获取第一个项并将其放入下一个表达式的第一个位置,并重复直到表达式的末尾。在这种情况下,表达式展开成一个无效的 fn 形式。(尝试使用 hmm 而不是 :hmm 来增加乐趣!)

这也解释了为什么你的补救措施能够工作。通过在匿名函数周围额外添加一对括号,你使得表达式首先调用函数,然后传递参数给调用。

user=> (macroexpand-1 '(-> :hmm ((fn [x] x))))
((fn [x] x) :hmm)

在我的经验中,你不太会遇到这个问题,因为你通常在传递时通过名称引用其他函数,并且 -> 宏明确地处理了这种情况,如果需要,会将其转换为列表形式。

user=> (macroexpand-1 '(-> :hmm my-fn))
(my-fn :hmm)
感谢你详细的解答!我完全明白了。我忘记这是一个宏,并且它将一切都当作纯数据来完全逐字处理。然而,正如你所说,它对裸符号有一个特殊情况。我对线程宏“应该”如何工作的直观感觉是,通过将其包裹在列表中,以与符号相同的方式处理 (fn [x] ,,,) 形式...
0

线程宏是语法的操纵 - 它们不会评估函数并调用它。

宏展开后的代码看起来像

(fn :hmm [x] x)

正如规范告诉你的,这是无效的。所以这一切都按设计工作。

这个完全说得通。这明显不是一个错误。但是,从直觉上讲,我确实认为我所做的是正确的,我觉得我的使用与不添加括号就传递已经命名的函数是等价的。不是字面上的,但在意图层面上。是否可以为 (fn [x] ,,,) 形式添加一个特殊情况,将其包裹在列表中并传入单个参数?
by
编辑了 by
关于这个,@alexmiller?把这个改动放入核心的可能性有多大? ;)

From c18ca3c055b8267fed84e40cb3dcd9513c263599 Mon Sep 17 00:00:00 2001
From: Thomas Spellman <[email protected]>
Date: Sun, 12 Jan 2020 11:07:42 -0800
Subject: [PATCH] 线程lambda表达式

---
 src/clj/clojure/core.clj             | 14 +++++++++-----
 test/clojure/test_clojure/macros.clj | 12 ++++++++++++
 2 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 8e98e072..27d22558 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -1671,43 +1671,47 @@
 
   但更容易编写、阅读和理解。
   {:added "1.0"}
   ([x form] `(. ~x ~form))
   ([x form & more] `(.. (. ~x ~form) ~@more)))
 
 (defmacro ->
   "通过表单对表达式进行线程处理。将x插入第一个表单的第二个位置,如果x不是一个列表,则将其转换为列表。如果还有更多表单,则将第一个表单插入第二个表单的第二个位置,依此类推。"
-  第二个元素在第一个表单中,如果它是一个lambda或者不是一个列表,则将其转换为列表。
+  第二个元素在第一个表单中,如果它是一个lambda或者不是一个列表,则将其转换为列表。
     [x & forms]
     (loop [x x, forms forms]
   {:added "1.0"}
       (if forms
            threaded (if (seq? form)
-                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
+                       (if (#{'fn 'fn*} (first form))
+                         (list form x)
+                         (with-meta `(~(first form) ~x ~@(next form)) (meta form)))
-                       (list form x))]
-                       
+                       
-                       
-                       
       x)))
 
 (defmacro ->>
   "通过表单对表达式进行线程处理。将x插入第一个表单的第二个位置,如果x不是一个列表,则将其转换为列表。如果还有更多表单,则将第一个表单插入第二个表单的第二个位置,依此类推。"
-  最后一个元素在第一个表单中,如果它是一个lambda或者不是一个列表,则将其转换为列表。
+  最后一个元素在第一个表单中,如果它是一个lambda或者不是一个列表,则将其转换为列表。
     [x & forms]
   {:added "1.1"}
-              (with-meta `(~(first form) ~@(next form)  ~x) (meta form))
       (if forms
            threaded (if (seq? form)
-                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
+                       (if (#{'fn 'fn*} (first form))
+                         (list form x)
-              (list form x))]
-              
-                       (list form x))]
-                       
-              
+
-                       
       x)))
 
(定义 map)
 
(定义私有函数 check-valid-options
如果给定的选项映射包含不在列表中的有效键,则抛出异常,否则返回 nil。

diff --git a/test/clojure/test_clojure/macros.clj b/test/clojure/test_clojure/macros.clj
index ce17bb38..5ebd2dd5 100644
--- a/test/clojure/test_clojure/macros.clj
+++ b/test/clojure/test_clojure/macros.clj
@@ -106,8 +106,20 @@
空格空格空格空格空格(is (nil? (loop []
空格空格空格空格空格空格空格空格空格空格空格(as-> 0 x
空格空格空格空格空格空格空格空格空格空格空格空格(when-not (zero? x)
空格空格空格空格空格空格空格空格空格空格空格空格空格(recur))))))
空格空格空格空格(is (nil? (loop [x nil] (some-> x recur))))
空格空格空格空格(is (nil? (loop [x nil] (some->> x recur))))
空格空格空格空格(is (= 0 (loop [x 0] (cond-> x false recur))))
空格空格空格空格(is (= 0 (loop [x 0] (cond->> x false recur))))
+
+
+(运行测试 ->lambda-test
空格空格(is (= 'a (-> 'a ((fn [x] x)))))
空格空格(is (= 'a (-> 'a (fn [x] x))))
空格空格(is (= 'a (-> 'a #(identity %))))
+
空格空格(is (= 'a (-> 'a (#(identity %))))))
+
+(运行测试 ->>lambda-test
空格空格(is (= 'a (->> 'a ((fn [x] x)))))
空格空格(is (= 'a (->> 'a (fn [x] x))))
空格空格(is (= 'a (->> 'a #(identity %))))
--
空格空格(is (= 'a (->> 'a (#(identity %))))))

文件末尾没有换行符

2.21.0 (Apple Git-122.2)
成功了!

(> :hmm (fn [x] x))

=> :hmm
并且测试通过!
测试
[INFO] 执行了任务
测试
[INFO] ------------------------------------------------------------------------
[INFO] 构建成功
测试
...