扩展宏导致 KeyError

Expanding macro results in KeyError

使用以下(简化)代码:

(setv agnostic-manager-installers {})

(defmacro alias-assign [am &rest aliases]
    (for [alias aliases] (assoc
        agnostic-manager-installers
        (str alias)
        (-> (globals) (get (str am)) (get "install")))))

(setv brew {
    "prefix" "/home/linuxbrew/.linuxbrew/"
    "install" (defn brew [] (run
                "curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | sudo bash"
                :shell True))
})

(alias-assign brew brew homebrew home-brew linuxbrew linux-brew)

我收到以下错误:

Traceback (most recent call last):
  File "/home/shadowrylander/.local/syvl/python/hy/bin/hy", line 8, in <module>
    sys.exit(hy_main())
  File "/usr/lib/python3.9/contextlib.py", line 137, in __exit__
    self.gen.throw(typ, value, traceback)
  File "<stdin>", line 9, in alias_assign
hy.errors.HyMacroExpansionError: 
  File "<stdin>", line 20
    (alias-assign brew brew homebrew home-brew linuxbrew linux-brew)
    ^--------------------------------------------------------------^
expanding macro alias-assign
  KeyError: 'brew'

我认为宏不应该在编译时评估参数,如果我正确地读取错误(我不认为我是)?基本上,我想 在提供给 alias-assign 的每个别名周围写双引号,这就是我使用宏的原因。

下面是一些产生相同错误的更简单的代码:

(setv brew 1)

(defmacro m []
  (get (globals) "brew"))

(m)

也许问题现在更明显了:在宏扩展期间尝试访问全局变量 brew 不起作用,因为 brew 在编译时不存在,当宏-扩张发生。如果您只是说 brew 而不是 (get (globals) "brew"),则会出现同样的问题,使用 NameError 而不是 KeyError。在任何情况下,形式 (setv brew 1) 直到 运行 时间才会被评估。解决这个问题的一种方法是改为说 (eval-when-compile (setv brew 1))。这使得评估发生得更早。

一个更广泛的问题是,作为宏扩展的结果,您似乎正在执行您实际上想要 return 的代码。毕竟,您的宏的主体是 for,因此它总是 return None。对比以下代码,它使用引号和取消引号来生成表单并 return 它们(并使用更新的语法):

(setv agnostic-manager-installers {})

(defmacro alias-assign [am #* aliases]
  `(do ~@(gfor
    alias aliases
    `(setv (get agnostic-manager-installers ~(str alias))
      (get ~am "install")))))

(setv brew (dict
  :prefix "/home/linuxbrew/.linuxbrew/"
  :install "placeholder"))

(alias-assign brew brew homebrew home-brew linuxbrew linux-brew)

(print (hy.repr agnostic-manager-installers))

结果是:

{"brew" "placeholder"  "homebrew" "placeholder"  "home-brew" "placeholder"  "linuxbrew" "placeholder"  "linux-brew" "placeholder"}