强制转换文件
— 焉知非鱼coercive files
许多 API 都有一两个例程可以接受一个文件作为参数。在 Raku 中,我们可以很容易地把 Str 变成 IO::Path,随后再变成 IO::Handle。作为模块作者,提供 MMD 变体是一种礼貌的举动,所以用户可以提供他们喜欢的东西。
sub s($file is copy where { $_ ~~ Str|IO::Path|IO::Handle or fail(&?ROUTINE.name ~ ' likes Str, IO::Path and IO::Handle.') } ) {
given $file {
when Str { $file = .IO.open; }
when IO::Path { $file = .open; }
when IO::Handle { }
}
say ?$file;
}
这是个模板. 在 Raku 之国几乎是被禁止的. 使用新的强转协议,我们可以实现一个角色,从此快乐地节省时间。
role Filish[*%mode] is IO::Handle {
proto method COERCE($) {*}
multi method COERCE(Str:D $s) {
my $handle = self.new: :path($s.IO);
$handle.open: |%mode
}
multi method COERCE(IO::Path:D $p) {
my $handle = self.new: :path($p);
$handle.open: |%mode
}
multi method COERCE(IO::Handle:D $h) {
$h
}
}
sub f(Filish[:w, :a, :!bin]() $handle) {
$handle.put: "foo" xx 42;
$handle.close;
}
f('/tmp/foo.txt');
有了强转类型约束 Filish[:w, :a, :!bin]()
,我们基本上说: “给我一个代表文件的东西,我将以追加的方式打开它进行写作”。我不知道可以在角色的参数列表中使用一个 slurpy(*@a 也可以)。这似乎是一个 ENODOC。由于它使我的文件更容易,我不会抱怨。
新的强转协议非常有用,但是有一个缺陷。它强迫我返回包含 COERCE-方法的类型。在一个没有太大意义的角色中,它迫使我与 IO::Handle 打交道。我花了30分钟才弄清楚如何成功地将其子类化。可能有的模块中的类更糟糕。有些程序员非常喜欢他们的私有属性。如果能放弃这种对角色的限制和/或如果明确提供返回类型就更好了。在目前的模型中,一些容易的事情变得很难。
无论如何,我把它弄好了,一旦我想出一个好名字,可能会把一个模块贴进生态系统。