in reply to Re: Ways to implement a closure
in thread Ways to implement a closure

Some people define any data structure that closes over an environment to be a closure. (Some people don't.) In which case this is a closure:
sub demonstration { my $demonstration_data = { demo_data => shift }; return { demonstration_data => $demonstration_data, }; }
because $demonstration_data knows to stick around even though the function ended.

An example of a language where the data would not be expected to stick around is C - if you don't watch out the demonstration data would be stuck on the stack, and a few function calls later would not be there to be found.

Replies are listed 'Best First'.
Re^3: Ways to implement a closure
by ikegami (Patriarch) on Oct 15, 2004 at 19:48 UTC

    I see. Well there you go, yet another way!

    Nit: that code works in C (given some imagined Hash class), and the data does stick around:

    Hash<...>* demonstration<...>(... a) Hash<...>* hash_ptr = new Hash<...>("demo_data", a); return new Hash<...>( "demonstration_data", hash_ptr ); }

    I think you meant this:

    sub demonstration { my %demonstration_data = ( demo_data => shift ); return { demonstration_data => \%demonstration_data, }; }

    Which wouldn't work in C:

    Hash<...>* demonstration<...>(... a) Hash<...> hash("demo_data", a); return new Hash<...>( "demonstration_data", \hash XXX BUG ); }
      C does not have templates OR classes! (Not that this is a bad thing...)
        Oops, I often call C++ by the wrong name. But while the code was C++, the argument does work for C as well. Here's the C version of the C++ snippets:
        HASH* demonstration(void* p) HASH* hash_ptr = hash_new() hash_add(hash_ptr, "demo_data", p); HASH* outer_hash_ptr = hash_new(); hash_add(outer_hash_ptr, "demonstration_data", hash_ptr); return outer_hash_ptr; }

        and

        HASH* demonstration(void* p) HASH hash; hash_init(&hash); hash_add(&hash, "demo_data", p); HASH* outer_hash_ptr = hash_new(); hash_add(outer_hash_ptr, "demonstration_data", &hash); XXX BUG return outer_hash_ptr; }
Re^3: Ways to implement a closure
by ihb (Deacon) on Oct 16, 2004 at 13:42 UTC

    I disagree with it being a closure, even using the broader definition of a closure.

    If you mean that a closure (in Perl) is anything that increases reference count anywhere, then I understand you, but must say that that's the broadest definition of a closure I've ever heard. With that definition though, this below would be equally much a closure:

    sub demonstration { return { demonstration_data => { demo_data => shift }, }; }
    If you mean that it's a closure because you use a lexical variable in there, I disagree that it's a closure. Adding that it must close over/bind a lexical environment (which is what some people think and I think you are referring to), the returned hash reference is not a closure because it doesn't bind anything in its lexical surrounding.

    $demonstration_data knows to stick around even though the function ended

    In fact, it doesn't. The value it references knows to stay around. The instance of $demonstration_data doesn't stay around itself. Changing $demonstration_data after the latter hash reference is created does not change anything in it. This code below illustrates that.

    sub demonstration { my $demonstration_data = { demo_data => shift }; return { demonstration_data => $demonstration_data, }, sub { $demonstration_data = $_[0]; }; } my ($data, $modifier) = demonstration('foo'); print Dumper $data; $modifier->({ a => 1 }); print Dumper $data; __END__ $VAR1 = { 'demonstration_data' => { 'demo_data' => 'foo' } }; $VAR1 = { 'demonstration_data' => { 'demo_data' => 'foo' } };
    The hash value holds a RV that references the same PVHV as $demonstration_data references. $demonstration_data and demonstration()->{demonstration_data} are different RVs, but reference the same value.

    One can argue that the reference operator \ can act as a closure. Looking at lexical variables, it binds the current instance of the lexical variable. The similarity between \ and an anonymous closure and the contrast to other scalar types is demonstrated below.

    sub foo { my $foo = shift; return $foo, [ $foo ], \$foo, sub :lvalue { $foo } ; } my ($noref, $aref, $sref, $cref) = foo('foo'); my $dump = sub { print "\$noref: $noref\n", "\$aref : $aref->[0]\n", "\$sref : $$sref\n", "\$cref : " . $cref->() . "\n\n"; }; $dump->(); $noref = 'bar'; # Changes the first value. $dump->(); $aref->[0] = 'baz'; # Changes the second value. $dump->(); $$sref = 'zip'; # Changes the two last values. $dump->(); $cref->() = 'zap'; # Changes the two last values. $dump->(); __END__ Long output, run it yourself.
    Your hash above is no more closure than the array reference here. Neither the hash or the array bind the variables used in it - they store copies of the values the variables hold. In your case the value is a reference but that is no different from any other non-reference value except that it increases a ref count somewhere when copied, but that has nothing to do with closing over any environment.

    One may argue that the array reference itself is a closure and so may be, if you look at [ LIST ] as syntactic sugar for do { my @foo = LIST; \@foo }. That does not make any data structure keeping a reference to the anonymous array a closure though. It's just a data structure that holds a closure.

    ihb

    Read argumentation in its context!