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

Trying to get a handle on closures and ran into an obstacle. It's probably pretty simple to get around, but I haven't figured it out:
#!/usr/bin/perl -w use strict; doStuff(); sub doStuff { my $toggle = initToggle( 1 ); for ( 1..6 ) { print &$toggle . "\n" }; } sub initToggle { my $x = shift; return sub { $x ^= 1; return $x }; }
The above snippet happily prints 0,1,0,1,0,1 on successive lines. However, every time I try to pass $toggle to a subroutine from doStuff(), it gets turned to a scalar. How do I get it to pass itself as a code reference? I'm using this to encapsulate some functionality in a module and I don't want to declare $toggle globally.

Further, I'm not looking for a better way to toggle values. This is just something I'm doing to understand closures better.

Cheers,
Ovid

Join the Perlmonks Setiathome Group or just go the the link and check out our stats.

Replies are listed 'Best First'.
Re: Closures question
by btrott (Parson) on Oct 20, 2000 at 01:29 UTC
    Strange. I just tried this:
    sub doStuff { my $toggle = initToggle( 1 ); blah($toggle); } sub blah { my $t = shift; for ( 1..6 ) { print &$t . "\n" } }
    and it worked just fine.

    Are you doing something differently than the above? Are you storing your code ref as the key in a hash, then pulling it back out and trying to use it again? If so the ref will be stringified and thereafter useless as a code ref. In fact, any stringification of your reference will render it unusable.

      Ok but then "use strict" would report:
      can't use string ("CODE(_SOMETHING_)") as a subroutine ref while "strict refs" in use
        Well yes, of course. But just to clarify, I don't think Ovid actually gave us an error message in his original post. So he very well *could* have received such an error--who knows.
(Ovid) Re: Closures question
by Ovid (Cardinal) on Oct 20, 2000 at 02:10 UTC

    Ho, ho! Hah! Chuckle...laugh... snicker... sob.

    Behold, ye mighty, and despair! I am OVID, the idiot!!!

    btrott put me on the right path. I wasn't storing the coderef in a hash key, but I had accidentally stringified it.

    Basically, I was trying to abstract out bits of synthetic code in my program so that the main parts were nice and clear. In the process, I forgot to remove one, teeny, weeny line:

    $toggle ^= 1;
    Needless to say, do a bitwise XOR on a coderef is not my most brilliant coding.

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just go the the link and check out our stats.

Re: Closures question
by chromatic (Archbishop) on Oct 20, 2000 at 01:51 UTC
    A reference to an anonymous subroutine (which is part of what makes a closure a closure) is just like a normal reference. As btrott says, it can be stringified if you're not super careful.

    Handle it just like you would a reference to a hash or to a scalar or to an array -- pass the scalar holding the reference around, and you'll be fine.

    If you accidentally stringify it, you can generally do something like: my $sub = \&{ $stringified_sub_ref }; to get it back, but your sense of good taste and squeamishness might prevent that, along with strict.

    I also generally call subrefs with merlyn's dereferencing arrow trick: $sub->();

Re: Closures question
by cianoz (Friar) on Oct 20, 2000 at 01:29 UTC
    i'm not shure to understand what you say but:
    #!/usr/bin/perl -w use strict; doStuff(); sub doStuff { my $toggle = initToggle( 1 ); for ( 1..6 ) { print &$toggle . "\n" }; print "now something different...\n"; &othersub($toggle); } sub initToggle { my $x = shift; return sub { $x ^= 1; return $x }; } sub othersub { my $c = shift; print ref($c) ."\n"; for ( 1..6 ) { print &$c . "\n" }; }
    prints
    0 1 0 1 0 1 now something different... CODE 0 1 0 1 0 1
    as expected... (perl 5.005_03 i386-linux)