引入含`raise'函数的Hy宏时,`require'阶段触发`raise'执行异常
解决Hy宏中
raise偶发触发的问题 我来帮你拆解这个偶发异常的问题,核心根源其实是宏的参数声明语法出错了,导致宏展开后的代码完全不符合你的预期。
问题到底出在哪?
你写的宏定义是这样的:
(defmacro assert [a [b True] [message None]] `(unless (= ~a ~b) (raise (AssertionError (or ~message ~a)))))
这里的参数列表[a [b True] [message None]]不是设置默认参数的正确写法——在Hy里,这种写法属于参数解构,意思是:
- 这个宏需要接收3个参数:第一个是
a,第二个是能拆成两个元素的列表(分别绑定给变量b和True),第三个是能拆成两个元素的列表(分别绑定给变量message和None)
当你调用(assert True)时,只传了第一个参数,后两个参数默认是nil。Hy尝试把nil解构为[b True]时,会出现非预期的绑定(比如b被赋值为nil),这时候宏展开后的代码会变成:
(unless (= True nil) (raise (AssertionError (or None True))))
显然(= True nil)是False,unless条件成立,自然就触发了raise,抛出AssertionError: True。
至于你说的“偶发现象”,大概率是Hy解析器处理错误解构时的不稳定行为(比如偶尔缓存了错误的宏展开结果,或者解析时的临时容错逻辑),但核心问题还是参数声明的语法错误。
正确的宏写法
在Hy中,给宏(或函数)设置可选默认参数,必须用&optional关键字来声明。修正后的宏定义应该是:
(defmacro assert [a &optional [b True] [message None]] `(unless (= ~a ~b) (raise (AssertionError (or ~message ~a)))))
&optional会告诉Hy:后面的[b True]和[message None]是可选参数,调用时没传对应值的话,就用后面的字面量当默认值。
验证修正后的代码
按照你的步骤重新测试:
- 把修正后的宏写入
a.hy:echo '(defmacro assert [a &optional [b True] [message None]] `(unless (= ~a ~b) (raise (AssertionError (or ~message ~a)))))' > a.hy - 保持
b.hy内容不变:echo '(require a [assert])' > b.hy echo '(assert True)' >> b.hy - 执行
hy b.hy,此时宏展开后的代码是:
因为(unless (= True True) (raise (AssertionError (or None True))))(= True True)是True,unless条件不成立,不会执行raise,也就不会再抛出异常了。
内容的提问来源于stack exchange,提问作者syvlorg
相关产品推荐
相关产品推荐

