请在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 "No matching clause: " 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](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) 在 user/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) 在 user/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 "No matching clause: " 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*` 特殊形式的合约要求映射必须排序,如果确实存在错误,它应该在上游代码中,即负责遍历和转换宏展开形式并将排序列表转换为常规映射的代码中。

这几次让我有些措手不及,我的建议(如附带的补丁实现)是在分析时间简单地添加一个检查,如果映射未排序则抛出异常。
0
参考资料:https://clojure.atlassian.net/browse/CLJ-2511(由 vitoshka 报告)
...