Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?

lazily getting around an Exporter problem

by meraxes (Friar)
on Oct 04, 2007 at 19:46 UTC ( #642749=perlquestion: print w/replies, xml ) Need Help??
meraxes has asked for the wisdom of the Perl Monks concerning the following question:

Esteemed monks, I'm hoping someone can help me.

I'm working on an app that uses Params::Validate in a number of places. Quite a few really. What I want to do is add a method in the base class that uses Contextual::Return. Herein lies the problem:

use Params::Validate qw(validate_pos validate SCALAR ARRAYREF HASHREF) +; use Contextual::Return;

BOOM! It took me a while to realize the problem here. Params::Validate is importing SCALAR, ARRAYREF and HASHREF tags into the current namespace.... and oh look, Contextual::Return uses those as well! ARG!

Now, I know what I can use the full package name with Params::Validate to get the various "type" validators and not import them like so:

use Params::Validate qw( validate_pos validate ); use Contextual::Return; # and later in the args to validate I could do: sub fun { my $self = shift; my $args = validate( @_, { fun => Params::Validate::ARRAYREF } ); return LIST { @{ $args{fun} } } ARRAYREF { $args{fun} } SCALAR { $#{$args{fun}} } DEFAULT { croak "illegal context" } }

but I'd have to go through all the modules and fix all the use statements and expand out the type checks to Params::Validate::ARRAYREF/SCALAR/HASHREF. I might even be able to automate something to do it for me... but that scares me. It's all over the place!

Now, the easy thing would be to do the same with Contextual::Return but I realized my Perl-fu is not as strong as TheDamian's (stunning realization, I know). It doesn't play well like that and from what I can see in the source (yes, you may laugh now at a very average Perl hacker trying to understand Damian's code) there's a whole lot going on that makes just specifying out the full package name a non-option (I've tried). So that means this doesn't work:

use Params::Validate qw( validate_pos validate SCALAR ARRAYREF HASHREF + ); use Contextual::Return qw(); # later, in a subroutine # this don't work! sub funner { return Contextual::Return::LIST { qw/Larry Curly Moe/ } Contextual::Return::ARRAYREF { [ qw/Larry Curly Moe/ ] } Contextual::Return::DEFAULT { croak "listy type contexts only +" } ; }

So, I HAVE a solution, but it seems a bit of a pain to have to go through ALL the code and change every use of Params::Validate in bunches of files so I can use Contextual::Return in one place. Can anyone help me be lazy? I really don't know how else I can stop these two modules from clashing.

Well... maybe I'll ask Rolsky and Damian to arm wrestle for the namespace. :D

Thanks for the assistance.

Updatery: fixed wrong module name in code

Updatery the Second: fixed missing bracket.


Replies are listed 'Best First'.
Re: lazily getting around an Exporter problem (
by lodin (Hermit) on Oct 04, 2007 at 22:03 UTC

    Unless you really need the lazy evaluation, memoization, and what-not, you could use Want directly. Want is the module that Contextual::Return uses to figure out the context. Here's an example:

    use Want; sub funner { if (want('LIST')) { return qw/Larry Curly Moe/; } elsif (want('ARRAY')) { return [ qw/Larry Curly Moe/ ]; } else { croak("listy type contexts only"); } } print Dumper [ funner() ]; print Dumper [ @{funner()} ]; funner(); __END__ $VAR1 = [ 'Larry', 'Curly', 'Moe' ]; $VAR1 = [ 'Larry', 'Curly', 'Moe' ]; listy type contexts only at ...


Re: lazily getting around an Exporter problem
by runrig (Abbot) on Oct 04, 2007 at 22:27 UTC
    You have an unmatched "[" in your Contextual::Return::ARRAYREF line, but after fixing that, it works for me. The import routine in Contextual::Return does export "FAIL" and "FAIL_WITH" subs, so maybe you're triggering something related to that, but without an example (do you have a simple example?), I'm not sure what's going on.
Re: lazily getting around an Exporter problem
by chromatic (Archbishop) on Oct 04, 2007 at 20:37 UTC

    I haven't tested it, but I suspect that making a change like this might help:

            Contextual::Return::LIST( sub { qw/Larry Curly Moe/ } )

    If you're not getting the imports, you're not getting their prototypes and you have to be slightly more explicit.

      That actually does properly return qw/Larry Curly Moe/ if called in a LIST context but it doesn't permit me to have more than one return context.

      • If I add another one, Contextual::Return::SCALAR( sub{ 3 }), I get a syntax error.
      • If I || those two conditions on the same line (thinking if the first fails, I'll get the SCALAR context), I get told that "main::funner() cannot be called in boolean context".
      • If I call funner() in a scalar context when I just have only have the LIST contextual return clause i get this out of Data::Dumper:
      $VAR1 = { 'result' => bless( do{\(my $o = undef)}, 'Contextual::Return +::Value' ) };

      So it seems you're closer... but it's still a breakin'.

Re: lazily getting around an Exporter problem
by throop (Chaplain) on Oct 04, 2007 at 21:07 UTC
    Gentle meraxes,

    You apparently have already tried to stop Contextual::Return from exporting SCALAR ARRAYREF HASHREF and that breaks something mysterious, right?

    Ok, here are several lazy hacks that I'd try. I am a monk assuredly more foolish and confused than many. Due to my ignorance, they are unlikely to work, but some helpful monk may explain to me why not, and thus I will profit.

    Vandalism: I'd fire up EMACS on all the .pl / .pm files in Contextual::Return. I'd replace every occurence of
    SCALAR => SCALARdangit
    HASHREF => HASHREFdangit,
    and try the MAKE again.

    I'm assuming you have EMACS. No doubt someone else can supply the method for vi. (You could also do this with a perl script, or maybe even a single command line, but not me.) To invoke Tags Query Replace, you first create a tag-table. Go to the directory where you put Contextual::Return

    % etags `find . -name '*.p[lm]' -print`
    which creates a TAGS file. Go into emacs and command
    M-x Visit Tags Table M-x Tags-Query-Replace
    Do the replace three times, changing each of the offending variables. Default is that this replace is case sensitive. Save all changed files (c-x s).

    After I'd given up on that approach, I'd try
    Stealth: I'd create a dummy module, with its own package, that used Contextual::Return. The only things I'd export out of it would be the specific Contextual::Return functions that I needed, not including SCALAR ARRAYREF HASHREF. Then I'd use that dummy module in my main module.


      Thankee, but I don't think these are the approaches for me.

      The first, manually changing the module means I can't update it from CPAN anymore without having to reapply my changes. As the App I'm working on is deployed on multiple boxes, this isn't a solution that would we welcomed into the deployment process. It would also break any other modules that depend on it. I suppose I could RENAME the module to something else and do as you say to avoid the dependency problem, but I'd still be stuck manually caring for the module should it be patched.

      The second is problematic as well. I need the function I'm doing to be in the same namespace as it's an OO module and needs access to $self. I could manually pass $self into the function, but it wouldn't be a particularly good way out of this problem.

      Many thanks for the suggestions though.


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://642749]
Approved by GrandFather
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (3)
As of 2018-02-20 22:17 GMT
Find Nodes?
    Voting Booth?
    When it is dark outside I am happiest to see ...

    Results (274 votes). Check out past polls.