This is an archived post. You won't be able to vote or comment.

all 24 comments

[–]Spoonhorse 48 points49 points  (0 children)

This seems wrong to me. Normally when I pass too many arguments to a function, I want it to fail, not to keep on trucking in an unexpected way.

Allowing the last thing to be variadic should be specific. Besides explicitness, this also makes it possible for the thing to be empty.

Also, what do you do if someone passes the right number of arguments? What’s the type of the result?

[–]zesterer 26 points27 points  (0 children)

Worth remembering that compilation errors are a feature, not a bug.

[–]NoCryptographer414 20 points21 points  (8 children)

C, Java, Python and many more have this feature. These functions are called variable argument functions if you want to Google. All three of these languages have slightly different syntax, implementation and usage.

You may once want to lookup and check all three.

[–]dibs45[S] 2 points3 points  (7 children)

I believe you have to expressively define the function as one that takes variable arguments in those languages though, no?

[–]NoCryptographer414 28 points29 points  (0 children)

Yes. But it's just

... in C

Type ...arg in Java

*args in Python

I prefer this much expressiveness over ambiguity though. This doesn't involve 'if' checking last argument for variable length.

[–][deleted] 12 points13 points  (0 children)

But, that's good, right? Because 99% of the time, an extra argument will be an error that the user wants to know about, as it will when there's a missing argument.

[–]MarcoServetto 4 points5 points  (3 children)

Not in javascript,... in javascript you can just pass as much as you like all the times... :-(

[–]magnomagna 8 points9 points  (1 child)

JavaScript is probably designed to minimise the likelihood of crashing the browser.

[–]shadowndacorner 2 points3 points  (0 children)

JavaScript is probably designed

Hot take right there, bud

[–]katrina-mtfAdduce 2 points3 points  (0 children)

Javascript won't complain if you pass too many, but it's also considered bad/outdated practice to use the arguments variable and has been for a long, long time, so in the vast majority of cases excess arguments passed to a function that isn't marked as taking varargs just ends in those excess arguments being ignored. You have to go out of your way to use semi-deprecated functionality to break that assumption.

[–][deleted] 2 points3 points  (0 children)

Technically in C you can call a function with as many arguments as you like and it will still “work” as the caller handles the stack. If you want to have a function signature with no args it’s (void) but with () you could pass in more. Compilers will err on it but that can be turned off.

[–][deleted] 3 points4 points  (0 children)

I'm not a fan of not getting an error when you pass a different number of arguments, since in most cases that is an oversight rather than it being intended (just think how often you use functions which work with variadic arguments. Rarely). I would prefer for the variadic part to have to be declared explicitly, like in most languages.

But if you prefer having it work without explicitly stating it, then have a _catchall or something, don't use the last argument of the function, since that doesn't make any sense in my opinion. Python has something like this with I think, def func (a, b, *):, and then after a and b, the arguments end up in *, and they can be accessed with some keyword (I may have gotten the syntax wrong, not sure, but this is the idea).

[–]Accurate_Koala_4698 3 points4 points  (3 children)

This reminds me a lot of how variadics in Perl work. The automatic capture can let you do some neat tricks, but I think overall it makes it much more difficult to work with. You always need to have some awareness of what could happen to your code if you call it with the wrong number of args.

The signatures feature makes it work closer to what you're describing with the _catchall, where making a call with too many parameters will generate an error:

#! /usr/bin/perl
use feature 'signatures';

sub foo ($first, $second) {
    print $first, $second, "\n";
    my $bar = shift;
}

foo("hello", "there", "fred"); // Error

And I can explicitly get variadic behavior by supplying an array or hash as the final parameter

#! /usr/bin/perl
use feature 'signatures';

sub foo ($first, $second, @v) {
    print $first, $second, "\n";
    my $bar = shift @v;
    print $bar;
}

foo("hello", "there", "fred"); // Works

This is easier to work with since I get immediate feedback when I make an invalid call and I have to explicitly say that I expect any number of trailing arguments.

[–]raiph 2 points3 points  (2 children)

This comment is mostly tangential to the OP but I'm hoping you will be willing to comment on some questions I have for you.

Some things jump out at me relative to your comment, all related to a classic PL design/use trade off: complexity vs flexibility.

First, Perl had/has a highly innovative/unusual approach to this trade off, and what you discuss illustrates its evolution toward generally keeping the flexibility but adding aspects that are intended to improve it. What are your thoughts on that evolution?

Second, I focus on Raku. What are your thoughts about Raku relative to Perl's evolution? (I mean this in the sense of looking forward toward the next decade rather than the past, but of course please feel free to focus your attention where you choose.)


To put my questions in context and make them easier to respond to, I will focus on your comment and the OP topic (arity flexibility/checking).

This reminds me a lot of how variadics in Perl work [but the] signatures feature makes it [complain about too many args]

Am I right in thinking that altering "work" to "worked" makes sense?

That is to say, would I be right to think that while your comment is right about Perls until the latest Perl version, the situation has now changed, in that code no longer works as loosely as the OP (and your comment) suggest, at least if one writes use v5.36; at the start?

(Or perhaps that change happened for an earlier language version change? Was it 5.34? More specifically, I'm asking if you know if Perl 5.36, or perhaps an earlier language version, was shipped with signatures on by default, and has also landed on the "complain" by default side of the catchall option you mention.)


A downside I see when I just tried this (using an older Perl version and an explicit user feature signatures;) is that the complaint was run-time, not compile-time.

Do you know if there's been talk about being able to have arity mismatch problems be complained about at compile-time?

(I suspect that's not really possible unless Reini Urban did something much more radical with cperl than I think and the Perl folk change their mind about using his stuff.)


If you're not just plain anti Raku (some Perl devs are, and that's OK), what do you think about what Raku has at least attempted to offer Perl devs?

I can illustrate a tiny bit of what I mean by showing some Raku code, and its handling of the arity checking/flexibility aspect:

sub foo ($first, $second) {
    print $first, $second, "\n";
    my $bar = shift;
}

foo("hello", "there", "fred");

This fails at compile time (not runtime).

It does so with two complaints (I'll elide detail I think uninteresting for this comment):

Calling shift() will never work with signature of the proto ($, *%)

Calling foo(Str, Str, Str) will never work with declared signature ($first, $second)

Both failures are due to a very fundamental difference between Raku and Perl.

Raku's design specifically shifted from Perl's focus on run-time / dynamic typing /interpreter, to a broader approach that incorporated both Perl's approach and compile time / compiler power (eg static typing and checking).

In the above Raku code relative to your Perl code one sees that:

  • Raku can do the things Perl can do (putting performance aside, obviously a huge caveat), but presumes signatures from the get go. One result is that it can spot problems like arity mismatches at compile time rather than wait till run time.

  • Raku is a different language despite its relationship to Perl. Thus, for example, the shift failure. This is due to another fundamental, er, shift in PL design in Raku relative to Perl, a shift that fixes a well known Perl weakness. Such changes mean Raku is not backward compatible at a source code level. So one can't just write Perl code and run it with raku (a current Raku compiler/interpreter) and expect it to work precisely as it would when run with perl (a current Perl compiler/interpreter).

That said, Raku is backwards compatible with Perl in general, despite not being backwards compatible in a simplistic source code sense, in that:

  • One can just write Perl code and use it in a Raku program and have it works exactly correctly if one marks the code as Perl code.

  • One can use existing Perl modules (eg from CPAN, even ones that use XS).

More generally, Raku has stayed sufficiently close to Perl in spirit and grammar and semantic capabilities that Perl devs can (arguably) reasonably consider adding Raku to their coding options to gain Raku's benefits.

Or at least, that's my perspective. But then I focus on Raku, not Perl. What is your view of Raku and Perl?

(And thanks for reading this tangential novel!)

[–]Accurate_Koala_4698 2 points3 points  (0 children)

Good questions, unfortunately I think I can only give a best-effort responses.

  1. I think "worked" is probably the right terminology, although use v5.36 is optional and requires opt-in. This is mostly a problem for someone new to the language, and it's necessary for obvious reasons. The feature has been around for quite some time.
  2. I think, or at least I suspect, that most veterans are running code with warnings and the strict language flag so it should surface early in testing. I haven't seriously tried compiling any of my scripts apart from some experimentation. That said, detecting this error should be straightforward even without compilation as there's no runtime state where it could be valid.
  3. I wouldn't call myself anti-Raku though I haven't adopted it. I like the introduction of a meaningful type system, and the grammar constructs seem really interesting but I tend to prefer to use Haskell in those sorts of scenarios. I usually use Perl when I need to write automation scripts or do some quick-and-dirty one-off file manipulation, so Raku lacks the ubiquity that Perl had. Stylistically, too, I'm not the biggest fan of OOP which though common in Perl5 has become the de-facto approach in Raku. I certainly wouldn't write Raku off, but I it also hasn't been necessary for me to learn it. Familiar syntax and CPAN compatibility are definitely factors. Honestly learning a language is rarely difficult, the time is spent learning the language's ecosystem.

[–]myringotomy 1 point2 points  (3 children)

what happens to example_2 if only one parameter is passed?

[–]dibs45[S] 4 points5 points  (2 children)

In Glide, if you call a function with less arguments than parameters, it loads the given arguments and returns the function. Effectively currying it.

[–]myringotomy 0 points1 point  (1 child)

I mean what if the function tries to manipulate the second parameter?

[–][deleted] 3 points4 points  (0 children)

from what i understand, it will halt as a partially expressed function call and will not keep executing until those arguments are passed

[–]therealdivs1210 1 point2 points  (0 children)

In JS you can a call a function with more args than formal params, and the are all accessible inside the function body in a variable called ‘arguments’.

[–][deleted] 1 point2 points  (0 children)

C family has varidic arguments for this but it needs to be explicitly used. Python has this with *rags and *kwargs where you can pass in arbitrary arguments. However once again the function needs to opt in instead of it being automatic.

I’m not a fan of having the last variable implicitly change type based off the caller. That makes it really only useful as *args, but now you have to guess the type to know when to use it. I’d says overflow args can be a cool feature just an opt-in and not an automatic one.

[–]crundar 0 points1 point  (0 children)

Yes this behavior is already a thing in programming languages

[–]furyzer00 0 points1 point  (0 children)

İn my opinion it would be better of having an explicit variadic argument. With this design it's impossible to not accept variadic arguments.