Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

(tye)Re: ref, no, maybe?

by tye (Sage)
on Jan 11, 2001 at 05:00 UTC ( #51042=note: print w/replies, xml ) Need Help??


in reply to ref, no, maybe?

I say that the only valid use of ref() is doing something like ! ref($r). I think it is just fine to have string manipulating code that refuses to work on a reference (if you really wanted it to do that, then you can stringify the reference before passing it in). So your code would be fine with me if you wrote it either like this:

if ( ref( $value ) ) { # <- that's a ref! # We have multiple values for this key foreach ( @$value ) { my $array_value = uri_escape( $_, $cgi_chars ); $query_string .= "$key=$array_value&"; } } else { $value = uri_escape( $value, $cgi_chars ); $query_string .= "$key=$value&"; }
but I'd prefer this:
if ( ! ref( $value ) ) { $value = uri_escape( $value, $cgi_chars ); $query_string .= "$key=$value&"; } else { # We have multiple values for this key foreach ( @$value ) { my $array_value = uri_escape( $_, $cgi_chars ); $query_string .= "$key=$array_value&"; } }
but note that these will die fatally and rather confusingly if given a hash reference.

The correct substitute for ref() is UNIVERSAL::isa, as in:

if( UNIVERSAL::isa( $r, "ARRAY" ) ) { # access @$r here } elsif( UNIVERSAL::isa( $r, "HASH" ) ) { # access %$r here

The problem with "HASH" eq ref($r) is that it will fail if $r is a reference to a blessed hash. Why would you want to refuse to do useful hash stuff on something just because it is blessed?

I also don't approve of using:

if( ref($r) && "$r" =~ /(^|=)HASH/ ) {
as this could break if stringification is overloaded.

Another misuse of ref() is the old:

sub method { my $self= shift; die "Invalid object" unless ref($self) eq "My::Package";
I'd probably leave such a test out completely most of the time, but if you want to do that, then isa() is the right tool:
die "Invalid object" unless UNIVERSAL::isa( $self, __PACKAGE__ ); # or die "Invalid object" unless eval { $self->isa(__PACKAGE__) };
The awkward syntax is required because you can't call a method on an unblessed reference (and calling a method on a non-reference tries to treat it like a string containing a package name, which is only what you want here for a class method such as a constructor).

By the way, how do you tell if a reference is blessed without using the fragile hack of:

if( ref($r) && "$r" =~ /=/ )
?

        - tye (but my friends call me "Tye")

Replies are listed 'Best First'.
Re: (tye)Re: ref, no, maybe?
by clintp (Curate) on Jan 11, 2001 at 05:35 UTC
    >By the way, how do you tell if a reference is blessed without using the fragile hack

    I've always used variations of:

    if ($type = ref $t) { eval { $t->isa("UNIVERSAL"); }; if ( $@=~/unblessed/ ) { print "It's a $type, not blessed.\n" } else { print "It's blessed into class $type"; } } else { print "Not a reference at all\n"; }
    with great success. It may not catch everything, and normally I'm not hunting around to figure out what's in a variable... If you don't know, you're in bigger trouble than this. :)
      This thread strongly reminds me of Readonly error on $_. In his response, merlyn displays some trick using the can method. /me thinks it can be applied here also. Saves the use of UNIVERSAL, but uses eval.
      if eval( $r->can('can')) { #we have an object } elsif ref( $r) { #we have an unblessed reference } else { #no reference at all }
      Merlyn's solution looks somewhat cleaner to me. What do you think?

      Jeroen
      I was dreaming of guitarnotes that would irritate an executive kind of guy (FZ)

        I'd rewrite your code like this:

        if( ! ref($r) ) { #no reference at all } elsif( ! eval { $r->can('can') } ) { #unblessed ref } else { #blessed ref }
        because I think the eval{} seems likely to be more expensive than the ref(). But even more important, if $r is, for example, the simple string "My::Package", then $r->can('can') will succeed (which is also a bug in merlyn's version), even if there is no package called My::Package. In fact, try "This is a test"->can('can') for fun. (:

        As for the difference between eval { $r->can('can') } and eval { $r->isa('UNIVERSAL') }, the only advantage I see to the first is fewer keystrokes. I find the latter clearer, personally. But that gave me the idea for $r->can('isa'), which I find clear (as well as short). I guess isa() screams "object" to my brain faster than can() does, not sure why.

                - tye (but my friends call me "Tye")

      Ah, thanks. I was wanting to use it to avoid the eval, so I guess I'll just keep the eval. (:

              - tye (but my friends call me "Tye")
Re: (tye)Re: ref, no, maybe?
by jlp (Friar) on Jan 11, 2001 at 06:33 UTC
    tye said:
    The problem with "HASH" eq ref($r) is that it will fail if $r is a reference to a blessed hash. Why would you want to refuse to do useful hash stuff on something just because it is blessed?
    Because a blessed hashref is not a hashref; it's an object. The whole point of an object is encapsulation; to quote The Camel, "In Perl culture, by contrast, you're expected to stay out of someone's home because you weren't invited in, not because there are bars on the windows" (pg. 278, 2nd ed.). i.e., Just because a blessed hashref is a hashref - with modifiable values - at heart doesn't mean i should manipulate it. That could break an object in spectacular ways.

      Yes, you shouldn't go messing with the hash behind my blessed ref when I give it to you as an object. But that isn't what I'm talking about.

      You have some function that does something useful with a hash. I have an object that consists of a blessed reference to a hash. Inside the code for that class, I have to mess with my own hash. Well, if this manipulation of my own hash would be made easier by using your useful function, then why shouldn't I be allowed to do that?

      Now, it'd be nice, being inside the class code, if I could get a non-blessed reference to my hash. But I can't. If that one thing were different, then I wouldn't mind the use of "HASH" eq ref($r) nearly so much.

              - tye (but my friends call me "Tye")
        I'm not sure this is a good example.

        If you're inside your own class, you ought to know if it's a blessed hash or not without calling ref on it.

        Not that I haven't violated that rule a few times... but we could all come up with outrageous examples where this doesn't work. We're just too smart for our own good; let's leave it at that.

        As the author of this hypothetical function, i take the stance that it is Not My Problem(tm). My flawless (haha!) docs clearly state that the function can take a plain scalar or a hashref; not a hash-based object. I contend that the burden falls on the you, the caller, to put the data in the condition my function expects.

        Now if Perl doesn't provide a good way for you to do that, that is another argument entirely.
Re (tilly) 2: ref, no, maybe?
by tilly (Archbishop) on Jan 11, 2001 at 06:08 UTC
    Note that even UNIVERSAL::isa is not perfect.

    What happens if someone decides to create a package called ARRAY?

    At some point you have to assume some reasonableness on the part of the programmer using your library...

      What happens if someone decides to create a package called ARRAY?

      Then they deserve to be flogged and banned from CPAN and have no right to complain if anyone's code breaks with their stuff. Did you expect anything different?

      There are other cases like this. I'd rather that there weren't, but you can't expect things to work if you have packages named SUPER or CORE either. In fact, I've never seen an all-caps package name.

      Update: mdillon was kind enough to remind that I actually have seen a few all-caps package names. He mentioned B and O and I'll add DB. These are all examples of horrid package names. Anyone who doesn't already know care to guess what any of these are for? mdillon also mentioned DBI and CGI. Well, those are reasonably named modules. But I reserve the right to shoot anyone who makes a module whose name is an ALL-CAPS word. ;)

              - tye (but my friends call me "Tye")

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://51042]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (2)
As of 2023-09-26 10:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?