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

欢迎!请参阅关于页面,了解更多关于其工作原理的信息。

0
Clojure
以下宏展开的结果无法被评估,抛出`java.lang.NegativeArraySizeException`


(macroexpand-1 '(case :a
                  (:a-36 :b-36) 36
                  (:a-37 :b-37) 37
                  (:a-38 :b-38) 38
                  (:a-39 :b-39) 39
                  (:a-40 :b-40) 40
                  (:a-42 :b-42) 42))

;; =>

(clojure.core/let
    [G__8112 :a]
  (case*
    G__8112
    0
    31
    (throw
      (java.lang.IllegalArgumentException.
        (clojure.core/str "无匹配分支: " G__8112)))
    {3 [:b-36 36],
     4 [:b-37 37],
     6 [:b-40 40],
     13 [:b-39 39],
     14 [:b-38 38],
     18 [:a-42 42],
     20 [:a-40 40],
     21 [:a-38 38],
     22 [:a-37 37],
     24 [:a-39 39],
     27 [:b-42 42],
     28 [:a-36 36]}
    :compact
    :hash-identity
    nil))



如果您在上面的case中移除任何选项,则宏展开可以正确评估。

原因在于`case*`无法处理REPL中用`{...}`生产的`clojure.lang.PersistentArrayMap`。原始类型是`clojure.lang.PersistentTreeMap`

结果是,这些宏展开的列表在代码中不能自由操作,除非显式检查映射的具体类型。特别是,`clojure.walk/walk`会产生无法评估的结果。

原始问题:https://github.com/clojure-emacs/cider/issues/2335

5 答案

0
评论者:jafingerhut

我在Ubuntu 16.04 Linux系统上尝试过,运行OpenJDK 11.0.1和Clojure 1.10.0,在一个Leiningen REPL环境中,以及使用`clj`工具,并使用以下命令行选项来运行Clojure 1.10.0:

{{
clj -Sdeps "{:deps {org.clojure/clojure {:mvn/version \"1.10.0\"}}}"
}}
当我尝试求值这个表达式时

{{
(eval (macroexpand-1 '(case :a
                  (:a-36 :b-36) 36
                  (:a-37 :b-37) 37
                  (:a-38 :b-38) 38
                  (:a-39 :b-39) 39
                  (:a-40 :b-40) 40
                  (:a-42 :b-42) 42)))
}}
我没有得到java.lang.NegativeArraySizeException异常。相反,我得到了与尝试评估没有macroexpand-1和eval调用的(case :a ...)表达式相同的异常,这是一个IllegalArgumentException异常,因为没有匹配的子句::a,这在预期之中。

你能详细说明一下,是什么操作导致了java.lang.NegativeArraySizeException异常被抛出吗?
0

评论者:jafingerhut

我希望这个链接到原始的Cider问题的页面对您来说比上面的链接更容易理解,上面的链接看起来似乎是与其他URL拼接在一起的:https://github.com/clojure-emacs/cider/issues/2335

我通常不使用Cider,所以我对尝试在该处重现问题不太感兴趣,但如果该问题只能在该Cider中重现,那么这可能意味着问题可能就出在这里。

0

评论者:admin

我无法重现这个问题。在一个Clojure 1.10.0 repl中:

user=> (case :a (:a-36 :b-36) 36 (:a-37 :b-37) 37 (:a-38 :b-38) 38 (:a-39 :b-39) 39 (:a-40 :b-40) 40 (:a-42 :b-42) 42) 执行错误 (IllegalArgumentException) 在用户/eval145 (REPL:1) 中。没有匹配子句::a (执行 (macroexpand-1 '(case :a (:a-36 :b-36) 36 (:a-37 :b-37) 37 (:a-38 :b-38) 38 (:a-39 :b-39) 39 (:a-40 :b-40) 40 (:a-42 :b-42) 42))) 执行错误 (IllegalArgumentException) 在用户/eval150 (REPL:1) 中。没有匹配子句::a

我遗漏了什么?

0
评论者:bronsa_

触发这种方法的一种方式是从手动复制的宏展开代码。这将迫使字面量按普通映射而不是排序映射的方式来读取。


user=> (clojure.core/let
    [G__8112 :a]
  (case*
    G__8112
    0
    31
    (throw
      (java.lang.IllegalArgumentException.
        (clojure.core/str "无匹配分支: " G__8112)))
    {3 [:b-36 36],
     4 [:b-37 37],
     6 [:b-40 40],
     13 [:b-39 39],
     14 [:b-38 38],
     18 [:a-42 42],
     20 [:a-40 40],
     21 [:a-38 38],
     22 [:a-37 37],
     24 [:a-39 39],
     27 [:b-42 42],
     28 [:a-36 36]}
    :compact
    :hash-identity
    nil))
语法错误(NegativeArraySizeException),在编译 fn* 时出现,位置在 REPL:1:1。
null


我不会把这看作一个错误:`case*` 特殊形式的契约要求映射(map)是有序的。如果确实存在错误,它在上游,在任何负责遍历和转换宏展开形式,并将排序映射转换为常规映射的代码中。

这曾几次让我始料未及,我提出的建议(如附带的补丁所示)是在分析时简单地添加一个检查,如果不排序则抛出错误。
0
参考:https://clojure.atlassian.net/browse/CLJ-2511(由 vitoshka 报告)
...