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

Some unary functions accept multiple arguments #11540

Open
p5pRT opened this issue Jul 31, 2011 · 5 comments
Open

Some unary functions accept multiple arguments #11540

p5pRT opened this issue Jul 31, 2011 · 5 comments

Comments

@p5pRT
Copy link

p5pRT commented Jul 31, 2011

Migrated from rt.perl.org#96006 (status was 'open')

Searchable as RT96006$

@p5pRT
Copy link
Author

p5pRT commented Jul 31, 2011

From @cpansprout

perl56delta has this section​:

  Better syntax checks on parenthesized unary operators
  Expressions such as​:

  print defined(&foo,&bar,&baz);
  print uc("foo","bar","baz");
  undef($foo,&bar);

  used to be accidentally allowed in earlier versions, and produced
  unpredictable behaviour. Some produced ancillary warnings when used in
  this way; others silently did the wrong thing.

  The parenthesized forms of most unary operators that expect a single
  argument now ensure that they are not called with more than one
  argument, making the cases shown above syntax errors. The usual
  behaviour of​:

  print defined &foo, &bar, &baz;
  print uc "foo", "bar", "baz";
  undef $foo, &bar;

  remains unchanged. See perlop.

A few of unary functions survived, though​:

prototype(1,2,3) evaluates 1 and 2 in void context, using 3 (in scalar context) as its argument. That’s harmless, although a bit surprising. eval() behaves the same way.

When called in list context readline and readpipe evaluate their arguments in list context, and then pop the topmost item leaving everything else there, in addition to the value intended to be returned.

In scalar context readline and readpipe act like prototype.

In dynamic context (determined at run-time; e.g., at the end of a sub) readline and readpipe propagate their context to *all* their arguments. So readpipe(foo(),foo()) at the end of a subroutine makes both foo calls in scalar context if the subroutine is called in scalar context, but calls the first foo in void context if readpipe can be determined at compile time to be in scalar context.

Here are some examples​:

$ perl -le'open my $foo, "<", \"bar"; print readline("baz",$foo)'
bazbar

$ perl -le'open my $foo, "<", \"bar"; print "baz",$foo,readline(sub{}->())'
bazbar

Context of readline’s operands​:

$ perl -le 'sub context { print qw[void scalar list][wantarray + defined wantarray] } readline(context,context)'
void
void

which means that a function called in void context can return a value (the second readline would give bar, not baz, if get_foo’s retval were ignored)​:

$ perl -le 'open my $foo, "<", \"bar\nbaz"; sub get_foo {print qw[void scalar list][wantarray + defined wantarray]; $foo } readline(get_foo); print scalar readline $foo'
void
baz

$ perl -le 'sub context { print qw[void scalar list][wantarray + defined wantarray] } $x = readline(context,context)'
void
scalar

$ perl -le 'sub context { print qw[void scalar list][wantarray + defined wantarray] } () = readline(context,context)'
list
list

Context of readline’s operands in dynamic context​:

$ perl -le 'sub context { print qw[void scalar list][wantarray + defined wantarray] } sub { readline(context,context) }->()'
void
void

$ perl -le 'sub context { print qw[void scalar list][wantarray + defined wantarray] } $x = sub { readline(context,context) }->()'
scalar
scalar

$ perl -le 'sub context { print qw[void scalar list][wantarray + defined wantarray] } () = sub { readline(context,context) }->()'
list
list


Flags​:
  category=core
  severity=low


Site configuration information for perl 5.15.0​:

Configured by sprout at Thu Jun 16 05​:42​:17 PDT 2011.

Summary of my perl5 (revision 5 version 15 subversion 0) configuration​:
  Snapshot of​: 00b4043
  Platform​:
  osname=darwin, osvers=10.5.0, archname=darwin-thread-multi-2level
  uname='darwin pint.local 10.5.0 darwin kernel version 10.5.0​: fri nov 5 23​:20​:39 pdt 2010; root​:xnu-1504.9.17~1release_i386 i386 '
  config_args='-de -Dusedevel -Duseithreads'
  hint=recommended, useposix=true, d_sigaction=define
  useithreads=define, usemultiplicity=define
  useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
  use64bitint=undef, use64bitall=undef, uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cc', ccflags ='-fno-common -DPERL_DARWIN -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include',
  optimize='-O3',
  cppflags='-fno-common -DPERL_DARWIN -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
  ccversion='', gccversion='4.2.1 (Apple Inc. build 5664)', gccosandvers=''
  intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
  d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
  ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
  alignbytes=8, prototype=define
  Linker and Libraries​:
  ld='env MACOSX_DEPLOYMENT_TARGET=10.3 cc', ldflags =' -fstack-protector -L/usr/local/lib'
  libpth=/usr/local/lib /usr/lib
  libs=-ldbm -ldl -lm -lutil -lc
  perllibs=-ldl -lm -lutil -lc
  libc=/usr/lib/libc.dylib, so=dylib, useshrplib=false, libperl=libperl.a
  gnulibc_version=''
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=bundle, d_dlsymun=undef, ccdlflags=' '
  cccdlflags=' ', lddlflags=' -bundle -undefined dynamic_lookup -L/usr/local/lib -fstack-protector'

Locally applied patches​:
 


@​INC for perl 5.15.0​:
  /usr/local/lib/perl5/site_perl/5.15.0/darwin-thread-multi-2level
  /usr/local/lib/perl5/site_perl/5.15.0
  /usr/local/lib/perl5/5.15.0/darwin-thread-multi-2level
  /usr/local/lib/perl5/5.15.0
  /usr/local/lib/perl5/site_perl
  .


Environment for perl 5.15.0​:
  DYLD_LIBRARY_PATH (unset)
  HOME=/Users/sprout
  LANG=en_US.UTF-8
  LANGUAGE (unset)
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=/usr/bin​:/bin​:/usr/sbin​:/sbin​:/usr/local/bin​:/usr/X11/bin​:/usr/local/bin
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented May 12, 2012

From @cpansprout

On Sun Jul 31 13​:54​:42 2011, sprout wrote​:

perl56delta has this section​:

Better syntax checks on parenthesized unary operators
Expressions such as​:

       print defined\(&foo\,&bar\,&baz\);
       print uc\("foo"\,"bar"\,"baz"\);
       undef\($foo\,&bar\);

   used to be accidentally allowed in earlier versions\, and

produced
unpredictable behaviour. Some produced ancillary warnings when
used in
this way; others silently did the wrong thing.

   The parenthesized forms of most unary operators that expect a

single
argument now ensure that they are not called with more than one
argument, making the cases shown above syntax errors. The
usual
behaviour of​:

       print defined &foo\, &bar\, &baz;
       print uc "foo"\, "bar"\, "baz";
       undef $foo\, &bar;

   remains unchanged\.  See perlop\.

A few of unary functions survived, though​:

prototype(1,2,3) evaluates 1 and 2 in void context, using 3 (in scalar
context) as its argument. That’s harmless, although a bit
surprising. eval() behaves the same way.

I’ve been thinking about this on and off for some time.

!($foo, $bar) evaluates $foo in void context and $bar in scalar context.
If we were to change that to ‘too many arguments for !’, we would have
a lynch mob after us.

The same applies to not($foo, $bar) and scalar($foo, $bar).

So I’ve been wondering why chr and getprotobynumber (for example) need
to refuse more than one argument. How are these fundamentally different
from not and scalar?

If I get a void warning when writing scalar(3, foo()), isn’t that
sufficient for other ops as well?

Now, there is one case where it makes sense to refuse more than one
argument​: scalar lvalue context.

I’m using the term lvalue in the broad sense of anything that goes
through op_lvalue internally, which means anything with a prototype
containing \$, \[$@​%&*] or \[$@​%*], or that parses as though it had such
a prototype.

In the case of untie(%foo,%bar), it is completely counterintuitive that
%bar should be untied, but not %foo. So in this case it makes sense to
refuse more than one argument (which doesn’t currently happen).

What I propose we do is make built-in functions with a prototype (or
effective prototype) of ($), (;$), (*), (;*) or (_) allow more than one
argument, but treat it as a single scalar expression (i.e., with a
scalar comma); and make built-in functions with a(n effective) prototype
of (\$), (\[$@​%*]), etc. refuse more than one argument.

Now, should the laxity regarding ($) apply to user-defined subroutines, too?

--

Father Chrysostomos

@p5pRT
Copy link
Author

p5pRT commented May 12, 2012

From [Unknown Contact. See original ticket]

On Sun Jul 31 13​:54​:42 2011, sprout wrote​:

perl56delta has this section​:

Better syntax checks on parenthesized unary operators
Expressions such as​:

       print defined\(&foo\,&bar\,&baz\);
       print uc\("foo"\,"bar"\,"baz"\);
       undef\($foo\,&bar\);

   used to be accidentally allowed in earlier versions\, and

produced
unpredictable behaviour. Some produced ancillary warnings when
used in
this way; others silently did the wrong thing.

   The parenthesized forms of most unary operators that expect a

single
argument now ensure that they are not called with more than one
argument, making the cases shown above syntax errors. The
usual
behaviour of​:

       print defined &foo\, &bar\, &baz;
       print uc "foo"\, "bar"\, "baz";
       undef $foo\, &bar;

   remains unchanged\.  See perlop\.

A few of unary functions survived, though​:

prototype(1,2,3) evaluates 1 and 2 in void context, using 3 (in scalar
context) as its argument. That’s harmless, although a bit
surprising. eval() behaves the same way.

I’ve been thinking about this on and off for some time.

!($foo, $bar) evaluates $foo in void context and $bar in scalar context.
If we were to change that to ‘too many arguments for !’, we would have
a lynch mob after us.

The same applies to not($foo, $bar) and scalar($foo, $bar).

So I’ve been wondering why chr and getprotobynumber (for example) need
to refuse more than one argument. How are these fundamentally different
from not and scalar?

If I get a void warning when writing scalar(3, foo()), isn’t that
sufficient for other ops as well?

Now, there is one case where it makes sense to refuse more than one
argument​: scalar lvalue context.

I’m using the term lvalue in the broad sense of anything that goes
through op_lvalue internally, which means anything with a prototype
containing \$, \[$@​%&*] or \[$@​%*], or that parses as though it had such
a prototype.

In the case of untie(%foo,%bar), it is completely counterintuitive that
%bar should be untied, but not %foo. So in this case it makes sense to
refuse more than one argument (which doesn’t currently happen).

What I propose we do is make built-in functions with a prototype (or
effective prototype) of ($), (;$), (*), (;*) or (_) allow more than one
argument, but treat it as a single scalar expression (i.e., with a
scalar comma); and make built-in functions with a(n effective) prototype
of (\$), (\[$@​%*]), etc. refuse more than one argument.

Now, should the laxity regarding ($) apply to user-defined subroutines, too?

--

Father Chrysostomos

@p5pRT
Copy link
Author

p5pRT commented May 12, 2012

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

@p5pRT
Copy link
Author

p5pRT commented May 21, 2012

From @ap

* Father Chrysostomos via RT <perlbug-comment@​perl.org> [2012-05-12 21​:15]​:

!($foo, $bar) evaluates $foo in void context and $bar in scalar context.
If we were to change that to ‘too many arguments for !’, we would have
a lynch mob after us.

The same applies to not($foo, $bar) and scalar($foo, $bar).

So I’ve been wondering why chr and getprotobynumber (for example) need
to refuse more than one argument. How are these fundamentally different
from not and scalar?

If I get a void warning when writing scalar(3, foo()), isn’t that
sufficient for other ops as well?

I’ll turn your question on its head.

Is there any reason not to make `scalar` as strict as `chr`? What does
relaxing the rules gain? Does it simplify any code, either on the user
or the interpreter side? As far as I can tell, all you achieve is to
change one error to another warning.

We have had a lot of cases where syntax improvements or alterations
to accommodate new features have proven impossible or just extremely
difficult because Perl syntax is so lax in many ways that all plausible
options already had some meaning or other.

So all else being equal I would advocate more strictness over less, as
a rough rule of thumb.

Regards,
--
Aristotle Pagaltzis // <http​://plasmasturm.org/>

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

2 participants