Wait the light to fall

强制转换文件

焉知非鱼

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分钟才弄清楚如何成功地将其子类化。可能有的模块中的类更糟糕。有些程序员非常喜欢他们的私有属性。如果能放弃这种对角色的限制和/或如果明确提供返回类型就更好了。在目前的模型中,一些容易的事情变得很难。

无论如何,我把它弄好了,一旦我想出一个好名字,可能会把一个模块贴进生态系统。