BrowserUk has asked for the wisdom of the Perl Monks concerning the following question:

The title says it all pretty much. I'm currently using a instantiator sub to generate a utility sub that closes over some instance state:

sub instantiator{ my $state = @_; return sub{ return #some function of state and @_; } } ... my $utility = instantiator( 'init' ); ... $utility->( args );

That works fine for my purpose, but now I'd like to be able to chose/adjust the type (scalar/array/hash) of the closure when calling the utility sub, but each instance of the utility sub needs it's own independant closure. Any thoughts?

Update: I should have been clearer. The type of the closure would need to change during the lifetime of the utility function in response to arguments passed when calling it, so I can't generate an appropriate closure when it is instantiated.

So the question is really: Is there any way for a sub to generate a new closure--in my terms, a piece of cross-call persistant storage, lexically visible only from within the sub so there is no conflict between different instances of the sub--from within that sub?

Normally not, but I'm wondering if there is some B::* or Devel::* trick that can generate a closure on-the-fly?


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail

Replies are listed 'Best First'.
Re: Can a sub generate a closure from within itself?
by Fletch (Bishop) on Apr 24, 2004 at 01:05 UTC

    Each time you call the anonymous sub constructor it creates a new closure already. What exactly do you mean by "adjust the type"? Something like this perhaps?

    sub instantiator { my $state = shift; if( $state eq 'scalar' ) { return sub { ... something scalarly ... } } elsif( $state eq 'array' ) { return sub { ... an array of possibilities ... } } else { # hash return sub { ... make a complete hash of things ... } } }

    Update: Aaah. You can overload &{}, so perhaps you could use that to do what you want (i.e. you return an object rather than a sub ref which has an overloaded &{} that examines whatever and then behaves differently).

    package Foo; use overload "&{}" => \&deref; sub instatntiate { my $state = shift; # ... return bless { _original_code => sub { ... } } } sub deref { my $self = shift; ## examine $self and create $newsub using $self->{_original_code} $newsub->( @_ ); } package main; $f = Foo::instantiate( ... ); $f->( $a, $b, $c );
Re: Can a sub generate a closure from within itself?
by broquaint (Abbot) on Apr 24, 2004 at 01:59 UTC
    If I understand the OP correctly, you can add a scope to instantiator I believe you can get the desired effect e.g
    { my $both; sub inst { my $inner = shift; $both .= "xxx "; return sub { printf "both: %s\nmine: %s\n", $both, $inner; }; } } for( 1 .. 3 ) { my $n = inst("[$_]"); &$n; } __output__ both: xxx mine: [1] both: xxx xxx mine: [2] both: xxx xxx xxx mine: [3]
    So with the additional scope both inst and the newly generated sub will refer to the same lexical variable ($both).
    HTH

    _________
    broquaint

Re: Can a sub generate a closure from within itself?
by Zaxo (Archbishop) on Apr 24, 2004 at 01:08 UTC

    How about always accepting the arguments as an array?

    sub instantiator { my @state = @_; sub { # work on @state } }
    I think that working on @_ gives you less of a closure than you suggest.

    To retain a data structure through the argument list of a sub, you need to pass a reference to it (you knew that!). Then you can do a perly case switch on ref $_[0] to return a sub specialized to the argument type.

    After Compline,
    Zaxo

Re: Can a sub generate a closure from within itself?
by TomDLux (Vicar) on Apr 24, 2004 at 01:05 UTC
    my $utility = instantiator( \&instantiator );

    Not what you were looking for, but I couldn't resist the opportunity.

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

Re: Can a sub generate a closure from within itself?
by simonm (Vicar) on Apr 24, 2004 at 01:02 UTC
    It's not clear to me what you're asking.

    If you want to return one of several different styles of closure, you'll need to have closure generators for each of them, or a conditional block that determines which to return.

Re: Can a sub generate a closure from within itself?
by dave_the_m (Monsignor) on Apr 24, 2004 at 11:53 UTC
    Even with your update, its still totally unclear what you're asking for. What do you mean by the 'type' of a closure?

      Sorry, I thought that ...chose/adjust the type (scalar/array/hash) of the closure... was fairly clear. The problem was that during the life of the utility sub, as a result of it's use, it may be beneficial to change the form in which the retained state is stored.

      For example, when the sub is generated, the parameters supplied to the instantiator might indicate that an array would be the best representation. However, in use, the sub might determine that the data being retained is sparse data and therefore it makes more sense to revert the storage format to using a hash rather than an array. The reverse situation might also apply.

      I was looking for some way for the utitlity sub to change the type (scalar/array/hash) of the closure on the fly. It turns out that I was simply looking for a complicated solution when a simple one would suffice. Zaxo++ pointed out (via /msg) that if the closure defined at construction time is a simple scalar I can later assign a reference to either an array or a hash as I decide I need and that addresses the problem.

      I'm embarrassed that I didn't see the obvious myself before posting, but I didn't:(


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail