develooper Front page | perl.perl5.porters | Postings from November 2003

[5.8.12] Proposal for changing UNIVERSAL semantics

Thread Next
From:
Michael Jacob
Date:
November 4, 2003 01:30
Subject:
[5.8.12] Proposal for changing UNIVERSAL semantics
Message ID:
GMail.1067891869.230573766.91668135212@smtp.youvegotpost.com
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
aka michael.jacob@schering.de

PS: BTW: This is, of cause, a first draft. 
--
Gratuitous Advertisements:

This email was brought to you by You've Got Post!
http://www.youvegotpost.com

--

Publish your company's press releases for just $5.99 per month (unlimited access), visit http://www.officialspin.com


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About