Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could I subclass Perl 6's IO::Handle?

Tags:

subclass

raku

How would I subclass IO::Handle? For example, I want to be able to "autoflush" by calling flush after every say:

class MyIO is IO::Handle {
    multi method say(MyIO:D: **@text --> True) {
        nextsame;
        $*ERR.say: "Flushing\n";
        self.flush;
        }
    };

Metamodel::Primitives.rebless: $*OUT, MyIO;
put $*OUT.^name;

$*OUT.say: "This is standard out";

But, it appears that MyIO is never called.

I figure that I might wrap say to produce the effect, but I'm more interested in the technique of simple subclasses to override some behavior. But, having said that, if there's a more Perly 6 way that the design intends for people to use.

So, some questions:

  • Does Perl 6 actaully care that I reblessed it? How does it look up method names that might skip that?

  • Are builtin classes especially resistent to standard OO techniques because of their sorcery and NQPness?

  • Does Perl 6 discourage low level fiddling with handles, such as re-opening $*OUT on a file descriptor? (As in Does changing Perl 6's $*OUT change standard output for child processes?)

like image 531
brian d foy Avatar asked Jul 11 '17 02:07

brian d foy


1 Answers

Reblessing is sooo 90's :-)

Why not use role composition to achieve your goal?

role MyWay {
    method say(|) {
        note "whee";
        nextsame
    }
}
my $*OUT = PROCESS::<$OUT> but MyWay;
$*OUT.say("foo")   # whee\nfoo

This basically creates a new dynamic $*OUT based on the existing $*OUT but with a new method say mixed in.

To answer your questions as well as I can:

  • reblessing is considered something internal that you most likely don't want to do
  • a lot of builtin classes are optimised for speed by not using the standard way of creating objects. This sorta makes it difficult to subclass them. In some cases, special code reverts to the standard way of making objects, making these classes subclassable after all (like Date).
  • generally I would say low level fiddling is not advised as there are still internal refactors underway to make things faster, which could break your code if you're fiddling too low.

Since say internally uses print, it would be better to actually mix in your own print method:

role MyWay {
    method print(|) {
        note "whee";
        nextsame
    }
}
my $*OUT = PROCESS::<$OUT> but MyWay;
say "foo";   # whee\nfoo

This has the added benefit of no longer needing to call say as a method, because the sub version of say will pick up the altered $*OUT dynamically.

like image 130
Elizabeth Mattijsen Avatar answered Nov 03 '22 16:11

Elizabeth Mattijsen