haukex has asked for the wisdom of the Perl Monks concerning the following question:
Hello everyone,
I seek your wisdom on this question:
Is there a way to ask Perl whether an object supports stringification,
including via "magic autogeneration"?
The only way I've found so far was by trying to eval the stringification,
like in the code below.
The specific case here is the "ICanStringify" class,
where overload::Method($s,'""') is false, but the object still stringifies.
Did I miss some function somewhere that can tell me whether that class will stringify?
#!/usr/bin/perl
use strict;
use warnings;
{ package OnlyAString;
use overload fallback=>0, '""'=>sub { ${shift()} }
}
{ package ICanStringify;
use overload fallback=>undef, '0+'=>sub { ${shift()} }
}
{ package OnlyANumber;
use overload fallback=>0, '0+'=>sub { ${shift()} }
}
bless my $s1=\do {my $x=111}, 'OnlyAString';
bless my $s2=\do {my $x=222}, 'ICanStringify';
bless my $s3=\do {my $x=333}, 'OnlyANumber';
can_str($s1);
can_str($s2);
can_str($s3);
use overload ();
sub can_str {
my $s = shift;
print "Object ", overload::StrVal($s), ":\n";
print " \"\" ", overload::Method($s,'""')
?"IS":"is NOT", " overloaded\n";
my $e = eval { "$s" };
print " stringification ", defined($e)
?"WORKED: $e\n":"DIDN'T work: $@\n";
}
Output:
Object OnlyAString=SCALAR(0x3684370):
"" IS overloaded
stringification WORKED: 111
Object ICanStringify=SCALAR(0x3664210):
"" is NOT overloaded
stringification WORKED: 222
Object OnlyANumber=SCALAR(0x3679682):
"" is NOT overloaded
stringification DIDN'T work: Operation """": no method found, argu
+ment in overloaded package OnlyANumber at test.pl line 28.
The background is that I have a function that accepts only strings.
Because passing a reference was a mistake I made a few times, I started
warning if any references were passed to it, including objects.
But then I realized that some objects stringify and that's useful,
and that some objects die when you try to stringify them.
I'd like to loosen the restrictions, and still warn on references
and objects that don't stringify, but not on objects that stringify.
Any wisdom on this topic would be greatly appreciated!
Re: Can I ask Perl if an object will stringify?
by jdporter (Paladin) on Mar 29, 2015 at 17:40 UTC
|
I think that is probably non-trivially difficult to do as you're thinking; and anyway, is not the best way to be approaching it. I think, instead, you should implement the constraint (the contract) as permissively as possible, and that means trying to use the argument as a string, and simply letting the chips fall where they may. (That is, you could catch them and issue your own warning; or let them bounce up the stack, or whatever.) You've already identified one scenario where a simple can-like test fails to meet the need; there could certainly be others. For example, the argument could be a ref to a tied object whose FETCH method returns a stringifiable or nonstringifiable value depending on program state. (At least, I think that's a possible scenario; I haven't tested it.) The point is, don't try to predict and handle all the possibilities.
Btw... I think forcing stringification by concatenation ( $s = ''.$s;) is probably more efficient than by interpolation ( $s = "$s";).
I reckon we are the only monastery ever to have a dungeon stuffed with 16 ,000 zombies.
| [reply] [d/l] [select] |
|
Hi jdporter,
Thanks for your thoughts, I think you're right, and if I was writing this module from
scratch I probably wouldn't implement the check in the first place, or make it simpler...
especially now that I see how much time went into researching it ;-)
My module originally had a warning in place for all references and undefined values,
the idea was to be user-friendly and prevent the user from passing references by accident,
and this effort was an attempt to exclude objects that stringify from that warning.
I think that I've found a solution for now (I've posted it below), which is
definitely on the complex side, but I'm okay with it for now.
Your idea of a tied variable returning different values is something it might not handle...
I will have to test that out!
Thanks and Regards,
-- Hauke D
| [reply] |
Re: Can I ask Perl if an object will stringify?
by Corion (Patriarch) on Mar 29, 2015 at 20:45 UTC
|
This is not a solution to your immediate problem but might be a solution to your overarching problem. no stringification is a module that (fatally) prevents stringification of references.
| [reply] |
|
Hi Corion,
Thanks for the idea, I hadn't seen that pragma before! It looks like you're right
that it might not solve my question directly, but since my intention was to warn
people about them accidentally having stringified a reference into something like
"HASH(0x9c5d2d9)", a value which probably isn't useful in this context,
those users might also be able to use this pragma to avoid mistakes like that.
Thanks and Regards,
-- Hauke D
| [reply] |
Re: Can I ask Perl if an object will stringify?
by LanX (Saint) on Mar 29, 2015 at 22:10 UTC
|
If I were you I'd reject simple refs and allow all objects without testing.
Don't start fiddling with internals.
In any case you can only do runtime checks in Perl.
So if you get the default stringification from an object (IIRC that's what ref returns) you are free to warn, that something looks strange.
Don't try to overengineer your function!
Sometimes "it's easier to ask for forgiveness than it is to ask for permission. " (stolen from Python ;)
| [reply] |
|
And to add, it’s terrible, terrible advice in legal matters. And in personal matters… seems like terrible advice too. :P (Some day I’ll tell a directly related multi-million dollar lawsuit work story.)
| [reply] |
|
| [reply] |
|
| [reply] |
|
Tag Rolf
What is 'stolen from Python'? Do you mean the phrase "it's easier to ask for forgiveness than it is to ask for permission"?
That's a quotation from the great Rear-Admiral Grace Hopper.
Grace Hopper - Wikiquote
| [reply] |
|
Moin Dumu,
already admitted that Grace was earlier but ...
EAFP is a very common coding style in Python, such that it became part of their glossary.
Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.
IMHO they rely on it much more than Perl hackers do. I'm often surprised how often try/catch constructs are used there to handle fuzzy situations.
"Stolen" because we don't catch any exceptions in my suggestion.
| [reply] |
|
|
|
|
Hi Rolf,
Thanks for your thoughts, and I agree that my solution is probably in the gray
zone of being a little bit overengineered ;-) Instead of my current implementation
(I've posted it below) getting any more complex, I think I would remove the check
entirely, or at least simplify it as you suggest. This particular module already
spends a significant amount of code on trying to be helpful to its user - as far
as I know, that's only me right now :-)
Thanks and Regards,
-- Hauke D
| [reply] |
Re: Can I ask Perl if an object will stringify?
by basiliscos (Pilgrim) on Mar 29, 2015 at 21:49 UTC
|
Hi,
Methinks you are trying to break the language itself: perl stringifies everything on demand (in string context). The "stringification" operator purpose is to provide something meaningful, and not to be some marker "it stringifies to to something meaningful".
I think you can use something like UNIVERSAL::can to check pretense of method like "to_my_string". If the passed object don't have that method, that means that something is wrong, and die could be better then warn.
| [reply] |
|
Hi basiliscos,
Thanks for your response. Just to clarify: The values I'm looking at are not under my control.
They are normally supposed to be numbers, strings, or objects that stringify to
something useful, like for example Path::Class objects.
Those values will be stringified and used as an argument to an external command,
which is why I make the assumption that references,
which would be stringified to something like "HASH(0x9c5d2d9)",
are probably a mistake on the user's part, and I'd like to provide a warning for those cases.
I've posted my current solution below.
Thanks and Regards,
-- Hauke D
| [reply] |
Re: Can I ask Perl if an object will stringify?
by ribasushi (Pilgrim) on Apr 01, 2015 at 08:15 UTC
|
Strange that nobody brought up Devel::OverloadInfo. It will do exactly what you need (unless I misunderstood your question). | [reply] |
|
| [reply] |
|
Hi ribasushi,
Thanks for the suggestion! I tried it out, and the module returns a bit more
information than overload::Method(), but unless I missed something it unfortunately
doesn't seem to directly answer the question whether an object will stringify via
magic autogeneration or not, but I could probably figure it out based on all the
information it returns. Here's an example of what the overload_info() function
returns for two of my test classes from the original post:
# this class stringifies even though "" is not directly overloaded
{
"0+" => {
class => "ICanStringify",
code => sub { ... },
code_name => "ICanStringify::__ANON__",
},
"fallback" => { class => "ICanStringify", value => undef },
}
# this class does not stringify
{
"0+" => {
class => "OnlyANumber",
code => sub { ... },
code_name => "OnlyANumber::__ANON__",
},
"fallback" => { class => "OnlyANumber", value => 0 },
}
Right now my current solution (posted below) of "just try it out" seems to work,
so I'm a bit hesitant to change it except in a way that would simplify it.
I'll look into it a bit more whether I can do so with the above info.
Thanks and Regards,
-- Hauke D
| [reply] [d/l] |
Re: Can I ask Perl if an object will stringify?
by haukex (Archbishop) on May 25, 2015 at 09:44 UTC
|
Hi everyone,
First of all, apologies for my extremely late reply, I'm working on this in
my free time and I haven't had a chance to get back to it until now.
So below is the logic I am now using, and it seems to work: the function "strify" takes a value,
produces the warnings / errors that I wanted to provide for user-friendliness,
and otherwise should act like normal Perl stringification.
If I wasn't providing those custom warning and error messages,
the whole thing could be simplified quite a bit, that's something I'll
consider for the next version.
You can see the code (with detailed comments) in my module
here.
In researching a solution I ended up writing a bunch of
test classes and
test cases.
#!/usr/bin/perl
use strict;
use warnings;
{ package OnlyAString;
use overload fallback=>0, '""'=>sub { ${shift()} } }
{ package ICanStringify;
use overload fallback=>undef, '0+'=>sub { ${shift()} } }
{ package OnlyANumber;
use overload fallback=>0, '0+'=>sub { ${shift()} } }
print " OnlyAString: ",strify(bless \do {my $x=111}, 'OnlyAString' )
+,"\n";
print "ICanStringify: ",strify(bless \do {my $x=222}, 'ICanStringify')
+,"\n";
print " OnlyANumber: ",strify(bless \do {my $x=333}, 'OnlyANumber' )
+,"\n";
use Scalar::Util 'blessed';
use overload ();
use Carp;
sub strify {
my ($x) = @_;
if (!defined $x) {
warnings::warnif('uninitialized','Use of uninitialized value i
+n argument list');
return "" }
elsif (blessed($x) && overload::Overloaded($x)) {
if (overload::Method($x,'""')) # we can assume stringify will
+ work
{ return "$x" }
elsif (defined(my $rv = eval { "$x" }))
{ return $rv }
elsif ($@=~/\bno method found\b/) # throw custom error messag
+e
{ croak "Package ".ref($x)." doesn't overload stringificat
+ion: $@" }
else { die $@ }
}
else {
ref($x) and warnings::warnif("Argument list contains reference
+s/objects");
return "$x" }
}
Output:
OnlyAString: 111
ICanStringify: 222
Package OnlyANumber doesn't overload stringification: Operation """":
+no method found, argument in overloaded package OnlyANumber at ...
Thanks and Regards,
-- Hauke D
| [reply] [d/l] [select] |
|
|