扩展宏导致 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"}
使用以下(简化)代码:
(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"}