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

Re: [5.12.0] Proposal for changing UNIVERSAL semantics

Thread Previous | Thread Next
From:
Michael Jacob
Date:
November 4, 2003 06:04
Subject:
Re: [5.12.0] Proposal for changing UNIVERSAL semantics
Message ID:
GMail.1067947858.215676664.81198350997@smtp.youvegotpost.com
On 2003-11-04 11:34:36 Michael G Schwern <schwern@pobox.com> was overheard saying:
---------------------------------------------------
> I think you're attacking this problem from the wrong end.

I was looking on what programmers of popular CPAN modules DWIMed Perl to do MY opinion on UNIVERSAL is that the whole concept is broken and that UNIVERSAL should be removed completely, but that is not an option.

>On Mon, Nov 03, 2003 at 12:37:49PM -0800, Michael Jacob wrote:

>> 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:

>It would seem DBI breaks because it did something to can().

Huh, I didn't notice DBI doing things with UNIVERSAL itself. But this bit shouldn't matter here.

>DBI appears to have overriden and busted can().  Running DBI with UNIVERSAL.pm
>doesn't cause any problems, so I think adding Attribute::Handlers into the 
>mix exposed some problem with DBI's can() hacks.

Ok, here you're right, I was oversimplifying. DBI will break when used with any DBD that does not implement all possible methods when there is an UNIVERSAL::AUTOLOAD present.

That's because DBI does somthing like:

if ($dbd->can($method) or $dbd->can('AUTOLOAD') {
  $dbd->$method();

And that's just what I said above---an unexpected result from isa().

>> Starting with Perl 5.8.12
>
>Oh, good, we'll probably never make it to 5.8.12. ;)

Outch, one got through. 1.5., uhm, 6.12, uhm. I mean to type five dot 12, but somehow I have real problems with it :-)

I've just noticed I also did this is the subject. To make it clear: THIS IS NOTHING I WOULD DO IN A MAINT. Sorry, for the typies...

>> can() shall not search the package UNIVERSAL for inherited methods unless UNIVERSAL is explicitly included in the @ISA of the class tested against.
>
>Wait a second, the results are *not* unexpected.  can() tells us if a method
>can execute a given method and what that method will be.  This change

I think that should be "if an object can execute a given method".

That's exactly the problem. It is not that object, that can() do something. It is a code written by another person for another reason for use with another kinf of objects that can do something other when called. This is almost never the thing I want to know.

When I call can(), I want to know if the object or one of its base classes implement some method. And when someone pushed a method with the same name into UNIVERSAL he did almost surely did not want to add the functionality to that object I'm testing.

See the DBI example above. DBI wants to know if the DBD can AUTOLOAD. It does not want to use if there is a default AUTOLOAD handler for the running program.

If one wants to add e.g. AUTOLOAD functionality to some class, he would surely not install UNIVERSAL::AUTOLOAD but subclass that class. (And if he doesn't we can tell him he's coding some kind of dangerous rubish. <smile/>)

>breaks the well documented behavior of can() by making UNIVERSAL an
>exception.  can() might report the method isn't there, but its there.

UNIVERSAL is an exception already. We'd just remove it.

>This just papering over the problem of begin cavalier with UNIVERSAL 
>(as Attribute::Handlers is) and overriding UNIVERSAL methods can() (as DBI 
>is).  The namespace is still polluted, the methods are still there and can
>be called.  All you're doing is taking down the signs.

They can be called, right. But, if you want to call a method that comes from UNIVERSAL there is no need to test with can()---you already know it's there. And about the signs, you can always add "||UNIVERSAL->can()" if you really want to know.

>> 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.
>
>No, this breaks encapsulation and defeats the point of can().  I shouldn't 
>have to know if Foo implements a method or if Foo inherits it from UNIVERSAL 
>or any other module. 
>
>It adds an extra step to nearly every use of can().
>
>	$class->foo if $class->can('foo') or UNIVERSAL->can('foo');

No, it removes the extra step that is missing from nearly every use of can()

$class->foo if $class->can('foo') and $class->can('foo') ne UNIVERSAL->can('foo');

>> 2.) Implement a smart UNIVERSAL::AUTOLOAD in UNIVERSAL.pm
>
>Someone once said, when you've got a problem and you try to solve it with
>AUTOLOAD you now have two problems.

I second that statement. But as said, people are expecteing to be able to use it. So we either remove it or add a save way to do things.

>Also, I would not want to see UNIVERSAL.pm expanded at all.  There's
>already too many functions polluting the global namespace as it is which
>is what caused all these problems in the first place.  Adding more,
>and an AUTOLOAD no less!!, is likely to simply make things worse.

The problem with AUTOLOAD is, that it is one of the special methods Perl will automatically call. So if you want to code "autouse", you have no other way to do it. If you want to add specific error messages about attributes, you have no other way to do it. There will be conflicts aout this limited resource, we see it now, and we now debate about changing UNIVERSAL.pm anyway, do why not solve this problem now, too?

>However, what you propose addresses a the valid problem of coordinating
>AUTOLOAD overrides.  But its likely to be a really sticky thing to get just
>and thus wrong for the core.  You should put this up on CPAN as 
>UNIVERSAL::AUTOLOAD.

There may be better ways, maybe "use UNIVERSAL AUTOLOAD => \&myHandler;" or "use UNIVERSAL::AUTOLOAD handler => \&myHandler;" instead of the array, that's true. But it really should be a solution in the core so all module authors pick up that save way to do it. Else it's just another nearly unused module like that slurp thing last week...

Let's express it this way: UNIVERSAL::AUTOLOAD is a limited resource inside core Perl. So a solution for coordinated use should exist in the core, too.

>> 5.) Implement a smart UNIVERSAL::import in UNIVERSAL.pm

>This issue has been fixed in bleadperl.

And taken out of 2.8.2, I followed the list a while. But I've also seen that the simple fix breaks some other code, even if it's just some tests. So I proposed a more compatible way to do it.

>The severity of these problems is very low...

Yes, the severity is low, but the level of knowledge to fix/workaround a simple "use Attribte::Handlers; use DBI; DBI->connect('mysql... => KAWUMM" is rather high.

>>  * useing UNIVERSAL breaking DBI, autouse, Net::SSH, ...
>
>DBI is doing Dark Magick with can() and the method dispatcher which appears to
>be not 100% correct.  Plus Attribute::Handlers is plowing *way* too much 

see above for my oversimplifying about DBI here.

>stuff into the UNIVERSAL namespace.  Both will be fixed and the problem 
>should disappear.

Attribute::Handlers was doing a very legitimate thing: Providing custom error messages when it was misused. This should really be possible. But as of now, every use of UNIVERSAL::AUTOLOAD will vreak some other code.

>I didn't hear anything about Net::SSH.

It used use() parameters in a cosmetic way for it's submodules and was no Exporter. Broke on UNIVERSAL::import...

>>  * removing UNIVERSAL::import breaking BerkeleyDB, ...
>
>This was a bug in a test, not the module itself.  It would only effect

I know, but I'd guess there is at least one module in the wild, that will really break. (Ok, fix is very easy, but as said above, the knowledge to fix..)

>>  * existance of UNIVERSAL::AUTOLOAD breaking XML::SAX, ...
>
>Haven't heard about this one, but I'm not surprised.

 elsif (defined $callbacks->{'Handler'} and $method = $callbacks->{'Handler'}->can('comment') ) {
 ...
 elsif (defined $callbacks->{'Handler'} and $callbacks->{'Handler'}->can('AUTOLOAD') ) {

Have a thousand Perl programmers search for bugs in this two lines, I'd guess the only ones that would find it are those who read about UNIVERSAL on p5porters these days.

Michael 
--
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 Previous | 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