Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible closure bug #689

Closed
p5pRT opened this issue Oct 7, 1999 · 6 comments
Closed

Possible closure bug #689

p5pRT opened this issue Oct 7, 1999 · 6 comments

Comments

@p5pRT
Copy link

p5pRT commented Oct 7, 1999

Migrated from rt.perl.org#1587 (status was 'resolved')

Searchable as RT1587$

@p5pRT
Copy link
Author

p5pRT commented Oct 7, 1999

From gross@klizix.mpi-stuttgart.mpg.de

I am not sure if the following is really bug or just a feature
which I don't understand. In any case it has costed me hours
to figure it out (so please don't be annoyed if this is not
considered a bug).

In my opinion the output produced by 'func1' and 'func2' in the
following program should be the same... but it isn't.

Greetings,

Johannes Gross

<<<<<<< cut <<<<<<<<<<<<

my $count = 0;
my @​func1 = ();
my @​func2 = ();

foreach (1..10) {
  my ($c) = $count;

  $func1[ $count ] = sub { print "$count\n" };
  $func2[ $c ] = sub { print "$c\n" };

  $count++;
}

$count++; # now $count == 11

foreach (@​func1) { &$_ }

print "\n";

foreach (@​func2) { &$_ }

<<<<<<< cut <<<<<<<<<<<<

Output​:

11
11
11
11
11
11
11
11
11
11

0
1
2
3
4
5
6
7
8
9

Perl Info


Site configuration information for perl 5.00503:

Configured by gross at Thu Sep 23 10:58:32 MET DST 1999.

Summary of my perl5 (5.0 patchlevel 5 subversion 3) configuration:
  Platform:
    osname=dec_osf, osvers=4.0, archname=alpha-dec_osf-thread
    uname='osf1 klizx7.mpi-stuttgart.mpg.de v4.0 878 alpha '
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=define useperlio=undef d_sfio=undef
  Compiler:
    cc='cc', optimize='-O4', gccversion=
    cppflags='-pthread -std -ieee -D_INTRINSICS -I/usr/local/include -DLANGUAGE_C'
    ccflags ='-pthread -std -fprm d -ieee -D_INTRINSICS -I/usr/local/include -DLANGUAGE_C'
    stdchar='unsigned char', d_stdstdio=define, usevfork=false
    intsize=4, longsize=8, ptrsize=8, doublesize=8
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=8
    alignbytes=8, usemymalloc=n, prototype=define
  Linker and Libraries:
    ld='ld', ldflags =' -L/usr/local/lib'
    libpth=/usr/local/lib /usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /var/shlib
    libs=-ldbm -ldb -lm -lpthread -lexc
    libc=/usr/shlib/libc.so, so=so, useshrplib=true, libperl=libperl.so
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='  -Wl,-rpath,/usr/local/lib/perl5/5.00503/alpha-dec_osf-thread/CORE'
    cccdlflags=' ', lddlflags='-shared -expect_unresolved "*" -O4 -msym -s -L/usr/local/lib'

Locally applied patches:
    


@INC for perl 5.00503:
    /home/home2/gross/perl/lib/site_perl/5.005/alpha-dec_osf-thread
    /home/home2/gross/perl/lib/site_perl/5.005
    /home/home2/gross/perl/lib/site_perl/5.005/alpha-dec_osf-thread
    /usr/local/lib/perl5/5.00503/alpha-dec_osf-thread
    /usr/local/lib/perl5/5.00503
    /usr/local/lib/perl5/site_perl/5.005/alpha-dec_osf-thread
    /usr/local/lib/perl5/site_perl/5.005
    .


Environment for perl 5.00503:
    HOME=/home/home2/gross
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/home2/gross/perl:.:/home/home2/gross/binaxp:/home/home2/gross/bin:/usr/local/bin/elm2.5.1:/usr/local/grace/bin:/usr/local/bin:/usr/local/server/bin:/usr/bin:/usr/bin/X11:/usr/dt/bin:/sbin:/usr/sbin
    PERL5LIB=/home/home2/gross/perl/lib/site_perl/5.005:/home/home2/gross/perl/lib/site_perl/5.005/alpha-dec_osf-thread
    PERL_BADLANG (unset)
    SHELL=/usr/local/bin/tcsh

@p5pRT
Copy link
Author

p5pRT commented Oct 7, 1999

From @mjdominus

I am not sure if the following is really bug or just a feature
which I don't understand.

It's a feature that you don't understand.

In my opinion the output produced by 'func1' and 'func2' in the
following program should be the same... but it isn't.

Each time through the loop, a new $c variable is created, and then a
function that has a reference to it is put into @​func2. At the end of
the loop, the block is exited and the $c variable goes out of scope,
so that it is accessible only via the function that is in @​func2. At
the start of the next iteration, an entirely new $c variable is
created. Each of the ten functions in @​func2 therefore refers to a
different variable.

However. $count is in an enclosing scope which is executed only once.
So there is only one $count variable for the entire lifetime of the
program. Every function in @​func1 refers to the same $count variable.

This is exactly the behavior that closures are supposed to have.
In fact, your example could appear in a textbook as a demonstration of
how closures are supposed to work.

@p5pRT
Copy link
Author

p5pRT commented Oct 7, 1999

From @TimToady

gross@​klizx7.mpi-stuttgart.mpg.de writes​:
: I am not sure if the following is really bug or just a feature
: which I don't understand. In any case it has costed me hours
: to figure it out (so please don't be annoyed if this is not
: considered a bug).

It's not a bug, actually. All the references to $count refer to the
same variable, while all the references to $c refer to one of 10
variables instantiated by the loop. When you run the closures, they're
going to print out the current value of whichever variable they refer to.
You seem to be thinking of them more like macros, which would interpolate
the value of $count when the sub {} is executed. Closures don't do that.
Executing a sub {} just takes a snapshot of which variables are attached
to which names at the moment.

Larry

@p5pRT
Copy link
Author

p5pRT commented Oct 7, 1999

From [Unknown Contact. See original ticket]

I am not sure if the following is really bug or just a feature
which I don't understand.

It's a feature that you don't understand.

In my opinion the output produced by 'func1' and 'func2' in the
following program should be the same... but it isn't.

Each time through the loop, a new $c variable is created, and then a
function that has a reference to it is put into @​func2. At the end of
the loop, the block is exited and the $c variable goes out of scope,
so that it is accessible only via the function that is in @​func2. At
the start of the next iteration, an entirely new $c variable is
created. Each of the ten functions in @​func2 therefore refers to a
different variable.

However. $count is in an enclosing scope which is executed only once.
So there is only one $count variable for the entire lifetime of the
program. Every function in @​func1 refers to the same $count variable.

O.K., obviously I didn't correctly understood how closures work.
I sorry that I bothered you.

Actually, I was relying on Sriram Srinivasan, who wrote in
'Advanced Perl Programming'​:
  `` [...] the subroutine remembers the value that [variable name] had
  when that subroutine was _created_ ''

This is exactly the behavior that closures are supposed to have.
In fact, your example could appear in a textbook as a demonstration of
how closures are supposed to work.

Yes, may be in the second edition of 'Advanced Perl Programming'!

Thank you very much for your fast reply,

Johannes

@p5pRT
Copy link
Author

p5pRT commented Oct 7, 1999

From @mjdominus

`` [...] the subroutine remembers the value that [variable name] had
when that subroutine was _created_ ''

Sriram is mistaken, or perhaps oversimplifying. A closure doesn't
remember the *value*; it remembers the *variable*. If you change the
value that is stored in the variable, the closure will see that.

Consider this​:

  sub make_counter {
  my $n = 0;
  my $counter = sub {
  my ($action) = shift;
  if ($action eq 'print') { print $n, "\n" }
  elsif ($action eq 'increment') { ++$n }
  };
  return $counter;
  }

  $c = make_counter();

  $c->('increment');
  $c->('increment');
  $c->('print');

If $c were remembering the *value* that $n had when it was created,
it would print 0. But it doesn't remember the value; instead what it
retains is a reference to the variable $n. The ->('increment') call
changes the value of this variable, and the ->('print') call prints
out the new value.

@p5pRT
Copy link
Author

p5pRT commented Oct 7, 1999

From @gsar

On Thu, 07 Oct 1999 14​:17​:18 EDT, Mark-Jason Dominus wrote​:

`` [...] the subroutine remembers the value that [variable name] had
when that subroutine was _created_ ''

Sriram is mistaken, or perhaps oversimplifying. A closure doesn't
remember the *value*; it remembers the *variable*. If you change the
value that is stored in the variable, the closure will see that.

Another way to think about it is that closures remember aliases
to variables, not *copies* thereof.

Sarathy
gsar@​activestate.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant