Let's make IO
useful: First, it should provide default implementations for operations that can be realized in terms of more primitive ones. The latter should die if not overridden.
Second, error handling should happen in terms of failures. However, properly propagating failures can he a bit of a hassle, so the idea is to let the implementation just throw exceptions, which get converted to failures by the proto methods in IO
.
Example code:
role IO {
sub unsupported($obj, &method) is hidden-from-backtrace {
die "IO operation '{&method.name}' not supported by {$obj.^name}";
}
proto method print(|) { CATCH { when X::IO { fail $_ } }; {*} }
multi method print(IO:D: Str:D --> True) { unsupported self, &?ROUTINE }
multi method print(IO:D: *@list --> True) {
self.print($_) for @list;
}
proto method put(|) { CATCH { when X::IO { fail $_ } }; {*} }
multi method put(IO:D: Str:D \x --> True) {
self.print(x);
self.print-nl;
}
...
}
The rest of the system will also make liberal use of roles for maximum composability, eg
role IO::Handle does IO {
has $.oshandle;
multi method write(IO::Handle:D: Blob:D $buf --> True) {
# TODO: catch exception and rethrow as X::IO
nqp::writefh($!oshandle, nqp::decont($buf));
}
...
}
Decoding on input and encoding on output are realized by mixing in appropriare roles, eg
role IO::Encoding {
has $!encoder;
method set-encoder($encoder) {
if defined $!encoder {
# invalidate/flush buffers and streams
...
}
$!encoder = $encoder;
}
multi method print-nl(IO:D: --> True) {
self.write($!encoder.nl);
}
...
}
Opening an IO object is implementation-specific, so &open
just re-dispatches to that:
proto sub open(|) {*}
multi sub open(IO() $io, *%options) { $io.open(|%options) }
Finally, a class that actually does IO:
class IO::FileHandle does IO::Handle {
has $.path;
multi method open(IO::FileHandle:D: Bool:D :$bin = False, ...
--> IO::FileHandle:D) {
die 'file handle already open'
if defined $!oshandle;
...
$!oshandle := ...;
# mixin the roles for encoding if required
if !$bin {
if $r {
self does IO::Decoding;
self.set-decoder(...);
}
if $w {
self does IO::Encoding;
self.set-encoder(...);
}
}
self;
}
...
}
jnthn++ gave a comment, in case of you missing it: http://irclog.perlgeek.de/perl6-dev/2016-09-14#i_13210399