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

Inconsistent parsing of parenthesized expressions involving masked outer variables with local/my #15461

Closed
p5pRT opened this issue Jul 19, 2016 · 6 comments

Comments

@p5pRT
Copy link

p5pRT commented Jul 19, 2016

Migrated from rt.perl.org#128669 (status was 'rejected')

Searchable as RT128669$

@p5pRT
Copy link
Author

p5pRT commented Jul 19, 2016

From shmem@qwurx.de

Created by shmem@qwurx.de

Masking variables is inconsistent between local() and my().

#!/usr/bin/perl -l
use strict; use warnings;
our $foo = "foo"; do { (local $foo = $foo) and $foo =~ s/o/i/g; print $foo };print $foo;
{ my $foo = "foo"; do { ( my $foo = $foo) and $foo =~ s/o/i/g; print $foo };print $foo; }
__END__
fii
foo
foo
fii

In the line involving local(), the inner variable is modified.
In the line involving my()..., the outer variable is modified.

Looks like local is IMMEDIATE, while my is DEFERRED (in forth speak ;-).
Is there any rationale for this behavior?

I consider this a bug or, at least, a breakage of orthogonality.

Perl Info

Flags:
     category=core
     severity=low

Site configuration information for perl 5.20.2:

Configured by Debian Project at Mon May 23 22:59:54 UTC 2016.

Summary of my perl5 (revision 5 version 20 subversion 2) configuration:

   Platform:
     osname=linux, osvers=3.16.0-4-amd64, archname=x86_64-linux-gnu-thread-multi
     uname='linux themisto 3.16.0-4-amd64 #1 smp debian 3.16.7-ckt25-2 (2016-04-08) x86_64 gnulinux '
     config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN -D_FORTIFY_SOURCE=2 -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Dldflags= -Wl,-z,relro -Dlddlflags=-shared -Wl,-z,relro -Dcccdlflags=-fPIC -Darchname=x86_64-linux-gnu -Dprefix=/usr -Dprivlib=/usr/share/perl/5.20 -Darchlib=/usr/lib/x86_64-linux-gnu/perl/5.20 -Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/x86_64-linux-gnu/perl5/5.20 -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl/5.20.2 -Dsitearch=/usr/local/lib/x86_64-linux-gnu/perl/5.20.2 -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Dsiteman3dir=/usr/local/man/man3 -Duse64bitint -Dman1ext=1 -Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Ud_ualarm -Uusesfio -Uusenm -Ui_libutil -Uversiononly -DDEBUGGING=-g -Doptimize=-O2 -Duseshrplib -Dlibperl=libperl.so.5.20.2 -des'
     hint=recommended, useposix=true, d_sigaction=define
     useithreads=define, usemultiplicity=define
     use64bitint=define, use64bitall=define, uselongdouble=undef
     usemymalloc=n, bincompat5005=undef
   Compiler:
     cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
     optimize='-O2 -g',
     cppflags='-D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pipe -I/usr/local/include'
     ccversion='', gccversion='4.9.2', gccosandvers=''
     intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
     d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
     ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
     alignbytes=8, prototype=define
   Linker and Libraries:
     ld='cc', ldflags =' -fstack-protector -L/usr/local/lib'
     libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed /usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib
     libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt
     perllibs=-ldl -lm -lpthread -lc -lcrypt
     libc=libc-2.19.so, so=so, useshrplib=true, libperl=libperl.so.5.20
     gnulibc_version='2.19'
   Dynamic Linking:
     dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
     cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib -fstack-protector'

Locally applied patches:
     DEBPKG:debian/cpan_definstalldirs - Provide a sensible INSTALLDIRS default for modules installed from CPAN.
     DEBPKG:debian/db_file_ver - http://bugs.debian.org/340047 Remove overly restrictive DB_File version check.
     DEBPKG:debian/doc_info - Replace generic man(1) instructions with Debian-specific information.
     DEBPKG:debian/enc2xs_inc - http://bugs.debian.org/290336 Tweak enc2xs to follow symlinks and ignore missing @INC directories.
     DEBPKG:debian/errno_ver - http://bugs.debian.org/343351 Remove Errno version check due to upgrade problems with long-running processes.
     DEBPKG:debian/libperl_embed_doc - http://bugs.debian.org/186778 Note that libperl-dev package is required for embedded linking
     DEBPKG:fixes/respect_umask - Respect umask during installation
     DEBPKG:debian/writable_site_dirs - Set umask approproately for site install directories
     DEBPKG:debian/extutils_set_libperl_path - EU:MM: set location of libperl.a under /usr/lib
     DEBPKG:debian/no_packlist_perllocal - Don't install .packlist or perllocal.pod for perl or vendor
     DEBPKG:debian/prefix_changes - Fiddle with *PREFIX and variables written to the makefile
     DEBPKG:debian/fakeroot - Postpone LD_LIBRARY_PATH evaluation to the binary targets.
     DEBPKG:debian/instmodsh_doc - Debian policy doesn't install .packlist files for core or vendor.
     DEBPKG:debian/ld_run_path - Remove standard libs from LD_RUN_PATH as per Debian policy.
     DEBPKG:debian/libnet_config_path - Set location of libnet.cfg to /etc/perl/Net as /usr may not be writable.
     DEBPKG:debian/mod_paths - Tweak @INC ordering for Debian
     DEBPKG:debian/module_build_man_extensions - http://bugs.debian.org/479460 Adjust Module::Build manual page extensions for the Debian Perl policy
     DEBPKG:debian/prune_libs - http://bugs.debian.org/128355 Prune the list of libraries wanted to what we actually need.
     DEBPKG:fixes/net_smtp_docs - [rt.cpan.org #36038] http://bugs.debian.org/100195 Document the Net::SMTP 'Port' option
     DEBPKG:debian/perlivp - http://bugs.debian.org/510895 Make perlivp skip include directories in /usr/local
     DEBPKG:debian/deprecate-with-apt - http://bugs.debian.org/747628 Point users to Debian packages of deprecated core modules
     DEBPKG:debian/squelch-locale-warnings - http://bugs.debian.org/508764 Squelch locale warnings in Debian package maintainer scripts
     DEBPKG:debian/skip-upstream-git-tests - Skip tests specific to the upstream Git repository
     DEBPKG:debian/patchlevel - http://bugs.debian.org/567489 List packaged patches for 5.20.2-3+deb8u5 in patchlevel.h
     DEBPKG:debian/skip-kfreebsd-crash - http://bugs.debian.org/628493 [perl #96272] Skip a crashing test case in t/op/threads.t on GNU/kFreeBSD
     DEBPKG:fixes/document_makemaker_ccflags - http://bugs.debian.org/628522 [rt.cpan.org #68613] Document that CCFLAGS should include $Config{ccflags}
     DEBPKG:debian/find_html2text - http://bugs.debian.org/640479 Configure CPAN::Distribution with correct name of html2text
     DEBPKG:debian/perl5db-x-terminal-emulator.patch - http://bugs.debian.org/668490 Invoke x-terminal-emulator rather than xterm in perl5db.pl
     DEBPKG:debian/cpan-missing-site-dirs - http://bugs.debian.org/688842 Fix CPAN::FirstTime defaults with nonexisting site dirs if a parent is writable
     DEBPKG:fixes/memoize_storable_nstore - [rt.cpan.org #77790] http://bugs.debian.org/587650 Memoize::Storable: respect 'nstore' option not respected
     DEBPKG:debian/regen-skip - Skip a regeneration check in unrelated git repositories
     DEBPKG:fixes/regcomp-mips-optim - [perl #122817] http://bugs.debian.org/754054 Downgrade the optimization of regcomp.c on mips and mipsel due to a gcc-4.9 bug
     DEBPKG:debian/makemaker-pasthru - http://bugs.debian.org/758471 Pass LD settings through to subdirectories
     DEBPKG:fixes/perldoc-less-R - [rt.cpan.org #98636] http://bugs.debian.org/758689 Tell the 'less' pager to allow terminal escape sequences
     DEBPKG:fixes/pod_man_reproducible_date - http://bugs.debian.org/759405 Support POD_MAN_DATE in Pod::Man for the left-hand footer
     DEBPKG:fixes/io_uncompress_gunzip_inmemory - http://bugs.debian.org/747363 [rt.cpan.org #95494] Fix gunzip to in-memory file handle
     DEBPKG:fixes/socket_test_recv_fix - http://bugs.debian.org/758718 [perl #122657] Compare recv return value to peername in socket test
     DEBPKG:fixes/hurd_socket_recv_todo - http://bugs.debian.org/758718 [perl #122657] TODO checking the result of recv() on hurd
     DEBPKG:fixes/regexp-performance - [0fa70a0] http://bugs.debian.org/777556 [perl #123743] simpify and speed up /.*.../ handling
     DEBPKG:fixes/failed_require_diagnostics - http://bugs.debian.org/781120 [perl #123270] Report inaccesible file on failed require
     DEBPKG:fixes/array-cloning - http://bugs.debian.org/779357 [perl #124127] [902d169] fix cloning arrays with unused elements
     DEBPKG:fixes/perldb-threads - http://bugs.debian.org/779357 [perl #124127] [41ef2c6] lib/perl5db.pl: Restore noop lock prototype
     DEBPKG:fixes/CVE-2015-8607_file_spec_taint_fix - ensure File::Spec::canonpath() preserves taint
     DEBPKG:fixes/encode-unicode-bom - http://bugs.debian.org/798727 [rt.cpan.org #107043] Address https://rt.cpan.org/Public/Bug/Display.html?id=107043
     DEBPKG:debian/encode-unicode-bom-doc - http://bugs.debian.org/798727 Document Debian backport of Encode::Unicode fix
     DEBPKG:debian/kfreebsd-softupdates - http://bugs.debian.org/796798 Work around Debian Bug#796798
     DEBPKG:fixes/CVE-2016-2381_duplicate_env - remove duplicate environment variables from environ
     DEBPKG:debian/debugperl-compat-fix - [perl #127212] http://bugs.debian.org/810326 Disable PERL_TRACK_MEMPOOL for debugging builds
     DEBPKG:fixes/CVE-2015-8853_regexp_hang - http://bugs.debian.org/821848 [perl #123562] PATCH [perl #123562] Regexp-matching "hangs"
     DEBPKG:fixes/utf8_regexp_crash - http://bugs.debian.org/820328 [perl #124109] save_re_context(): do "local $n" with no PL_curpm
     DEBPKG:fixes/regcomp_whitespace_fix - http://bugs.debian.org/820328 [perl #124109] Perl_save_re_context(): re-indent after last commit
     DEBPKG:fixes/5.20.3/eval_label_crash - http://bugs.debian.org/822336 [perl #123652] eval {label:} crash
     DEBPKG:fixes/5.20.3/preserve_record_separator - http://bugs.debian.org/822336 [perl #123218] "preserve" $/ if set to a bad value
     DEBPKG:fixes/5.20.3/test_count_base_rs - http://bugs.debian.org/822336 Fix test count in t/base/rs.t
     DEBPKG:fixes/5.20.3/remove_get_magic - http://bugs.debian.org/822336 [perl #123739] Remove get-magic from $/
     DEBPKG:fixes/5.20.3/speed_up_scalar_g - http://bugs.debian.org/822336 [perl #123202] speed up scalar //g against tainted strings
     DEBPKG:fixes/5.20.3/accidental_all_features - http://bugs.debian.org/822336 Stop $^H |= 0x1c020000 from enabling all features
     DEBPKG:fixes/5.20.3/multidimensional_arrays_utf8 - http://bugs.debian.org/822336 [perl #124113] Make check for multi-dimensional arrays be UTF8-aware
     DEBPKG:fixes/5.20.3/unquoted_utf8_heredoc_terminators - http://bugs.debian.org/822336 Allow unquoted UTF-8 HERE-document terminators
     DEBPKG:fixes/5.20.3/parentheses_ambiguous_warning_utf8_functions - http://bugs.debian.org/822336 Fix "...without parentheses is ambuguous" warning for UTF-8 function names
     DEBPKG:fixes/5.20.3/leak_namepv_copy - http://bugs.debian.org/822336 [perl #123786] don't leak the temp utf8 copy of namepv
     DEBPKG:fixes/5.20.3/h2ph_hex_constants - http://bugs.debian.org/822336 h2ph: correct handling of hex constants for the preamble
     DEBPKG:fixes/5.20.3/leftbracket_XTERMORDORDOR - http://bugs.debian.org/822336 [perl #123711] Fix crash with 0-5x-l{0}
     DEBPKG:fixes/5.20.3/fatalize_warnings_unwinding - http://bugs.debian.org/822336 [perl #123398] don't fatalize warnings during unwinding (#123398)
     DEBPKG:fixes/5.20.3/setpgrp - http://bugs.debian.org/822336 =?UTF-8?q?Don=E2=80=99t=20treat=20setpgrp($nonzero)=20as=20setpgr?= =?UTF-8?q?p(1)?=
     DEBPKG:fixes/5.20.3/death_unwinding_crash - http://bugs.debian.org/822336 [perl #124156] RT #124156: death during unwinding causes crash
     DEBPKG:fixes/5.20.3/stashpvn_crash - http://bugs.debian.org/822336 [perl #125541] Fix crash with %::=(); J->${\"::"}
     DEBPKG:fixes/5.20.3/possessive_quantifier - http://bugs.debian.org/822336 [perl #125825] PATCH: [perl 125825] {n}+ possessive quantifier broken
     DEBPKG:fixes/5.20.3/quoted_code_crash - http://bugs.debian.org/822336 [perl #123712] Fix /$a[/ parsing
     DEBPKG:fixes/5.20.3/checking_sub_inwhat - http://bugs.debian.org/822336 [perl #123712] Don't check sub_inwhat
     DEBPKG:fixes/5.20.3/yylex_loop - http://bugs.debian.org/822336 Fix hang with "@{"
     DEBPKG:fixes/5.20.3/docs/op - http://bugs.debian.org/822336 Fix apidocs for OP_TYPE_IS(_OR_WAS) - arguments separated by |, not ,.
     DEBPKG:fixes/5.20.3/docs/encoding - http://bugs.debian.org/822336 perlpodspec: Corrections/adds to detecting =encoding
     DEBPKG:fixes/5.20.3/docs/SvPV_set - http://bugs.debian.org/822336 improve SvPV_set's docs, it really shouldn't be public API
     DEBPKG:fixes/5.20.3/docs/autodie - http://bugs.debian.org/822336 Fix warning message regarding "use autodie" and "use open".
     DEBPKG:fixes/5.20.3/docs/autodie_2_26 - http://bugs.debian.org/822336 perlunicook: Note that autodie >= 2.26 should be okay with "use open".
     DEBPKG:fixes/5.20.3/docs/setenv - http://bugs.debian.org/822336 Fix setenv() replacement documentation in perlclib
     DEBPKG:fixes/5.20.3/docs/clib_caution - http://bugs.debian.org/822336 perlhacktips: Add caution about clib ptr returns to static memory
     DEBPKG:fixes/5.20.3/docs/perlunicook_typos - http://bugs.debian.org/822336 Fix minor code typos in perlunicook
     DEBPKG:fixes/5.20.3/docs/ook_example - http://bugs.debian.org/822336 [perl #122322] Update OOK example in perlguts
     DEBPKG:fixes/5.20.3/docs/study_noop - http://bugs.debian.org/822336 perlfunc: mention that study() is currently a noop


@INC for perl 5.20.2:
     /home/shmem/perl5/lib/perl5/5.20.2/x86_64-linux-gnu-thread-multi
     /home/shmem/perl5/lib/perl5/5.20.2
     /home/shmem/perl5/lib/perl5/x86_64-linux-gnu-thread-multi
     /home/shmem/perl5/lib/perl5
     /home/shmem/comp/perl/lib
     /etc/perl
     /usr/local/lib/x86_64-linux-gnu/perl/5.20.2
     /usr/local/share/perl/5.20.2
     /usr/lib/x86_64-linux-gnu/perl5/5.20
     /usr/share/perl5
     /usr/lib/x86_64-linux-gnu/perl/5.20
     /usr/share/perl/5.20
     /home/shmem/perl5/lib/perl5/5.20.0
     /usr/local/lib/site_perl
     .


Environment for perl 5.20.2:
     HOME=/home/shmem
     LANG=en_GB.utf8
     LANGUAGE=en_GB:en
     LD_LIBRARY_PATH (unset)
     LOGDIR (unset)
     PATH=/home/shmem/perl5/bin:/sbin:/usr/sbin:/home/shmem/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:.
     PERL5LIB=/home/shmem/perl5/lib/perl5:/home/shmem/comp/perl/lib
     PERL_BADLANG (unset)
     PERL_LOCAL_LIB_ROOT=/home/shmem/perl5
     PERL_MB_OPT=--install_base "/home/shmem/perl5"
     PERL_MM_OPT=INSTALL_BASE=/home/shmem/perl5
     SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Jul 19, 2016

From @mjdominus

#!/usr/bin/perl -l
use strict; use warnings;
our $foo = "foo"; do { (local $foo = $foo) and $foo =~ s/o/i/g; print $foo };print $foo;
{ my $foo = "foo"; do { ( my $foo = $foo) and $foo =~ s/o/i/g; print $foo };print $foo; }
__END__
fii
foo
foo
fii

In the line involving local(), the inner variable is modified.
In the line involving my()..., the outer variable is modified.

Your expectation of 'my' is incorrect. Consider

  my $x = 1;
  { my $x = $x + 1;
  print $x } # prints 2

Why does this work? `my` declares a second `$x` variable, but the
name `x` does not refer to this new variable until _after_ the
statement containing the declaration.

The two '$x' tokens in the second line refer to two different
variables​: the $x on the left is the new variable, and the $x on the
right is the old variable. Then the $x in the 'print' is the new
variable. If it didn't work this way, then the $x on the right would
have an undefined value, the + would produce an undefined-value
warning, and the print would print 1.

In your example, the same thing is happening​:

{ my $foo = "foo";
do { ( my $foo = $foo) and $foo =~ s/o/i/g;
print $foo };
print $foo; }

There are three $foo names in the second line. The `my $foo` is
declaring a new variable, but the name '$foo' will not refer to it
until the following statement. This means that the second $foo refers
to the old variable, which you expected, and the third one, with the
s///, _also_ refers to the old variable, which you did not expect.

But the behavior is consistent, reasonable, and predictable.

================================================================

Now why is your example with 'local' different?

'my' and 'local' are not closely related. 'my' creates a new
variable. 'local' does not create a new variable; there is only one
variable. local saves the old value of $foo and assigns a new value,
and arranges for the old value to be restored when the scope is
exited, after the first 'print'.

I consider this a bug or, at least, a breakage of orthogonality.

I hope the situation is clearer now.

@p5pRT
Copy link
Author

p5pRT commented Jul 19, 2016

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Jul 19, 2016

@cpansprout - Status changed from 'open' to 'rejected'

@p5pRT p5pRT closed this as completed Jul 19, 2016
@p5pRT
Copy link
Author

p5pRT commented Jul 20, 2016

From shmem@qwurx.de

Please take my apologies for filing this as a bug report, which was a
silly thing to do perhaps. On the other hand... this bugs me really...
For some reason I wrote the report as from a casual programmer's view.

From the keyboard of Mark Dominus [19.07.16,16​:29]​:

#!/usr/bin/perl -l
use strict; use warnings;
our $foo = "foo"; do { (local $foo = $foo) and $foo =~ s/o/i/g; print $foo };print $foo;
{ my $foo = "foo"; do { ( my $foo = $foo) and $foo =~ s/o/i/g; print $foo };print $foo; }
__END__
fii
foo
foo
fii

In the line involving local(), the inner variable is modified.
In the line involving my()..., the outer variable is modified.

Your expectation of 'my' is incorrect. Consider

No; my expectations are not incorrect, but they don't fit the current
implementation ;-)

 my $x = 1;
 \{ my $x = $x \+ 1;
   print $x \}   \# prints 2

Why does this work? `my` declares a second `$x` variable, but the
name `x` does not refer to this new variable until _after_ the
statement containing the declaration.

Yes, this is documented in perlsub​:

: The declared variable is not introduced (is not visible) until after
: the current statement. Thus,
:
: my $x = $x;
:
: can be used to initialize a new $x with the value of the old $x, and
: the expression
:
: my $x = 123 and $x == 123
:
: is false unless the old $x happened to have the value 123.

Which is not quite correct. The declared variable is visible for
assignment, and it can subsequently altered via s///​:

our $foo = "foo";
{ (my $foo = $foo) =~ s/o/i/g; print $foo; }
__END__
fii

Is there a compelling reason for my() being implemented this way?
Is it even useful? What code relies on this restriction?
Are there obstacles to implementing visibility in expressions?

Would it not be more useful if my variables were visible (after
initialization) in subsequent expressions of the current statement?

This would resemble the semantics of local $x = $x where the current
value of $x is assigned to the localized value slot of $x which can then
subsequently used in expression of the current statement and eliminate a
syntactical ambiguity.

Aliasing a value or masking a variable or introducing a lexical variable
should not imho lead to different syntax requirements inside
expressions.

The two '$x' tokens in the second line refer to two different
variables​: the $x on the left is the new variable, and the $x on the
right is the old variable. Then the $x in the 'print' is the new
variable. If it didn't work this way, then the $x on the right would
have an undefined value, the + would produce an undefined-value
warning, and the print would print 1.

...whereas in local $x = $x both identifiers refer to the same variable
but two different value slots.

The aliasing of the value slot via local() is carried out immediately,
not just after the current statement, which could be otherwise as well.

The visibility of my variables is deferred until after the current
statement.

In your example, the same thing is happening​:

{ my $foo = "foo";
do { ( my $foo = $foo) and $foo =~ s/o/i/g;
print $foo };
print $foo; }

There are three $foo names in the second line. The `my $foo` is
declaring a new variable, but the name '$foo' will not refer to it
until the following statement. This means that the second $foo refers
to the old variable, which you expected, and the third one, with the
s///, _also_ refers to the old variable, which you did not expect.

But the behavior is consistent, reasonable, and predictable.

================================================================

Now why is your example with 'local' different?

'my' and 'local' are not closely related. 'my' creates a new
variable. 'local' does not create a new variable; there is only one
variable. local saves the old value of $foo and assigns a new value,
and arranges for the old value to be restored when the scope is
exited, after the first 'print'.

I consider this a bug or, at least, a breakage of orthogonality.

I hope the situation is clearer now.

0--gg-

--
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
  /\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

@p5pRT
Copy link
Author

p5pRT commented Jul 20, 2016

From zefram@fysh.org

Georg Moritz wrote​:

No; my expectations are not incorrect, but they don't fit the current
implementation ;-)

Your expectations are incorrect in that you are confusing lexical extent
with dynamic extent. It's not that these two happen not to match in
the present implementation; they're different kinds of thing that can
never match.

Which is not quite correct. The declared variable is visible for
assignment, and it can subsequently altered via s///​:

No, the documentation is correct. Your example does not use the declared
name for the new variable until after the statement that declares it.
The phenomenon you're using here is that the "my $foo" declaration and
the assignment are both lvalue expressions that in this usage evaluate
to the declared variable. That's not really visibility of the variable;
for the variable to be visibile in the sense of which the documentation
speaks is for the declared name to refer to the variable.

Is there a compelling reason for my() being implemented this way?

Precisely where the name's scope begins is a somewhat-arbitrary language
design choice. It could just as well have been designed to have the
name come into scope at the point of the declaring expression, thus
being in scope for the rest of that statement. Variable declarations
work equivalently to that in C​: the variable is in scope for its own
initialiser. The choice made in Perl is equivalent to that made in Lisp,
where in a "let" form none of the variables are in scope until after
all of the initialisers. (Though, being Lisp, there are also variant
forms that scope differently.)

The main advantage of declarations working this way is that a variable
can be initialised from an expression that makes use of an outer-scope
variable of the same name which is being shadowed. This doesn't come
up terribly often in Perl, so I wouldn't say that it's compelling in
this context. In Lisp there's culturally a slightly greater tendency
to deliberately reuse names this way, but macros make it very easy to
accidentally reuse names. Common advice for macro writing includes being
sure to handle all macro parameters in a single "let" form, so that none
of the macro's introduced names affects the interpretation of any of
the parameter expressions. (Less relevant when using gensyms, of course.)

Another benefit in some languages is that delayed introduction of the
name can make it impossible to examine the variable prior to it being
initialised. If it's impossible to see the uninitialised variable,
the language is more in accordance with mathematical abstractions,
and is especially consonant with the no-assignment-after-initialisation
programming style. This is relevant in Lisp. C doesn't care because
it's a low-level language that makes uninitialised memory visible in
many ways and has well-defined rules for what can be done with it.
It's also not relevant in Perl, which has a well-defined state for
`uninitialised' variables and exposes side effects all over.

Are there obstacles to implementing visibility in expressions?

The Perl implementation currently has built-in knowledge that scope
boundaries only occur at statement boundaries. I'm not sure how much
really relies on that; I suspect that for most purposes one could actually
get away with telling the internals, in the middle of a statement, that
the scope began at the start of the present statement. If that doesn't
work, though, it would be very painful to make the implementation capable
of more general scoping.

Backcompat prohibits changing this for "my". One could implement it
only for a new keyword, which can be done as a CPAN module if one feels
the need.

Would it not be more useful if my variables were visible (after
initialization) in subsequent expressions of the current statement?

No. In the context of Perl, the advantages each way are small, neither
clearly dominant.

This would resemble the semantics of local $x = $x

"local" doesn't affect the scope of variable names at all. It's not
directly comparable with "my". Though I note that the initialising
expression here sees the outer value of $x, which is analogous to the
behaviour of "my $x = $x", and not to the different behaviour that
you're proposing.

subsequently used in expression of the current statement and eliminate a
syntactical ambiguity.

There is no ambiguity.

-zefram

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