Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Can I ask Perl if an object will stringify?

by haukex (Archbishop)
on Mar 29, 2015 at 17:16 UTC ( [id://1121710]=perlquestion: print w/replies, xml ) Need Help??

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!

Replies are listed 'Best First'.
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.

      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

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.

      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

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 ;)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)

    PS: Je suis Charlie!

      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.)

      "it's easier to ask for forgiveness than it is to ask for permission. " (stolen from Python ;)

      I guess Python stole it from Admiral Hopper, who doubtless stole it from someone else.

        Yes you're probably right...

        ...I beg for forgiveness! ;-P

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        PS: Je suis Charlie!

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

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        PS: Je suis Charlie!

      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

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.

    WBR, basiliscos.

      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

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).
      > Strange that nobody brought up ...

      Because nobody compares to you, you son of a rabbit. ;)

      Thanks++ :)

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)

      PS: Je suis Charlie!

      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

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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1121710]
Approved by ww
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (7)
As of 2024-04-24 10:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found