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*`在`clojure.lang.PersistentArrayMap`上会被抑制,而`{...}`在REPL中产生的正是这种类型。原始类型是`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。相反,我得到了与尝试评估(case :a ...)表达式时相同的异常,没有macroexpand-1和eval调用,这将是IllegalArgumentException,因为没有匹配的条款: :a,这是预期的。

你能更详细地说明你做了什么导致抛出java.lang.NegativeArraySizeException异常吗?
0 投票

jafingerhut发表的评论

我希望这个指向原始Cider问题的链接比上面的链接更容易理解,因为它看起来被另一个URL连接到了一起:https://github.com/clojure-emacs/cider/issues/2335

我通常不使用Cider,所以不是非常感兴趣尝试在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 投票
by
参考:https://clojure.atlassian.net/browse/CLJ-2511(由 vitoshka 报告)
...