从元模型强制执行单个实例
Enforcing single instances from the metamodel
我知道从 class 级别确保单个实例更容易,并且可以处理出色的 Staticish
module from Jonathan Stowe that does the same by using roles, but I just want to try and understand a bit better how the class higher order working,主要用于 FOSDEM 演讲。我可以想到几种在元模型级别执行此操作的方法,但最终这就是我想出的:
my class MetamodelX::Singleton is Metamodel::ClassHOW {
my \instance = Mu;
method compose(Mu \type) {
my &callsame := CORE::<&callsame>; # Workaround for RT #127858
self.method_table(type)<new>.wrap: -> \SELF, | {
unless instance.defined { instance = SELF };
callsame();
};
}
}
my package EXPORTHOW {
package DECLARE {
constant singleton = MetamodelX::Singleton;
}
}
Mainly ripped from the OO::Monitors
code, written as far as I understand it, by JJ Atria and Jonathan Worthington)
主要理由是尝试包装构建子方法,或者以某种方式尝试创建对象的新实例。但是,这(与 BUILD
和 BUILDALL
相同,接近原始版本)失败了:
No such method 'wrap' for invocant of type 'Any'. Did you mean any of
these: 'Map', 'WHAT', 'grep', 'map'?
很明显我不明白这些是做什么的,或者就此而言,整个 HOW 概念。那么知道这里可能失败的原因是什么,或者以任何其他方式覆盖元模型级别的对象构建以能够按预期进行吗?
这种尝试存在一些误解。
- 每种类型有一个元class实例。因此,如果我们想让给定的类型只被实例化一次,正确的范围是元class中的一个属性,而不是
my
。 my
意味着无论我们创建哪种类型,都有一个全局对象。
compose
方法,当 subclassing ClassHOW
时,应该总是回调到基础 compose
方法(可以使用 callsame
).否则,class不会组成。
- 此 exact 类型的
method_table
方法 returns table 方法。但是,大多数 classes 不会有 new
方法。相反,它们将继承默认值。但是,如果我们将 that 包装起来,我们将产生非常全局的影响。
虽然 new
相对常见地覆盖以将接口更改为构造,但 bless
方法 - new
在完成任何映射工作后调用 - 不是我们想要的期望语言用户是压倒一切的。因此,我们可以继续的一种方法是尝试安装一个执行所需逻辑的 bless
方法。 (我们也可以使用 new
,但实际上我们需要检查此 class 中是否有一个,如果有则将其包装,然后添加默认副本的副本,如果不是,这需要更多的努力。)
这是一个有效的解决方案:
my class MetamodelX::Singleton is Metamodel::ClassHOW {
has $!instance;
method compose(Mu \type) {
self.add_method(type, 'bless', -> \SELF, |c {
without $!instance {
$!instance := SELF.Mu::bless(|c);
}
$!instance
});
callsame();
}
}
my package EXPORTHOW {
package DECLARE {
constant singleton = MetamodelX::Singleton;
}
}
请注意,我们不能在为 bless
添加的代码中使用 callsame
,因为它实际上不是 method
。我们可以改为使用 anon method
来编写它,但是我们遇到了问题,即该方法有自己的 self
想法,因此我们最终不得不保存元 class self
并安排一些其他方式访问 $!instance
.
最后,一个实际的例子:
use Singleton;
singleton Counter {
has $.x;
method inc() { $!x++ }
}
my $c1 = Counter.new;
$c1.inc;
my $c2 = Counter.new; # Gets same instance as in $c1
$c1.inc;
$c2.inc;
say $c1.x; # 3
say $c2.x; # 3
我知道从 class 级别确保单个实例更容易,并且可以处理出色的 Staticish
module from Jonathan Stowe that does the same by using roles, but I just want to try and understand a bit better how the class higher order working,主要用于 FOSDEM 演讲。我可以想到几种在元模型级别执行此操作的方法,但最终这就是我想出的:
my class MetamodelX::Singleton is Metamodel::ClassHOW {
my \instance = Mu;
method compose(Mu \type) {
my &callsame := CORE::<&callsame>; # Workaround for RT #127858
self.method_table(type)<new>.wrap: -> \SELF, | {
unless instance.defined { instance = SELF };
callsame();
};
}
}
my package EXPORTHOW {
package DECLARE {
constant singleton = MetamodelX::Singleton;
}
}
Mainly ripped from the
OO::Monitors
code, written as far as I understand it, by JJ Atria and Jonathan Worthington)
主要理由是尝试包装构建子方法,或者以某种方式尝试创建对象的新实例。但是,这(与 BUILD
和 BUILDALL
相同,接近原始版本)失败了:
No such method 'wrap' for invocant of type 'Any'. Did you mean any of
these: 'Map', 'WHAT', 'grep', 'map'?
很明显我不明白这些是做什么的,或者就此而言,整个 HOW 概念。那么知道这里可能失败的原因是什么,或者以任何其他方式覆盖元模型级别的对象构建以能够按预期进行吗?
这种尝试存在一些误解。
- 每种类型有一个元class实例。因此,如果我们想让给定的类型只被实例化一次,正确的范围是元class中的一个属性,而不是
my
。my
意味着无论我们创建哪种类型,都有一个全局对象。 compose
方法,当 subclassingClassHOW
时,应该总是回调到基础compose
方法(可以使用callsame
).否则,class不会组成。- 此 exact 类型的
method_table
方法 returns table 方法。但是,大多数 classes 不会有new
方法。相反,它们将继承默认值。但是,如果我们将 that 包装起来,我们将产生非常全局的影响。
虽然 new
相对常见地覆盖以将接口更改为构造,但 bless
方法 - new
在完成任何映射工作后调用 - 不是我们想要的期望语言用户是压倒一切的。因此,我们可以继续的一种方法是尝试安装一个执行所需逻辑的 bless
方法。 (我们也可以使用 new
,但实际上我们需要检查此 class 中是否有一个,如果有则将其包装,然后添加默认副本的副本,如果不是,这需要更多的努力。)
这是一个有效的解决方案:
my class MetamodelX::Singleton is Metamodel::ClassHOW {
has $!instance;
method compose(Mu \type) {
self.add_method(type, 'bless', -> \SELF, |c {
without $!instance {
$!instance := SELF.Mu::bless(|c);
}
$!instance
});
callsame();
}
}
my package EXPORTHOW {
package DECLARE {
constant singleton = MetamodelX::Singleton;
}
}
请注意,我们不能在为 bless
添加的代码中使用 callsame
,因为它实际上不是 method
。我们可以改为使用 anon method
来编写它,但是我们遇到了问题,即该方法有自己的 self
想法,因此我们最终不得不保存元 class self
并安排一些其他方式访问 $!instance
.
最后,一个实际的例子:
use Singleton;
singleton Counter {
has $.x;
method inc() { $!x++ }
}
my $c1 = Counter.new;
$c1.inc;
my $c2 = Counter.new; # Gets same instance as in $c1
$c1.inc;
$c2.inc;
say $c1.x; # 3
say $c2.x; # 3