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

欢迎!请查阅关于页面了解更多关于如何操作的信息。

+17

如果您在两个命名空间中使用相同名称的letfn定义了一个fn,则将使用错误的fn。

`
(ns hello-world.one)

(letfn [(answer [] "1")]
(defn get-answer []

(answer)))

`

`
(ns hello-world.two)

(letfn [(answer [] "2")]
(defn get-answer []

(answer)))

`

`
(ns hello-world.core)
(:require [hello-world.one])

        [hello-world.two]))

(println "one =>" (hello-world.one/get-answer)) ; one => 1
(println "two =>" (hello-world.two/get-answer)) ; two => 1 WHAT?!?
`

这个问题似乎存在于顶层的letfn以及非顶层(let (link: ) (letfn (link: ...)))

共有10个回答

0

评论者:jeremyrsellars

此补丁将letfn的:expr和:statement形式包裹在函数声明中(以前,只有:expr形式被包裹)。

我只进行了最小程度的测试。这修复了描述中的代码。

注意:这是我第一次尝试编译器,所以我完全可能没有理解整个问题。

0

评论者:mfikes

已确认,此修补程序修复了在自托管的ClojureScript (Planck)中的问题

没有修补程序

cljs.user=> (require 'hello-world.core) one => 2 two => 2 nil

有修补程序

cljs.user=> (require 'hello-world.core) one => 1 two => 2 nil

0

评论者:dpsutton

这个问题不涉及不同的命名空间。错误在于 let-fn 将其绑定作为一个全局变量。

简单的复现方法是:
1. lein new mies letfn-bug,
2. 更新 cljs 版本至 (link: org.clojure/clojurescript "1.9.946")
3. 然后

`
(ns letfn-bug.core
(:require [clojure.browser.repl :as repl]))

(enable-console-print!)

(letfn [(non-unique-name [] 4)]
(defn f1 [] (non-unique-name)))

(letfn [(non-unique-name [] 5)]
(defn f2 [] (non-unique-name)))

(println "应该是4→ " (f1))
(println "应该是5→ " (f2))
`

接着进入 scripts/repl

结果为

`
cljs.user=> (load-file "letfn_bug/core.cljs")
应该是4→ 5
应该是5→ 5
nil
cljs.user=>

`

生成的 js 代码

// 由 ClojureScript 1.9.946 编译 {} goog.provide('letfn_bug.core'); goog.require('cljs.core'); goog.require('clojure.browser.repl'); cljs.core.enable_console_print_BANG_.call(null); var non_unique_name = (function letfn_bug$core$non_unique_name(){ return (4); }); letfn_bug.core.f1 = (function letfn_bug$core$f1(){ return non_unique_name.call(null); }); var non_unique_name = (function letfn_bug$core$non_unique_name(){ return (5); }); letfn_bug.core.f2 = (function letfn_bug$core$f2(){ return non_unique_name.call(null); }); cljs.core.println.call(null,"应该是4→ ",letfn_bug.core.f1.call(null)); cljs.core.println.call(null,"应该是5→ ",letfn_bug.core.f2.call(null));

0

评论者:dnolen

快速审查补丁,我们可能只需要在顶部级别进行包裹,因为那里实际上是问题所在,而不是总是包裹在语句上下文中。

0

评论者:gnl

我想提出,建议提高此问题的优先级。像这样的编译器破坏作用域的问题绝对不是小问题。

0

评论者:jeremyrsellars

这个补丁与第一个类似,但仅将声明 letfn 表达式包裹在 fn-scope 外部的函数中。(CLJS-1965-Wrap-top-level-letfn-to-avoid-collisions.patch)

0

评论由:timothypratley发表

我在本地确认新补丁运行良好...但我必须手动应用更改,因为“git apply”失败。如果Jeremy创建一个新的补丁,你觉得如何?告诉我是否可以在任何方式上帮助(如果这样可以帮助他保留信用,我很乐意更新他的补丁?)

0

评论者:dnolen

在顶级包装闭包可能会引入与Closure高级的不理想的交互 - 在继续进行建议的补丁之前,我想看看DCE等是否仍然适用于此模式。

另一个选择是跟踪此模式并将其分配给命名空间+阴影逻辑(即在单一命名空间中使用相同的名称多次)。

0
_评论由jeremyrsellars发表

以下是Wrap-top-level-letfn-to-avoid-collisions补丁当前rm1.10.520的最新更新。

我想不出如何使用deftest测试代码的淘汰,但我们希望高级编译输出包含“kwc”,但不包含“dce”。

{code:title=letfn_dce_test.cljs|borderStyle=solid}
(ns cljs.letfn-dce-test
  (:require [cljs.test :refer-macros [deftest is]]))

(letfn [(fn-to-kwc [] :kwc)
        (fn-to-dce [] :dce)]  ; 这个函数和关键字应该被DCE消除
  (defn f0 [] (count (str f (name (fn-to-kwc)))))
  (deftest letfn-live-code-test
    (is (pos? (f0)))))


我观察到它表现得像代码淘汰仍然起作用一样。


bin/cljsc src/test/cljs/cljs/letfn_dce_test.cljs  "{:optimizations :advanced}"|less


David Nolan的替代方案很有趣。是否还有更多原因要追求将letfns语句附加到命名空间而不是这样?
0
参考:https://clojure.atlassian.net/browse/CLJS-1965 (由jeremyrsellars报道)
...