http://qs321.pair.com?node_id=300471

I was all set to give a superb answer to Tests for 'printing' code. I went to test my code and ruined it all.

Here's the great code failure:

use Errno ':POSIX'; { no warnings 'redefine', 'once'; local *CORE::GLOBAL::print = sub { $! = ENOSPC; return; }; print 'test', $/ or die $!; }
I intended to override builtin print so that it prints nothing, and triggers a 'No space left on device' system error in the same way print would. The handiness for testing is obvious. Trouble is this code doggedly prints 'test', completely ignoring the override.

After poking around for syntactos and scratching my head and reading the wrong docs, I posed the question in CB. Why doesn't this work? I got two answers.

One was that print is not overridable because it is an object method and doesn't really act like a function. That makes sense - the indirect object notation of print FOO @List makes the handle look very like an object. But, in Perl, or in OO in general, what kind of object method isn't overridable? Why isn't there something like a *CORE::GLOBAL::IO namespace with an overridable print method? Or is there?

The more convincing answer was that print is not overridable because the prototype is too complicated. The ability to have a default indirect object is the only complication I see, but it seems enough. The comment in perldoc -f prototype about things not overridable, things whose arguments are not expressible as a prototype, and things which don't really act like functions was the major justification for this view.

That doesn't entirely hold water. The expression prototype 'CORE::glob' evaluates to undef. Code from Camel3, p307 shows how to make glob take perl regular expressions rather than shell glob patterns. Corrected, it reads,

*CORE::GLOBAL::glob = sub { my $pat = shift; my @got; local *D; if (opendir D, '.') { @got = grep /$pat/, readdir D; closedir D; } return @got; }; package Whatever; print <[a-z_]+\.pm>; #show all pragmas in the current directory
which works just fine. In spite of the prototype docs, glob is overridable, certainly function-like, and it obviously has the simple prototype (;$), though that seems not reflected in CORE::.

I'm puzzled that global print is not overridable. It seems a natural function to want to do that to. It doesn't modify its arguments, so prototypes don't play an important role. In fact, many uses would ignore the arguments. Does anyone know why *CORE::GLOBAL::print is read-only?

I've tried this with my oldest perl, 5.004_02 for djgpp, and the behavior is the same. Prototypes were present in that version, so perhaps that's no surprise. Was global print ever overridable?

I'd appreciate any technical or historical insight the monks may offer on these questions. Print seems an odd function to leave out of the fun.

After Compline,
Zaxo

Replies are listed 'Best First'.
Re: !Overriding Builtin print
by diotalevi (Canon) on Oct 20, 2003 at 05:31 UTC

    Update: Please see Initial Devel::UncheckedOps, a macro for perl for notes on how print() can be overridden.


    Its a function of toke.c's Perl_keywords() function. The keywords with positive values aren't overridable (so says pp.c's pp_prototype function). I get the impression from S_scan_inputsymbol that glob() and readline()'s overrideability is more a function of explicit support than something handled by the prototype. It also got the impression it might have something to do with print's status as an indirect object method expecting a list. Some guesses anyway.

    Maybe this generalizes to: all functions overridable by CORE::GLOBAL:: have must prototypes or be explicitly supported by the perl parser (and the only known exceptions are glob() and readline()).

    Added: For those that care, I pulled the list of keywords from toke.c:Perl_keywords() that are marked as not being overridable. You'll notice glob() and readmore() are in this list - again, it works only because of explicit support.

Re: !Overriding Builtin print
by liz (Monsignor) on Oct 20, 2003 at 09:20 UTC
    ...I was all set to give a superb answer..

    I know the feeling ;-) I was recently inspired by a post by BrowserUK to override eval, inserting "#line" information in string evals so that any errors inside the eval could be more easily traced. Eval is one of those you can't override either ;-(

    What I hate about this, is that Perl just ignores this silently, instead of bombing out saying something like: "You can't override *CORE::GLOBAL::print". ;-(

    Liz

Re: !Overriding Builtin print
by Jenda (Abbot) on Oct 20, 2003 at 17:43 UTC

    You are not supposed to override print (that'd be far too global to be usefull anyway), but to create a class and tie a FILEHANDLE to it. If you then select that handle your print()s will do whatever you want at all.

    See perldoc perltie and perldoc Tie::Handle. There are two different classes for tied FILEHANDLEs within Mail::Sender is you wanted more examples.

    Jenda
    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

    Edit by castaway: Closed small tag in signature

Re: !Overriding Builtin print
by demerphq (Chancellor) on Oct 20, 2003 at 08:05 UTC

    IIRC some keywords can _only_ be overriden from import. I dont have time to test if print() is one of them, but it may be that if this override is done through the import mechanism in a similar way to which glob is overridden in File::DosGlob that it will work.


    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi


      No. You are mistaking ->import() for BEGIN. The point is getting the override information to the parser before it parses the portion you expected to be overridden. ->import() can do that because it happens at BEGIN-time during use(). You can do that yourself with an inline BEGIN block.

        Interesting. However, given the documentation on my system this mistake is not difficult to understand... From perlsub

        Overriding may be done only by importing the name from a module--ordin +ary predeclaration isn't good enough. However, the use subs pragma lets yo +u, in effect, predeclare subs via the import syntax, and these names may +then override built-in ones: use subs 'chdir', 'chroot', 'chmod', 'chown'; chdir $somewhere; sub chdir { ... }

        But thanks for the clarification.


        ---
        demerphq

          First they ignore you, then they laugh at you, then they fight you, then you win.
          -- Gandhi


Re: !Overriding Builtin print
by ambrus (Abbot) on Oct 20, 2003 at 13:38 UTC
    Under UN*X, the device /dev/full does exactly this. You might try to use tied file handles, I've never used them though.