Skip to content

Instantly share code, notes, and snippets.

@cygx
Created August 26, 2016 16:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cygx/7d792e09b182a1a79b1dac82663dfddb to your computer and use it in GitHub Desktop.
Save cygx/7d792e09b182a1a79b1dac82663dfddb to your computer and use it in GitHub Desktop.
RFC: IO frontend redesign

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;
    }

    ...
}
@zhuomingliang
Copy link

jnthn++ gave a comment, in case of you missing it: http://irclog.perlgeek.de/perl6-dev/2016-09-14#i_13210399

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment