Front page | perl.perl5.porters |
Postings from November 2003
[5.8.12] Proposal for changing UNIVERSAL semantics
Thread Previous
|
Thread Next
From:
Michael.Jacob
Date:
November 4, 2003 01:46
Subject:
[5.8.12] Proposal for changing UNIVERSAL semantics
Message ID:
OFA50B2263.C62BB032-ONC1256DD4.002F4505@schering.de
[Sorry if this mail get's in twice, but I haven't seen it on the list for
12 hours, and that account I used appended an ad to the mail's end, so I
resend it from work.]
Hi all,
As we have seen in the last month, both UNIVERSAL:: and UNIVERSAL.pm are
broken in concept. Multiple fixes/workarounds have been discussed, but none
took. So I took some time to layout the root of the problem in my head.
This is the result:
1.) Change can()'s semantics.
can() returns unexpected reults if any module changed something in the
UNIVERSAL:: package. This can make other modules fail, we have seen this
with DBI(!)... So:
Starting with Perl 5.8.12, can() shall not search the package UNIVERSAL for
inherited methods unless UNIVERSAL is explicitly included in the @ISA of
the class tested against.
This means that
@Foo::Bar::ISA = qw();
(bless {}, 'Foo::Bar')->can('can')
will return false, but
@Foo::Bar::ISA = qw( UNIVERSAL );
(bless {}, 'Foo::Bar')->can('can')
will return true.
Starting with Perl 5.8.10, can() will issue a warning when returning a
value that it would not return with above change implemented: "Testing for
methods implicitly inherited from UNIVERSAL with can() is deprecated"
I think, this change should not break any well written module. Testing for
any method that's in UNIVERSAL is just useless, we already know before the
test that it must be there. Any module that want's to know if there is some
additional method in UNIVERSAL:: can just test against UNIVERSAL->can()
directly.
2.) Implement a smart UNIVERSAL::AUTOLOAD in UNIVERSAL.pm
Some modules may want to override UNIVERSAL::AUTOLOAD. We have seen that
this can be dangerous; we can also imagine what happens if two or more
modules want to do this in one program... So:
UNIVERSAL.pm will become a dual-life module. It will implemement a AUTOLOAD
method and an @AUTOLOAD array. Any module wishing to change AUTOLOAD's
behavior will push a CODE ref into this array. The smart
UNIVERSAL::AUTOLOAD will, when triggered, call these coderefs (aka
handlers) one after each other. Each can return one of four values:
CODE - means the handler has generated the requested method on-thy-fly.
UNIVERSAL::AUTOLOAD will goto() this coderef and stop processing.
true - means the handler has processed the request. UNIVERSAL::AUTOLOAD
will exit with this value. (Note: if the handler wants to return any false
value, it can always return sub{0}...)
false but defined - means the handler has accepted the request but cannot
generate the request. If there is something in $@, UNIVERSAL::AUTOLOAD will
re-trow it, else it will die with Perl's default error message.
undef - means the handler has refused the request. UNIVERSAL::AUTOLOAD will
ask the next handler. If no handler accepted the request, it will die with
Perl's default error message.
3.) Implement a smart UNIVERSAL::DESTROY in UNIVERSAL.pm
See UNIVERSAL::AUTOLOAD and ignore the return value things.
4.) Implement a smart UNIVERSAL::can in UNIVERSAL.pm
If we release the smart UNIVERSAL.pm onto CPAN, it must also run on older
Perls. As the core can() will behave not in the expected way, we will
include a can() in UNIVERSAL.pm. It will by default mimic the core can(),
but can be triggered by "use UNIVERSAL qw( :keyword );" to change it
behaviour into the Perl 5.8/5.10/5.12 way.
This "use UNIVERSAL qw( :keyword );" will only work if issued from the
package main. This is to prevent modules to change the behaviour of the
complete program without the program's programmer having any clue. It will
also die() if called more than once, even if it was with the same keyword.
5.) Implement a smart UNIVERSAL::import in UNIVERSAL.pm
To stop doing damage to other modules we should also include a smart import
():
UNIVERSAL::import will work as Exporter::import if call on UNIVERSAL
itself. It will issue a warning and behave like Exporter::import if the
packing it works for includes a @EXPORT array. It will do nothing
otherwise.
This way we will break neither the modules that rely on being an Exporter
if use()ing UNIVERSAL, nor the modules that rely on not having any import()
method by default.
Summary:
This may look like a lot of big changes, but it's not. This is a fix for
many small problems and, it also extends UNIVERSAL's usefulness by removing
the limitation of just one module being able to override
UNIVERSAL::AUTOLOAD.
With this fix, even if using only the new UNIVERSAL.pm with an old Perl, we
can fix all UNIVERSAL related problems reported the last month:
* useing UNIVERSAL breaking DBI, autouse, Net::SSH, ...
* removing UNIVERSAL::import breaking BerkeleyDB, ...
* existance of UNIVERSAL::AUTOLOAD breaking XML::SAX, ...
Michael Jacob
aka micaja@de.ibm.com
PS: BTW: This is, of cause, a first draft.
Thread Previous
|
Thread Next