请在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?把这项更改放入核心的可能性有多大呢? (;)

从 c18ca3c055b8267fed84e40cb3dcd9513c263599 Mon Sep 17 00:00:00 2001
发件人:Thomas Spellman <[email protected]>
日期:Sun, 12 Jan 2020 11:07:42 -0800
主题:[补丁] 线程lambda

---
 src/clj/clojure/core.clj             | 14 +++++++++-----
 test/clojure/test_clojure/macros.clj | 12 ++++++++++++
 2个文件已更改,21个插入 (+),5个删除 (-)

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 ->
   "通过expr遍历forms。在第一个form中将x作为第二个元素插入,如果它不是列表,则将其转换为列表。
-  如果存在更多forms,则将第一个form作为第二个元素插入第二个form等。
+  如果存在更多forms,则将第一个form作为第二个元素插入第二个form等。
-                  [x & forms]
-                   loop [x x, forms forms]
   {:added "1.0"}
-                       (if forms
-                             let [form (first forms)
-                             threaded (if (seq? form)
-                                  (with-meta `(~(first form) ~x ~@(next form)) (meta form))
-                                     (list form x))
-                             )]
-                             (let [form (first 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)))
 
-                       )]
   "通过expr遍历forms。在第一个form中将x作为第二个元素插入,如果它不是列表,则将其转换为列表。
-                       )]
+                       (let [form (first forms)
-                  [x & forms]
+                       threaded (if (seq? form)
+                             (list form x)
-                       (if forms
-                             let [form (first forms)
-                             threaded (if (seq? form)
-                                  (with-meta `(~(first form) ~x ~@(next form)) (meta form))
-                                     (list form x))
+                             (with-meta `(~(first form) ~x ~@(next form)) (meta form)))
+                       )]
-                             (let [form (first forms)
-                             threaded (if (seq? form)
-                       (loop [threaded threaded, forms (next forms)]
+                             (recur threaded (next forms)))
-                                 (list form x)
-                                 (with-meta `(~(first form) ~x ~@(next form)) (meta form)))
 
 (def map)
 
 (defn ^:private 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)))))
+
+(deftest ->lambda-test
+  (is (= 'a (-> 'a ((fn [x] x)))))
+  (is (= 'a (-> 'a (fn [x] x))))
+  (is (= 'a (-> 'a #(identity %))))
+  (is (= 'a (-> 'a (#(identity %))))))
+
+(deftest ->>lambda-test
+  (is (= 'a (->> 'a ((fn [x] x)))))
+  (is (= 'a (->> 'a (fn [x] x))))
+  (is (= 'a (->> 'a #(identity %))))
+  (is (= 'a (->> 'a (#(identity %))))))
\ No newline at end of file
--
2.21.0 (Apple Git-122.2)

it works!

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

所有测试都通过了!

测试
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:18 min
[INFO] Finished at: 2020-01-12T10:58:31-08:00
[INFO] ------------------------------------------------------------------------
...