Skip Menu |

This queue is for tickets about the Contextual-Return CPAN distribution.

Report information
The Basics
Id: 77734
Status: open
Priority: 0/
Queue: Contextual-Return

People
Owner: Nobody in particular
Requestors: andrew.bryan [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.004003
Fixed in: (no value)



Subject: C:R:V impostor objects
I recently got bitten by a behavior of C:R:V objects, that may not be a bug per se, but I believe it should be at least discussed: It is a reasonably common idiom to test objects in this way: use Scalar::Util 'blessed'; if ( blessed($thing) && $thing->isa('SomeClass') ) { # do something with our SomeClass object ... } Obviously C:R:V things are always going to "blessed", but when the C:R:V doesn't actually return an object in scalar context we can still normally get away with the above test since the AUTOLOADed isa will still return nothing: use Contextual::Return; my $thing = LAZY { 'some string' }; if ( blessed($thing) && $thing->isa('SomeClass') ) { # this won't be true because even though our thing is blessed, our isa # method will return false } # we continue on our merry way... There is a problem with some scalar values though... consider this: my $thing = LAZY { 123 }; # a numeric scalar if ( blessed($thing) && $thing->isa('SomeClass') ) { # this isa method is actually going to die... the real reason error message # is disguised behind a generic C:R:V message but it actually: # Can't call method "isa" without a package or object reference } This is the "bug" that bit me.. now that I know I might be given C:R:V objects I can work around it, but it was a head scratcher for a while. And it made me realize that something even more insidious could occur.. consider this: use DateTime; # for example # let's say our C:R:V scalar happens to be a string with, unintentionally, # the same value as the name of a loaded class: my $thing = LAZY { 'DateTime' }; if ( blessed($thing) && $thing->isa('DateTime') ) { # this will be true! Oh noes... now we think we have a valid # DateTime object and anything we try to do with it is probably # going to fail: my $date_str = $thing->iso8601; # FAIL! Can't use string ("DateTime") as a HASH ref while "strict refs" ... } I can imagine that if this occurred somewhere, where C:R was not even a consideration, it would be a nightmare to debug! The same errors would occur using ->can too... which may be worse since I don't believe 'can' is ever supposed to throw an exception. I feel like that we could "fix" this in C:R.. but I'm not yet entirely convinced that it wouldn't cause other problems.. but here are two potential ways we might address this: 1.) in C:R:V::AUTOLOAD in the context handler loop, as soon as we have the $object we could do something like: # after this my $object = eval { $handler->(@{$attrs->{args}}) }; # do this: return if ( $requested_method eq 'isa' || $requested_method eq 'can') && !blessed $object; In other words, if we're called with can or isa and our value is not actually an object then just return nothing. This seems fairly safe to me but perhaps I haven't thought through it entirely. 2.) The other idea was to have C:R monkey patch Scalar::Util::blessed to return false when given a C:R:V that does not resolve to an object, (and its class when it does; currently blessed will always return C:R:V). Then if someone is not using blessed and gets bit then so be it! This is not really meant to be a serious solution... I thought it was funny and evil but I suspect would cause waaaaay more problems. Your thoughts? (this module is awesome btw... I knew it existed but had never really come across it until it now... )
Subject: Re: [rt.cpan.org #77734] C:R:V impostor objects
Date: Thu, 14 Jun 2012 14:32:57 +1000
To: bug-Contextual-Return [...] rt.cpan.org
From: Damian Conway <damian [...] conway.org>
Hi Andrew, Thanks for the fascinating report. Like you, I'm not entirely convinced this is actually a bug. It's almost like a Wittgenstein-ian category error. ;-) Show quoted text
> It is a reasonably common idiom to test objects in this way:
Indeed. But that's the heart of the problem. Proxies are (almost inevitably) implemented as objects, so checking whether they're objects is operating at an ambiguous semantic level. A problem that becomes even more obvious it your "oh noes" example. The underlying issue is that blessed() really ought to be a method (so that proxy objects can override it). And, of course, it can't be a method. That's why I generally just use: if ( eval{ $thing->isa('SomeClass')} ) { .... } instead. Show quoted text
> This is the "bug" that bit me.. now that I know I might be given C:R:V > objects I can work around it, but it was a head scratcher for a while.
I can see how puzzling (and then annoying) that could be. It really does need to be fixed in some way. Show quoted text
> In other words, if we're called with can or isa and our value is not > actually an object then just return nothing. This seems fairly safe to > me but perhaps I haven't thought through it entirely.
It would have to be a little more sophisticated than that, because both isa() and can() are supposed to work on class names (i.e. strings) as well. So I suspect we would merely be replacing one puzzlement with another. Show quoted text
> 2.) The other idea was to have C:R monkey patch Scalar::Util::blessed > to return false when given a C:R:V that does not resolve to an > object, (and its class when it does; currently blessed will always > return C:R:V). Then if someone is not using blessed and gets bit > then so be it! This is not really meant to be a serious solution... > I thought it was funny and evil but I suspect would cause waaaaay > more problems.
Think of whom you're suggesting this evil solution to! Of course, that's now exactly what I'm contemplating. >;-) The only problems I think it would create are if anyone is using blessed() specifically to determine whether they have a C::R::V object, but I think it's more likely they'd be using ref() for that, and I can certainly document the trap. The more perplexing problem is whether I should extend the Scalar::Util monkeying to refaddr() and reftype() as well. I suspect not, as these two are more clearly in the "implementation domain", rather than the "solution space". I've attached a beta of the updated module. If you'd care to play with it prior tot the next release. I'd welcome any feedback. Show quoted text
> (this module is awesome btw...
<bows> Thanks for pointing out this less-than-awesome issue. :-) Damian

Message body is not shown because sender requested not to inline it.