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
_Comment made by: 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,因为它期望在此处的"No matching clause: :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 (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)))。执行错误(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


我不会将此视为一个bug:case*特殊形式的合约要求映射必须是排序的,如果有任何bug,它可能是上游的,在负责遍历和转换宏展开形式并将排序映射转换为常规映射的任何代码中。

这以前几次让我感到意外,我的建议(在附加的补丁中 实现)是在分析时简单地添加一个检查,如果映射没有排序则抛出异常。
0
参考: https://clojure.atlassian.net/browse/CLJ-2511 (由 vitoshka 报告)
...