问题描述
是否可以从特征中添加 CONTROL
移相器?
按照example from the docs,在运行时代码中添加自定义控件异常很简单:
class CX::Oops does X::Control {};
sub f { CONTROL { when CX::Oops { note 'Oops!'; .resume}}
CX::Oops.new.throw; }
f; # OUTPUT: «Oops»
但是,我尝试从特征中这样做没有奏效:
sub trait_mod:<is>(Sub $fn,:$oopsable) {
$fn.add_phaser: 'CONTROL',{ when CX::Oops { note 'Oops!'; .resume} }}
sub g is oopsable { CX::Oops.new.throw; }
g; # OUTPUT: «control exception without handler»
从 .has_phasers
和 fire_phasers
(有趣的名字!)方法,我可以看出这是添加控制移相器。我需要做些什么来将其注册为处理程序,还是我缺少其他东西?
解决方法
CATCH
和 CONTROL
是否真的是移相器是一个有趣的问题。它们适合使用大写字母并对特定时间发生的事情做出反应。但是,它们也在语法的不同部分进行解析,作为语句控制,因此仅限于在语句级别发生。并且编译器也不通过调用 add_phasers
来处理它们。异常处理程序隐含了一些代码生成,而正是例程主体中生成的代码实际导致异常被处理。
可以合理地询问编译器是否不应该查看特征是否确实使用 add_phasers
或 CATCH
调用了 CONTROL
,然后相应地生成代码。但是,由于在当前的编译器实现中,处理程序是例程主体的一部分,并且所有工作(优化除外)都在调用 trait 处理程序之前在例程主体上完成,所以现在对 trait 产生影响为时已晚。>
另外,CATCH
和 CONTROL
块的主体不是简单地编译为普通块,而是生成代码来处理 when
智能匹配,并重新抛出异常如果没有一个处理程序匹配;至少这最后一步需要手动完成。如果我没记错的话,还有一些关于更新 $!
的内容。
一个好消息是即将推出的基于 rakuast
的编译器前端将:
- 为特征处理程序中的例程的 AST 提供 API。然后可以使用它在 AST 级别添加
CATCH
/CONTROL
处理程序,从而获得所有正确的语义并执行与直接写入代码的语义相同的语义。 - 将代码生成推迟到更晚的时间,以便时间也安排妥当。