请分享您的想法至 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"}}}

交互式环境

$ 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

被选中
 
最佳答案

您遇到的错误并不是因为 bug,而是由于线程宏以这种方式展开您的表达式。调试此问题的方法之一是在交互式环境(REPL)中使用 macroexpand-1 来查看生成的代码。

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

thread-first 宏会将第一个项目推入下一个表达式的第一个位置,并重复此操作,直到达到表达式的末尾。在这种情况下,表达式展开为一个无效的 fn 形式。

这也解释了为什么您的工作区方法有效。通过在匿名函数外面额外添加一对括号,您让表达式先调用该函数,然后再将参数线程到调用中。

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

根据我的经验,您不太可能遇到这个问题,因为在多线程时,通常通过名称引用其他函数,而 `->` 宏会显式处理这种特殊情况,如果需要,将其转换成列表形式。

user=> (macroexpand-1 '(-> :hmm my-fn))
(my-fn :hmm)
感谢详细回答!我现在完全明白了。我忘了它是一个宏,并且它将所有内容作为数据进行完全字面处理。但是,就像你说的,它有一个特殊情况,即裸符号。我对于线程宏“应该”如何工作的感觉是,要以与符号相同的方式处理 (fn [x] ,,,) 形式,先将其包装在列表中...
0

线程宏是句法操作 - 它不会评估 fn 并调用它。

宏展开的代码看起来像

(fn :hmm [x] x)

正如规范告诉你的,这是无效的。所以,所有这些都按预期工作。

这完全说得通。这显然不是错误。但按照直觉来做我刚才做的事情感觉“正确”,我认为我的用法与传递不带括号的已符号化 fn 相等效。不是字面上,而是在意图层面。为 (fn [x] ,,,) 形式添加一个特殊情况,将其包装在列表中并列出一个参数是否可行?
by
编辑 by
好吧,@alexmiller? 这样做如何? 将这个更改集成到核心有什么机会? ;)

从 c18ca3c055b8267fed84e40cb3dcd9513c263599 星期日 9月17 00:00:00 2001
Email: 从: Thomas Spellman <[email protected]>
Date: 2020年1月12日 周日 11:07:42 -0800
Subject: [补丁] 线程lambda

---
 src/clj/clojure/core.clj             | 增加14行,减少7行
 test/clojure/test_clojure/macros.clj | 增加12行,减少0行
 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 ->
   "通过表达式穿过表单元。在第一个表单元中将x插入为第二个元素,如果x是一个lambda或不是一个列表,就将它转换成一个列表。如果有更多的表单元,将第一个表单元作为第二个元素插入到第二个表单元中,以此类推。"
-  
+  
   [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))
+                       (if (#{'fn 'fn*} (first form))
+                         (list form x)
+                         (with-meta `(~(first form) ~x ~@(next form)) (meta form)))
+                       (list form x))]
 
   
   
 
 
 
   "通过表达式穿过表单元。在第一个表单元中将x插入为第二个元素,如果x是一个lambda或不是一个列表,就将它转换成一个列表。如果有更多的表单元,将第一个表单元作为第二个元素插入到第二个表单元中,以此类推。"
-  
+  
   [x & forms]
   
-  
     (if forms
       (let [form (first forms)
             threaded (if (seq? form)
-                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
+                       (if (#{'fn 'fn*} (first form))
-  
-  
+                         (with-meta `(~(first form) ~x ~@(next form)) (meta form)))
+                       (list form x))]
+  
+  
   
 
 
 
 
 
   如果提供的选项映射包含不在有效列表中的键,则抛出异常,否则返回 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

and tests pass!

test
[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] ------------------------------------------------------------------------
...