[comp.lang.perl] package switching

dnb@meshugge.media.mit.edu (David N. Blank) (05/01/91)

Howdy-
   I've been having a great deal of fun with eval lately (as Tom and
Randall both know) in an effort to make something I am writing really
generic.  I've hit another stumper on the subject: "package".  If I
want to be able to switch packages in a very generic way:

sub blah{
 package @_[0];
 ...
}
&blah("bleh");

How do I do it?  I have seen Randall use this idiom:

>  $ret = eval "package main; $action"

but I would prefer not to stick the entire contents of a subroutine
into one scalar variable.  Is there some magic way to expand the
scope of the package commands?  Thanks.
           Peace,
               dNb
--

 David N. Blank                      o/    \  /    \ /     /      \o   
 M.I.T. Media Laboratory            /#      ##o     #     o##      #\
 E15-473F, (617) 253-2169           / \    /  \    /o\    / |\    / \

tchrist@convex.COM (Tom Christiansen) (05/02/91)

From the keyboard of dnb@meshugge.media.mit.edu (David N. Blank):
:Howdy-
:   I've been having a great deal of fun with eval lately (as Tom and
:Randall both know) in an effort to make something I am writing really
:generic.  I've hit another stumper on the subject: "package".  If I
:want to be able to switch packages in a very generic way:
:
:sub blah{
: package @_[0];
:&blah("bleh");

If that were to work, you would really mean $_[0] -- don't want
to be supplying array contexts willy-nilly.

:How do I do it?  I have seen Randall use this idiom:
:
:>  $ret = eval "package main; $action"
:
:but I would prefer not to stick the entire contents of a subroutine
:into one scalar variable.  Is there some magic way to expand the
:scope of the package commands?  Thanks.

Magic way?  Nay.  A package declaration only affects what default
package identifiers get compiled into.  That means it doesn't 
affect the run time scoping at all,unlike local().   Thus simply 
doing 

    package main;
    eval "package $package";
    $foo = 1;

won't do you any good, as you're not compiling anything: foo will still be
in main, not $package.  You've little choice but to put your subroutine in
a variable an eval it so it gets compiled.  if that's what you're trying
to do.  This is expensive.

Now, the only time I really care about other things' packages is
when I get a filehandle passed into me as a variable.  For example,
in my open2() routine in the FAQ, I do this:

    package open2;
    # blah blah
    # force unqualified filehandles into callers' package
    local($package) = caller;
    $dad_rdr =~ s/^[^']+$/$package'$&/;
    $dad_wtr =~ s/^[^']+$/$package'$&/;

Other than that, I'm not sure why you want to dynamically switch
packages at run time instead of compile time.

--tom

merlyn@iwarp.intel.com (Randal L. Schwartz) (05/02/91)

In article <DNB.91May1115254@meshugge.media.mit.edu>, dnb@meshugge (David N. Blank) writes:
| How do I do it?  I have seen Randall use this idiom:
                               ======= s/l// # :-)
| 
| >  $ret = eval "package main; $action"
| 
| but I would prefer not to stick the entire contents of a subroutine
| into one scalar variable.  Is there some magic way to expand the
| scope of the package commands?  Thanks.

You could do something like:

package smurf;

sub whatIreallywannado {
	foo;
	foo;
	foo;
}

sub user_calls {
	eval "package $bar; &smurf'whatIreallywannado;"; die $@ if $@;
}

That way, what you are evalling is only a little piece of text instead
of the whole thing.  Does that make you happier?

package Just_another_Perl_hacker;sub a{($_)=caller;s/_/ /g;print"$_,";}&a
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Intel: putting the 'backward' in 'backward compatible'..."====/

lwall@jpl-devvax.jpl.nasa.gov (Larry Wall) (05/02/91)

In article <1991May1.200904.3640@iwarp.intel.com> merlyn@iwarp.intel.com (Randal L. Schwartz) writes:
: In article <DNB.91May1115254@meshugge.media.mit.edu>, dnb@meshugge (David N. Blank) writes:
: | How do I do it?  I have seen Randall use this idiom:
:                                ======= s/l// # :-)

No, the first l is the correct one, so you want to delete the second one:

	s/(l)l/$1/   # :-)

: | >  $ret = eval "package main; $action"
: | 
: | but I would prefer not to stick the entire contents of a subroutine
: | into one scalar variable.  Is there some magic way to expand the
: | scope of the package commands?  Thanks.
: 
: You could do something like:
: 
: package smurf;
: 
: sub whatIreallywannado {
: 	foo;
: 	foo;
: 	foo;
: }
: 
: sub user_calls {
: 	eval "package $bar; &smurf'whatIreallywannado;"; die $@ if $@;
: }
: 
: That way, what you are evalling is only a little piece of text instead
: of the whole thing.  Does that make you happier?

I don't think that's going to help, since what he really wants (I think) is to
write generic routines that work for the variables in multiple packages.
The variables of smurf'whatIreallywannado are going to be in package smurf,
not package $bar.

The package phrase is one of the few real compile-time declarations in
Perl, and there is no automatic mechanism currently in place for doing
package resolution of variables at runtime.

There are currently only three ways to do generics that I know of: 1) pass in
the scalar variables you want to tinker with via $_[0], 2) pass in the names
you want to tinker with via *foo, or 3) use eval.

However, there nothing that says you have to use a scalar variable:

	eval "package $whatever; " . <<'END';
	    foo;
	    foo;
	    foo;
	END

Actually, method 2 doesn't really need to have the variables passed in, if
you mix in a bit of method 3.  You could say:

	sub swap_a_b {
	    package swap_a_b;
	    ($pack) = caller;
	    eval "*a = *${pack}'a; *b = *${pack}'b";
	    ($a,$b) = ($b,$a);
	}

Larry Wall
lwall@netlabs.com