Hacker News new | ask | show | jobs
by bloobloobloo 3253 days ago
> 10 different ways of doing the same thing

Mostly an urban myth. Where it isn't, at least 8 of those ways are actually important.

Exercise: write a python program that prints Python's quoting documentation, without external files, without editing the documentation. Spoiler alert: it's impossible.

2 comments

So, how exactly these different ways of getting sub arguments are useful?

    sub add1 {
        my ($arg1, $arg2) = @_;
        $arg1 + $arg2;
    }

    sub add2 {
        my $arg1 = shift;
        # possibly two screens of dense code here, then
        my $arg2 = shift;
        $arg1 + $arg2;
    }

    sub add3 {
        $_[0] + $_[1]
    }

    # There may be some other ways to extract arguments 
    #  that I am not aware of.
    # It's possible to combine any of the methods!
And this is just argument access, one of the core language primitives.
> So, how exactly these different ways of getting sub arguments are useful?

Because in the second (add2), you might do something interesting with the second (or third) arguments depending on the first. You might have optional arguments. Your arguments might be a list of items you don't know the size of. It allows you to develop what you need out of the extensible core mechanism. Modules can and have built on that.

> # There may be some other ways to extract arguments

There are. Now you can just do

    sub add( $arg1, $arg2 ) {
        $arg1 + $arg2;
    }
My personal favorite is to use Function::Parameters[1]

    fun add( Num $arg1, Num $arg2 ) {
        $arg1 + $arg2;
    }
which allows named params, type constraints (runtime), default params, etc.

1: https://metacpan.org/pod/Function::Parameters

I cannot imagine any way of practical (ab)using this flexibility that could not be covered in Python with its only way of handling arguments.
To be a bit pedantic, these are not different ways to get a sub arguments, but accessing an array in different ways ("default array" in this case). If I understand this correctly, your criticism is aimed at how the core language makes arguments available to a sub body, in the default array.

The first example uses the explicitly named default array @_. This is a common pattern, and easy to read.

The second one omits the "default". Note that whilst it is possible to write some dense code between the $arg1 and $arg2, it won't work as expected if the dense code bit has array access - ie., the default array can change in the code.

The third example uses the typical sigils for accessing individual elements within an array (default or otherwise).

I wish the core language had saner defaults, but over time, I've seen some reasonable uses for the different styles:

1. General subs, the same as you example

2. shift removes the first element of the array and returns it. This can make the code more readable in certain cases:

  use Params::Validate 'validate';
  sub add2 {
    my $self = shift;
    my $args = validate (@_, { ... } ); 
  }
3. Slightly less verbose code, for simple one-liners and/or anonymous functions:

  my $calc_functions = {
    'add' => sub { $_[0] + $_[1] },
    'subtract' => sub { $_[0] - $_[1] },
    ...
  };
  my $func = 'add';
  $calc_functions->{$func}->($args);
The significant part of argument handling in Perl5 is that @_ is a list of aliases to the arguments. You usually see add1 or add2 style argument processing because nice named arguments that are a copies of the function parameters.

add3 shows how to access the aliased arguments directly.

  # break the aliasing of @_ and make undef into 0.
  sub undef_to_zero { @_ = map { $_ // 0 } @_; }

  sub math_stuff {
    # call with identical @_
    &undef_to_zero;
    # Do math without undef warnings.
    my $sum = 0;
    $sum += $_ for @_;
  }

  # Alter all arguments in calls by repeating them
  sub fatten {
    $_ = "$_" x 2 for @_;
  }

  $foo = "abc";
  @bar = ( 1, 2, 4 );
  foo($foo, @bar);
  say $foo; # abcabc
  say for @bar # 11
               # 22
               # 44
You aren't really showing off different ways to get arguments in any of those examples. In all of them, you get the arguments in a list called @_; you're just showing off three different ways to get values out of a list, which Python has plenty of ways to do as well. Translating your examples into Python:

    def add1(*_):
        _ = list(_)

        arg1, arg2 = _
        return arg1 + arg2

    def add2(*_):
        _ = list(_)

        arg1 = _.pop(0)
        arg2 = _.pop(0)
        return arg1 + arg2

    def add3(*_):
        _ = list(_)

        return _[0] + _[1]
That does not change the fact that in Python everybody writes

    def add(x, y):
       return x + y
whereas the Perl codebase that I work with has every possible combination of these methods.
But people using Python do at different times use destructuring, pop, and indexing to get values out of a list, which are the different ways of doing the same thing that you demonstrated.

e: Just clarifying that your complaint here is that every sub in Perl 5 just receives a list of its arguments; this isn't a "more than one way to do it" issue.

Why does Python have other options if no one is using them? Are these other options cruft in the language design, the compiler, or just reasonable support for features intended for other, perhaps infrequent, usage?

The old P5 motto TIMTOWTDI was long ago updated to TIMTOWTDIBSCINABTE and P6 adopts the latter. While P6 supports most of the options shown for P5, most folk writing P6 will just write something like:

    sub add($x, $y) { $x + $y }
I don't mean to detract from your point, but I have a related question:

How much actual cognitive overhead is there associated with three of four different ways of unpacking args?

Speaking for myself (as a long-time Perl/Python/GoLang/etc programmer), when I jump into a language I don't know, it doesn't take me long to get 'muscle memory' for how such things work.

Come up with a style guide and stick to it. Failure to do that with any language, no matter how restrictive, leads to messy code.

It doesn't matter which way you unpack your args unless you are trying to do something fancy (the sub 1% cases). Just pick one.

Hell, it's easy enough to make a script to rewrite multiple shifts into destructured list assignment or vice versa.

The second example is just bad code. The third example one would rarely use in oneliners or very shorts subs, maybe in a dispatch table. You can write atrocious code in just about any language. Perl 5.20 finally has function signatures anyway.
you'll be happy to know that the sigils (@) don't change in perl6 for access.
I see no problem here.
I'm not sure I understand your exercise. What do you mean by "Python's quoting documentation", and what do you mean by "editing" it?
> What do you mean by "Python's quoting documentation"

String literals are described by the following lexical definitions:

   stringliteral:   shortstring | longstring
   shortstring:     "'" shortstringitem* "'" | '"' shortstringitem* '"'
   longstring:      "'''" longstringitem* "'''" | '"""' longstringitem* '"""'
   shortstringitem: shortstringchar | escapeseq
   longstringitem:  longstringchar | escapeseq
   shortstringchar: <any ASCII character except "\" or newline or the quote>
   longstringchar:  <any ASCII character except "\">
   escapeseq:       "\" <any ASCII character>

You can't paste that text into a file and write a Python program around it that outputs the same text. You need to encode the quote characters.

More generally, there is no way in (many languages including) Python to treat an arbitrary (yet known) block of data as data.

For those that don't know you can use `「` and `」` to quote anything that does not contain `」` and Perl 6 won't process it in any way other than storing it as a Str.

If you needed to have `」` in there you can fall back to `Q{{{{{{…}}}}}}` or `Q:to<END>;␤…␤END␤`

I was wondering about that, too. Unless `"Python's quoting documentation"` is an easter-egg in "help()" that recursively contains a quoted version of itself, it should be possible to just escape all problematic characters. But maybe that counts as `"editing"`? Not sure.
> maybe that counts as `"editing"`

how would it not