in reply to (tye)Re: Eval and $@% ...
in thread Eval and $@% ...

The point to this exercise is to have a function that allows a user to pass in either listrefs or lists to a given function foo(). foo() would then call a function called coerce_args(), passing in what type foo() is expecting (ARRAY, HASH, etc) and @_. coerce_args() would then return back the correct form, if possible.

I agree that it sounds like there are much better ways of accomplishing this, namely to require the user of foo() to pass in either a hash or hashref. But, once I started down this path, I wanted to finish it in the best Perl-ish fashion.

Why not use stringy eval? If not stringy eval, then could I use block eval? I'm probably not going to implement something like this (cause I'm going to take your advice, tye, and convert the lists to listrefs), but now I want to find out if what I'm trying is possible and, if not, why not.

Replies are listed 'Best First'.
(tye)Re2: Eval and $@% ...
by tye (Sage) on Jun 19, 2001 at 22:33 UTC

    So you want something like this to work:

    my %mapping = ( ARRAY => '@', HASH => '%', SCALAR => '$', GLOB => '*', REF => '$', CODE => '&' ); return eval $mapping{$type}.'{$_[0]}';
    Note that your old code used eval "$mapping{$type}{$_[0]}" which ends up trying to run code like "$SCALAR(0xbadf00)", which doesn't do much good.

    My code above expands out to:

    return @{$_[0]} if $type eq "ARRAY"; return %{$_[0]} if $type eq "HASH"; return ${$_[0]} if $type eq "SCALAR"; return *{$_[0]} if $type eq "GLOB"; return ${$_[0]} if $type eq "REF"; return &{$_[0]} if $type eq "CODE";
    but a more correct version of this useless code would be:
    my( $ref )= @_; $ref= $$ref while UNIVERSAL::isa($ref,"REF"); return @$ref if UNIVERSAL::isa($ref,"ARRAY"); return %$ref if UNIVERSAL::isa($ref,"HASH"); return $$ref if UNIVERSAL::isa($ref,"SCALAR"); return &$ref() if UNIVERSAL::isa($ref,"CODE"); return $$ref,@$ref,%$ref if UNIVERSAL::isa($ref,"GLOB");
    Now if you really want to try to use stringy eval for this, feel free. It seems like a waste to me. And, no, block eval would do you no good in this, unless you use it to make some fatal errors non-fatal (which is what block eval is for), such as using @$ref when $ref is a reference to something other than array.

            - tye (but my friends call me "Tye")
      Ok ... so what I was missing was the {}. And, I see why that would make it work. That would fail if you put use strict 'refs' in the eval, right?

      Now, I'm curious as to why you keep saying that string eval is a waste. You've been very adamant about this ... why?

        I see your code as similar to rewriting:

        if( something ) { do_this(); } else { do_that(); }
        as eval( "do_th" . (something?"is":"at") . "()" ); Sure, you can make that work. But what is the point? Stringy eval has to recompile code every time it is run. Unless you are very careful, it can hide serious errors (and you weren't careful in your original code and neither was I in any of the code using eval that I posted in this thread). It can leak memory.

        I just don't see any point to you using stringy eval here. I only use stringy eval as a last resort. Reaching for that tool too quickly is a common mistake when learning Perl so most experienced Perl programmers discourage it. It is easy to use stringy eval incorrectly.

        Ok ... so what I was missing was the {}.

        Huh? I didn't add any braces. The main difference between your use of eval and mine was that you expanded the value of $_[0] (by using double quote) while I passed eval the literal string '$_[0]'. I left the {} out of the non-eval code since I was using $ref and not $_[0] so the braces aren't needed.

        That would fail if you put use strict 'refs' in the eval, right?

        No, my code was using hard reference (not symbolic references) and so use strict 'refs' wouldn't have a problem with it (except, perhaps, for the GLOB dereferencing; I don't use GLOBs as references so I don't track the details of doing that). Your code would only work if $_[0] contained a variable name, causing the eval to use symbolic references.

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

        That would fail if you put use strict 'refs' in the eval, right?

        The eval should inherit use strict 'refs'; from the parent. The problem is that you wouldn't notice it failing unless you checked $@.

        Now, I'm curious as to why you keep saying that string eval is a waste.

        String eval is usually a waste when you can do without because string eval requires: that the code be compiled each time (a signifigant amount of time, probably), and that you be careful to not make mistakes like having variables expanded before the eval like you did. The first problem is the most signifigant. When you have to parse some perl code, rather then just run the appropriate if or item from a dispatch table it's usually much slower, something you'll notice especially when it's done a lot. Therefore when there are ways to do without it's (usually) best to take them. In fact avoiding evals of strings is probably the reason symrefs where included in the first place.