请分享您的想法,参与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)

线程第一个宏将取第一个元素并将其推入下一个表达式的第一个位置,重复直至表达式的结尾。在这种情况下,表达式扩展为无效的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] ,,,) 形式上增加一个特例来将其包装在列表中并提供一个单独的参数,这样的建议合理吗?

修改了
你说的这个怎么样,@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 ->
   "通过表达式将线程通过表单。在第一个表单中插入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)
-                    without-meta `(~(first form) ~x ~@(next form))
                      (with-meta `(~(first form) ~x ~@(next form)) (meta form)))
                      (list form x))]
         (recur threaded (next forms)))
         x)
 (defmacro ->>
-  在第一个表单的最后插入x,如果它不是列表,则将其创建为列表。在其后的第一个元素中也是类似操作。
 
+  在第一个表单的最后插入x,如果它不是列表,则将其创建为列表。在其后的第一个元素中也是类似操作。
   "通过表达式将线程通过表单。在第一个表单中插入x作为第二个元素,如果它不是列表则创建列表。如果还有更多表单,则在第二个表单中插入第一个表单作为第二个元素,依此类推。"
-  {:added "1.1"}
-  ({(first form) ~@(next form) ~x} (meta form))
     [x & forms]
-  (list form x))]
 {:added "1.1"}
       (if forms
             threaded (if (seq? form)
                      (with-meta `(~(first form) ~x ~@(next form)) (meta form))
                      (if (#{'fn 'fn*} (first form))
+                         (list form x)
-          ({(first form) ~@(next form) ~x} (meta form))
-          (list form x))]
                      (with-meta `(~(first form) ~x ~@(next form)) (meta form)))
                      (list form x))]
+                         (与元数据 `( (~(first 表达式) ~@(下一表达式) ~x) (元数据 表达式)))
+                       (列表 表达式 x))]
 (defmacro ->>
-  在第一个表单的最后插入x,如果它不是列表,则将其创建为列表。在其后的第一个元素中也是类似操作。
 
 ;(定义 map)
 
 ;(定义私有函数 ^:private check-valid-options)
   “如果给定的选项映射包含不在有效键列表中的键,则抛出异常,否则返回 nil。”
   否则返回 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? (循环 []
                 (as-> 0 x
                   (when-not (zero? x)
                      (recur))))))
   (is (nil? (循环 [x nil] (some-> x recur))))
   (is (nil? (循环 [x nil] (some->> x recur))))
   (is (= 0 (循环 [x 0] (cond-> x false recur))))
   (is (= 0 (循环 [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] 构建成功
[INFO] ------------------------------------------------------------------------
[INFO] 总时间: 01:18 分钟
[INFO] 完成时间: 2020-01-12T10:58:31-08:00
[INFO] ------------------------------------------------------------------------
...