It‘s a separator between positional and keywords. It’s * because the “rest” argument (*args) also makes following arguments keyword-only, so a lone star is essentially one which collects nothing.
For python-level functions initially all arguments could be passed either positionally or by keyword, unless they slotted into the special `∗args` (positional-only) or `∗∗kwargs` (keyword-only) parameters (note: the star-prefixes is the important part here the names can be anything).
Now I'm saying this was for python-level functions because (AFAIK) at the C level it was always possible to rather easily define positional-only or keyword-only arguments.
Anyway one of the improvements of Python 3 was to allow parameters other than `∗∗kwargs` after `∗args`, with the effect of making them individually named but keyword-only (PEP 3102). A logical extension of the same was to allow keyword-only arguments without any positional, that's what the `*` pseudo-argument defines:
> The reasoning behind this change is as follows. Imagine for a moment a function which takes several positional arguments, as well as a keyword argument.
> Now, suppose you wanted to have 'key' be a keyword-only argument. Under the above syntax, you could accomplish this by adding a varargs (nb: `∗ignore`) argument immediately before the keyword argument.
> Unfortunately, the 'ignore' argument will also suck up any erroneous positional arguments that may have been supplied by the caller. Given that we'd prefer any unwanted arguments to raise an error, we could do this (nb: add an explicit assertion that `ignore` is empty)
> As a convenient shortcut, we can simply omit the 'ignore' name, meaning 'don't allow any positional arguments beyond this point'.
Thank you very much. I am using python for some time now. Stumbled upon this, but never took the time to read up on it. If anybody also wants to know more I found this extensive post explaining the concepts behind * and in python (not in multiplication):
https://treyhunner.com/2018/10/asterisks-in-python-what-they...
I understand that this is standard practice by now, but this is one of those cases where I can't help but feel the language design team strayed too far from the original spirit of python. Now there's yet another possible interpretation of an asterisk: multiplication; splatting; exponentiation; declaring variadic arguments; and now declaring that all subsequently declared arguments are to be keyword-only. And I understand that it sort-of relates to declaring variadic arguments, but it certainly isn't declaring variadic arguments, and in my opinion reusing the same token in the same syntactic construct for two wildly different purposes is destined to cause confusion. For evidence, see the other replies: people don't know about this and can't easily tell what it does. This runs counter to the Zen: "Readability counts", "Explicit is better than implicit", "Special cases aren't special enough to break the rules.", etc.
I think JavaScript got this right where the semantic resolution steps of arguments pretty closely follow the steps for assignment, which means once someone understands the basic idea of object destructuring they get named arguments for free, in the form of destructured object parameters:
I'll disagree with you here. In Python `*` is already a extremely popular symbol for expanding and there's no usage conflict between expand and numeric multiplication. This change gels really nicely with the rest of expand ecosystem.
I think it's much more beautiful and convenient than your JS example which in all honesty looks necessary cluttered and ugly.
But there’s no expanding happening here? So why take a token that’s immediately recognizable as either “expanding” or “muliplying-ish” and extend it to mean “or enforcing named parameters and not expanding”?
> cluttered and ugly
I won’t argue with you about visuals because I don’t think they’re all that important. But, I think it’s undeniable that reusing the same syntactic concepts across different semantic use cases is quite a lot more logically clean & beautiful than introducing new syntax that other HN commenters with past python experience can’t even understand. If someone sees the JS named parameter syntax at a declaration and has any JS experience, they’ll almost certainly figure out what to do - if someone sees the asterisk, even with Python experience, they’ll think “what the heck is this asterisk?” And again, the other commenters on this thread are clear evidence that this is indeed as unintuitive as I describe.
> This runs counter to the Zen: "Readability counts", "Explicit is better than implicit"
Huh? It's both making parameters more explicit and functions using it more readable and self-documenting.
In fact, the old way, before bare / and * in args list, where functions essentially had mandatory optional-keyword arguments made every function create an API in violation of “There should be one-- and preferably only one --obvious way to do it.”
I’m not interested in comparing it to the old way, that’s saying “this unintuitive way of doing things is a better way of doing them than before when they were impossible”. Achieving that is a too low a bar to be worth discussing.
It is very much not readable, as evidenced by the other python-experienced commenters on this thread having no clue what it means (“readability” doesn’t mean “easy to understand for people who already know what it means”, as achieving that is a very low bar. What should be targeted is “obvious enough to have a clear meaning even to those who haven’t studied the exact section of the spec”, which is possible in the JS approach as they’re reusing the same syntactic constructs throughout the language, but is impossible in the Python approach because they introduce new syntax to work around every little limitation of their existing syntax)
There's a tension in PHP-land between PHP's roots as a low-ish level, get-it-done, hackish language, with its big standard library and simple scalar types, and the better-organized and quite vocal developers who want it to be more Java-like, with great big frameworks and many deeply-nested complex class hierarchies. Instead of unwieldy hobbyist-hacker balls of mud, you build enterprise-scale balls of mud.
Named arguments don't do a lot for the first group. It looks like the big point in favor is not having to look up a reference for which-arguments-go-where in functions anymore, but a good IDE already does that for you.
But there is a common anti-pattern where you have some polymorphic function or interface, and you want to accrete your arguments somewhere and then bundle them up and call the function, so then you see things like [1]:
...and that is bad because it totally bypasses all of PHP's type-checking and leaves it up to the called function to sanity-check the input types instead of letting the compiler do it, and basically nobody does that anyway.
So named parameters would fix that at least, which would be nice. I'm just not looking forward to the impact this is going to have on code readability, because everyone (including this article) is going to go, "oh, now my parameter list is too long!", and break the function signature into one-line-per-parameter, which is vile.
You're reading the sentence incorrectly: "and basically nobody does that anyway", means the preceding type checking argument you refer to is moot. So I insist, I am a nobody (according to this definition).
Btw I don't always sanity check the parameters, mostly at the system edges only.
I'd think that such a solution doesn't solve but merely moves the problem.
A method with N arguments, where N > 2, with a call that is messy due to the number of arguments could instead take a single argument being a parameter object where the constructor for the parameter object takes the same N arguments.
A constructor with N arguments with a call that is messy due to the number of arguments ...
Aah, so then you use the Builder Pattern! Instead of one constructor taking N arguments, have a constructor that takes no arguments and N function calls to initialize the constructed values. It's cleaner because it's a named pattern! /s
With a dictionary (array in PHP) you lose strict typing and it's possible for the person constructing it to make a typo and it won't cause an error, the called function will just think an optional argument was left out.
Writing and maintaining parameter classes is just tedious.
PHP's strict typing is only enforced at runtime, and because of that, its type system is very simple, unlike Typescript's. You can enforce that a variable is an array, but not that it's an array of ints, let alone that it's an array with certain types under certain keys.
Although there is external tooling that uses static analysis for such more complicated types.
it's not the best of both worlds though, splat should only be used on something where you're basically doing the same thing to a bunch of things.
like add(1,2,3,4,5) ->
function add(...$nums){
$count = 0;
foreach($nums as $num) { $count += $num; }
}
If you know what arguments you're getting, then the more sound way would be just use an associative array, but either way you really don't get great type checking.
> What is the functional difference between a named construct "Params" and just allowing named parameters?
That "just" is doing a lot of work. I've not used PHP for a few years, but off the top of my head I can forsee the following problems:
- `call_user_func` would need to change to support this, e.g. accepting and passing-along named parameters.
- `call_user_func_array` would need to change, e.g. passing along associative-array arguments as named parameters.
- `func_get_args` would need to change, e.g. returning an associative array
Other parts of the standard library, like ReflectionParameter, would also need corresponding changes.
On their own I don't think those are necessarily bad. However, this would break a lot of code which relies on those functions and their invariants. For example, currying and partial application would become more complicated (e.g. http://chriswarbo.net/blog/2014-02-21-partial_application___... ).
I'm not necessarily against this change, but I would make the following points:
Extending APIs is a breaking change, since it breaks invariants/contracts that previously held (in this case by function definitions and call sites). This breaks backwards-compatibility, and that should be acknowledged and stated explicitly, rather than ignored. Here's a quick example of code which breaks, since it relies on the now-violated invariant that the parameter order of a function call matches that of the definition:
Secondly: there's a definite bandwagon effect between scripting languages like PHP/Python/JS/etc., for better (easier to transfer between) or worse (uncompelling monocultures lacking innovation/USP). Just because a feature exists in one, doesn't necessarily make it a good idea for the others (or the first, for that matter!); conversely, there are lots of great features out there which scripting languages don't seem to have picked up, and which are hardly ever brought up in these discussions. For example:
- Default arguments are generally supported, e.g. `parseInt(i, base=10)`. Alternative approaches to the same problem, like currying, are mostly absent, e.g. `parseIntFor(base, i); parseInt = parseIntFor(10);`
- Generators are generally supported, e.g. `for x in foo: yield x`. Alternative approaches to the same problem, like delimited continuations, are mostly absent, e.g. `reset; for x in foo: shift(x);`. This is especially interesting, since it's just an extension of exception handling, which is another generally-supported feature!
- Named parameters are generally supported, e.g. `foo(a=1, c=5)`. Alternative approaches to the same problem, like row-polymorphism, are mostly absent, e.g. `foo(['a' => 1, 'c' => 5])`.
So one of your arguments is that it'll be difficult to maintain the C implementation of call_user_func, call_user_func_array and func_get_args? But this has already been done by the PHP maintainers.
As for your argument that it breaks code that today relies on argument order... Even in current versions of PHP, nothing in the language stops me from changing function f($a) to function f($b, $a) between v1 and v2 of my library and your code is equally broken. Whether I as a function author decide to re-order my arguments is a decision informed but not dictated by the existence of named arguments.
I don't understand your rant about bandwagons/monoculture. If you ask me the reasons most popular langauges are moving in the same direction is because popular languages are stealing each others' best ideas, which is a good thing. All your "alternative" solutions read like horrible code to me compared to the now commonly accepted designs.
> So one of your arguments is that it'll be difficult to maintain the C implementation of call_user_func, call_user_func_array and func_get_args?
No, I wasn't talking about C; or about language implementations at all. I was arguing that the phrase "just allowing named parameters" gives the impression that this feature would only affect function calls and definitions, when that's not the case. The most obvious repercussions are for `call_user_func`, `func_get_args`, etc. whose API would need to change. That induces changes to code that calls those functions, and code that calls that code, and so on.
Although PHP is famously un(der)specified, I'm talking about things like "`call_user_func(f, args...)` is equivalent to `f(args...)`"; anything relying on that property will break in the presence of named parameters (since they may be given in a different order), unless patched to take it into account.
> Whether I as a function author decide to re-order my arguments is a decision informed but not dictated by the existence of named arguments.
Sure, but that's just a first-order effect. It's a breaking change for higher order code (where functions and arguments are data, and calling is an operation on that data). That's why I gave an example of higher-order code.
As a silly analogy: if PHP extended its default numeric types to be complex numbers, that wouldn't affect existing values like `$age = 18` or `$length = 50`, since they're "informed, but not dictated by the existence of complex numbers"; it also wouldn't affect calculations using these values like `plus($age, $length)`. Yet it would have profound effects on numeric functions, which may have to be patched to take complex values into account: some functions, like `array_sum`, might work fine as-is; whilst others, like `array_slice`, might make no sense given the new situation (how might we take `3+5i` elements from an array?).
> All your "alternative" solutions read like horrible code to me compared to the now commonly accepted designs.
This sentence is a good example of what my "rant" is about. Why should improvements depend on what's "commonly accepted" (surely that's antithetical)? Could you elaborate any further on what makes those examples "read like horrible code"?
As a more detailed example we can look at `switch`: it's clearly a "now commonly accepted design", due to its prevalence in many "popular languages" like PHP/Javascript/etc. An alternative approach which solves the same problem is `match`, as used by ML. However, ML is not a popular language, and ML also introduced currying which you say "read[s] like horrible code". Does that make `switch` better than `match`?
This is an interesting example since a recent Python Enhancement Proposal is trying to add `match` to the language. This proposal seems credible, since the author list includes Guido van Rossum (the creator of Python): https://www.python.org/dev/peps/pep-0622
Perhaps this is an outlier, since Python doesn't already have an equivalent like `switch`?
I wonder if the existence of that PEP changes your opinion of `switch` versus `match`? Maybe not; after all, Python doesn't have `switch` so maybe `match` is better than nothing. What if I showed you an equivalent proposal for adding it to Javascript (using the keyword `case`, like the non-popular language Haskell)? https://github.com/tc39/proposal-pattern-matching
At what point does a feature go from "horrible code" to "stealing each others' best ideas"?
What makes this even more interesting is that the very first sentence of PEP622 m...
I'll use it for functions with two parameters, array functions and the order you pass in arguments is not standardized. I can start just using named functions to make it clear and easier to read/write.
But anyway, I'd prefer anonymous records like in F#. There's no reason to have static and dynamic mappings have the same syntax, especially if they're not the same to the type checker, even a compiler could statically dispatch the static variant. So I agree with you that dicts are overused.
who want ... and many deeply-nested complex class hierarchies
Are there really people aiming for that, as a goal? Or is it rather just the result of large-scale software? And/or if it's really complex, then perhaps that's just the result of suboptimal design, not the intent?
Yes, sort of. There are many people that think of OOP as the goal and not the tool, that a more OOP solutions is better. Some of them equate complex class hierarchies with a job well done.
I'm being a bit hyperbolic, but I've worked that think a if/switch statements are a code smell that should be converted to complex class hierarchies.
I can sympathize with that, but at the same time: the idea that the language and its OOP implementation is a tool rather than the goal, and the idea of improving said tool are not mutually exclusive. :)
In my experiences with C#, things like named params have been nice, even if it's often just syntactic sugar; these things can be quality of life improvements for the programmer without changing anything about the goal.
Not just beginners. Seeing the following in a pull request is bad for everybody:
someFunction(username, true, false, 10)
Named parameters remove ambiguity and make the code easier to read and review, and even avoid bugs because if you're passing the wrong values it's immediately obvious.
One of the hidden issues with the above code comes in the future: if anyone ever changes the order of the 2nd and 3rd arguments in the function definition, and forgets to update this spot, the code will still "work" (no compile error) but instead will have the wrong runtime behaviour. In a pull request, you can't see all the affected spots that didn't get updated, so it is easy to slip through.
> tension in PHP-land between PHP's roots as a low-ish level, get-it-done, hackish language [...] and the better-organized and quite vocal developers who want it to be more Java-like
Agree, and I'd like to add the original purpose of PHP, and what made it popular in the first place was that it can be used as embedded PHP within SGML-ish processing instruction ie <!php ...>. Only that PHP made such a lousy hack job of it being not HTML-aware and not doing context-dependent escaping, that it would become the primary script injection/XSS attack vector on the web. PHP's forte IMHO is its community and the large installed base of apps (there doesn't exist anything like Magento with its first-party plugins by DHL, UPS, payment providers, etc on Node.js for example). Why PHP wants to become more like Java or JavaScript is a mystery for me, when there's a hell of a cleanup job to be done beforehand, such as fixing <?php ...>.
> There's a tension in PHP-land between [...] hobbyist-hacker [vs] enterprise-scale
IMO the same tension exists in Python. The hobbyist-hackers are using the language to write small-ish scripts, and the enterprise-scale developers are working on million-line codebases.
Unfortunately for the hobbyist-hackers, at some point (perhaps when the BDFL started working at Dropbox), the focus of the language shifted from the first group to the second. That's why, for example, Python now has a complex type system - that many developers never use.
I would argue that has a strong correlation with the fact that universities are now teaching Python instead of Java. Once that alumni hits the job market, hobbyist-hackers are no longer the majority of users.
You'd have a point if there were any changes that are being introduced and putting developers on a straight-jacket. But I don't see this tension at all.
What is recent on Python3 that makes it hostile to hobbyists? The new features are optional, it's not like Python is forcing people to adopt mypy.
> What is recent on Python3 that makes it hostile to hobbyists?
Python has become a very complex language [0], and the complexity continues to increase. IMO it's too much for people who only write code for, say, a few hours a week.
> The new features are optional
I hope Python isn't becoming a "choose your own subset" language like C++.
The tweet shows a clear list of features (aside from the walrus operator) that are "complex" because they deal with inherit complexity. People who write code only for a few hours a week don't need to deal with asyncio or parallel programming, typing, cython, etc, etc.
The point about different frameworks and ecossytem is a strawman. Yes, it's large. No, it doesn't mean that is complex. No, you don't need to know all of it.
> I hope Python isn't becoming a "choose your own subset" language like C++
You are implying that are what is being added to the language have overlapping functionality. You really need a better argument than a FUD-y tweet to justify your concerns.
> Python now has a complex type system - that many developers never use.
Python doesn't have a complex type system. How could it when, to this day, it doesn't bundle a type-checker?
Python had a way to annotate functions (PEP 3107), which the community largely used for type-hinting, so Python introduced better support for type hints (PEP 484 and various extensions since).
> IMO the same tension exists in Python. The hobbyist-hackers are using the language to write small-ish scripts, and the enterprise-scale developers are working on million-line codebases.
It's an endless cycle in all aspects of IT, the conflict between small tech and enterprise tech
Large tools from bureaucratic enterprises are useless (that's a given)
Shadow IT built around hacker tools gets the business goals through
Enterprise IT see that these tools are popular and actually meet business goals, but they didn't invent them
Enterprise IT attempts to justify itself and find something approximating the usability of the good tools from some "safe" vendor like Oracle, Microsoft, etc. After all nobody got fired for buying IBM.
Enterprise IT spends a fortune, everyone hates it, but old tools are defunded so can't be used.
Shadow IT build new ecosystem around different hacker tools to get the business goals through
> Large tools from bureaucratic enterprises are useless (that's a given)
There are plenty useful tools that are large and built by bureaucratic enterprises. Stuff like ERP systems just has so many features to cover and tax laws to implement that it most likely can genuinely not be built by a small team.
(Disclosure: I work for such an enterprise, though I'm not involved in the development of software sold to customers.)
That many developers never use type annotations (nothing interesting has happened to the type system in a long time) kind of suggests that it's not a problem. The feature is there if you want it, if you don't you ignore it.
Type annotations aren't entirely optional, though. For example, they're required if you want to use data classes.
Even if they were optional, that would still be a problem. As above, too many optional features are turning Python into a "choose your own subset" language like C++.
>Named arguments don't do a lot for the first group. It looks like the big point in favor is not having to look up a reference for which-arguments-go-where in functions anymore, but a good IDE already does that for you.
I, for one, never particularly liked this last argument.
I get why it's being done that way, but the bit about parameter names being allowed to vary in subclasses feels... non-great.
As a result you can't really safely use named parameters on calls without more extensive control of what you're being passed: even if you typehint to a class or interface, you can just get passed a parameter-name-changed subclass that breaks your call anyway. Feels like a "strict" type warning would be in order rather than just silent acceptance of the variance combined with a hard error.
Still feels like a "strict warning" situation. Again, I can see why they have to do it that way because this was just not previously part of the public interface to a function, but it is now, so there should be some indication that doing this name mixing is, in fact, Now A Problem.
I guess it can just be handled on a static analysis/code-checker kind of level, but the language already has a system for emitting these kind of "code standards" warnings.
That's always been the way with PHP. It's always been the language of "you _can_ write nice code, but only if you follow strict rules and avoid this (long) list of bad practices that it encourages".
> Doctor, it hurts when I bend my elbow like this!
What's sad about it? Having interfaces defining the arguments of functions with complex parameters in a type safe manner and with support for auto-completion is one of the greatest thing to ever happen to JavaScript in my opinion. And you get named function arguments as a by-product of something way more powerful than just that.
I always wonder why PHP never seems to favour making breaking changes to keep things cleaner, if you aren't going to improve language constructs in a major version, when will they be cleaned up?
I think that post Python 3 nobody is going to do that.
It would be interesting to see a fork which removed all the crud though. Something that was easy to use for the person doing occaisional web work or beginner but without the traps and pitfalls.
Python 3 attempted too much change at once. The Python folks could have spread incremental rollout of the changes across several major version numbers and probably not have faced such slow adoption.
I'm not sure that they could have spread it out. I believe that was the point of Python 3. Still, PHP may end up in the exact same position Python was in for the last decade, if they tried to do a similar change.
It could be worse of cause, they could end up like Perl 6 (now Raku), which makes Python 3 look like a successful transition in comparison.
PHP 8 has plenty of breaking changes (https://github.com/php/php-src/blob/08e6c209550a268500bbdef4...), mostly in the department of throwing errors more aggressively, and fixing some long-standing "features" (like 0 == "foobar" being true). But there's a budget to this, and you have to strike a balance between how much code you break and what benefit you get out it.
> if you over supply a function by position today PHP doesn't error
I'd argue that this it the worrying part. How often do you intentionally supply an extra argument, and how often is it an accident? I'd take the warning.
^So this would error, even though it's a nice specification as far as the function is concerned, I'm all for strict static analysis but not at the expense of open maps
"If your program deals with information, these are among your primary problems: information is sparse, incrementally accumulated, open/extensible, conditionally available, formed into arbitrary sets in different contexts, merged with other information etc."
While you're right, I don't think that's something commonly used. I definitely can't remember the last time I wrote code where this would be useful.
That might be because usage of associative arrays is more or less discouraged in modern PHP in favor of value objects (especially with property promotion in PHP8). Main reason for that is that this usage is horrible for IDEs and static analysis. Way too easy to rename a parameter and create a runtime error. Typing is also enforced at usage instead of creation, and you have to repeat the type information in every function signature.
I really love this new feature however my only concern is that renaming some parameters will trigger a lot of changes in other places as a side effect, thus polluting diffs.
I get what you mean, but I remember at one point going through some kind of 'oh no, it's a large diff' or even 'do not do this because the diff will increase' phase. And I realized that's just not ok. There's already enough things to keep in mind when programming, adding an extra one just because some external tool which actually doesn't have a lot to do with how the code works, i.e. holding back changes because of diff, just isn't worth it.
Now, I admit cluttered diffs can be a problem, but there are workarounds: more fine-grained commits (especially separate commits for renames only) and better diff display. As an example of the latter: another commnter mentions one argument per line produces better diffs. It does for your standard display with +++/---, but tools like Sublime Merge have this covered and siplay the change in such a way you can immediately see what argument changed it's name, inline in the code. If it's was just a typo fix it will usually only highlight the changed character(s) not the complete name. Even going from all arguments on one line to one agrgument per line is covered, since it'll just display it as added whitespace and won't highlight the argument themselves indiacting they did not change.
My biggest pet peeve is when I meticulously craft a feature branch with commits neatly separated by subtopic so that it's pleasant to review... and then the reviewer just takes a glance, writes "lgtm" and squash-merges.
Not sure if the standard library has evolved since my PHP days, but this would at least solve the "key / haystack" issue. In which lots of the standard library had different orders in which parameters are passed. One can finally pass them named and no longer have to care about the order. This may also have been solved by better tooling since then, though.
> why they made array_map and array_filter switch parameter order
It kind of makes sense if you look at the signatures (array_map takes a callable and a variable number of arrays, while array_filter takes a singe array and an optional calable), but yeah, it's horrible to use.
This was my favourite thing about learning Swift. I hadn’t used a named argument language before. It feels very relaxing knowing that the compiler can check all these things for you. I haven’t used php for years but it’s cool to see it adopting this. Maybe I’ll give it another go!
C# has had named arguments for a while now too - and I’m in love with them. They’ve helped me avoid many ugly bugs caused by refactoring, such as when a method has many parameters with the same type - and a refactoring reordered or added/deleted parameters - thanks to named-parameters the call-sites still worked instead of silently failing (because the call-site would still be type-correct and compiled, even if it was intent-incorrect).
My only peeve is that VS still doesn’t let you style/colour named-argument labels in a different colour, so it does add to visual-noise in the editor.
Adding labels to every argument is just silly, so I do have a personal hard-and-fast rule that any call-site of a method with literal arguments must be explicitly labelled (especially booleans!), and any call-site of a method with consecutive homogenous typed parameters must also be labelled - these rules have worked out fantastically for me.
Definitely a top feature of crystal lang. Not having to worry about what the default was to the second positional argument while using the third is satisfying, not to mention that it's basically optional self documenting, too.
Named arguments is a no-brainer for me for any kind of programming language. I am hoping D adopts named arguments too, although it's a very uphill battle there with many programmers coming from C/C++ background being opposed to it in the name of API stability.
Named arguments are great where you can't do it without them. It's good that language provides you the tool. And it's your job to as a developer to not abuse it.
There was a time when I would've been all-out in favor for this because one of the most often used functions in Drupal had no less than seven arguments: https://api.drupal.org/api/drupal/includes%21common.inc/func... it being seven, the running joke was knowing the Seven Parameters Of L is our initiation rite.
And then URLs in 2015 became a value object and while you can still pass absolute => TRUE in options if you really want to, you can just call setAbsolute() on the object.
I guess if PHP core adopts this , phpstorm will quickly adopt as well and typing strpos( will immediately expand to strpos(haystack: , needle: so you can just fill that in. I guess? so maybe a reluctant yes but I am a bit wary of the APIs that will follow from this. I guess I have slowly grown fond of value objects? I must be getting old.
There are a lot of arguments surrounding this subject. Just came in here to chime about how I miss named arguments in c++. Sure, I can emulate them [0] but at the cost of ide support, runtime and compile time.
Yes, designated initializers help initialize a dto, much like what C# did back in 3.0 times. I wish they did something similar to function parameters so we didn't even have to create a data transfer object.
So here's a good test for the value of [language feature X]:
Do people naturally try and reinvent that feature in its absence?
So for named parameters you have:
1. Javascript would often be written with methods with a single "options" parameter, where "options" is an anonymous object and basically a map;
2. Day-to-day I write Hack. Many functions are similarly written taking a shape as an argument. Shapes are structs, basically, but the typechecker is smart enough to treat them as a well-defined by anonymous type.
3. I've also seen C code that does much the same with things structs.
Empirically there seems to generally be a demand for this kind of feature and it makes sense: long parameter lists are tedious and error-prone (have you ever seen a function that has 3 boolean parameters in a row?) where people try and be helpful by providing "optional" parameters (which are really just parameters with default values).
I can create an object, then dynamically add various parameters before sending them rather than bogging down the function call. Destructuring in particular ensures this is pleasant for both the function creator and consumer.
You see a similar debate between StandardML and Ocaml though on a more fundamental level. SML uses structurally-typed records, so you can instantiate an inline record without needing to add any types and the function will destructure it for use. Ocaml went with nominal typing making this painful, so they added a complex optional argument syntax instead. Typescript is also structurally typed and I'd guess this is the reason.
every wordpress, mangento and laravel developer which probably make up about 80% of the websites on the net. wordpress alone counts for 64% of the websites out there.
> named arguments allow you to pass input data into a function, based on their argument name instead of the argument order
Granted, I'm not a PHP developer, but I can't understand why a change like this would be controversial. It sounds like it's optional, and would help greatly to reduce mistakes when passing arguments to functions? Having multiple lines of things being passed to a function may look odd to some, but couldn't the same argument be made as with arrays, that one line per "thing" makes sense, if nothing else to clean up diffs when collaborating on code?
Granted, I'm not a PHP developer, but I can't understand why a change like this would be controversial. It sounds like it's optional,
It's only optional when you're writing code. Even if you don't use features in your own projects you're undoubtedly going to be reading a lot of other's code, which may or may not use these features, and from that perspective added language complexity is a burden.
Sure, but it's only confusing when reading it if it's hard to implicitly sort of understand what's happening when you're not used to it, right? Named arguments seem pretty straightforward to me, in the sense that it's relatively obvious what they are when you see them. It's not exactly like a new operator or some kind of symbol unique to the language that is almost impossible to understand without looking it up.
Any framework/library code which abstracts over invoking a callable must also be extended to support associative arrays, and to avoid assuming that parameters will be given in the order they're defined. For example, things which play around with call_user_func and func_get_args (e.g. delayed evaluation, partial evaluation, currying, mapping over datastructures, composing/pipelining, etc.)
It's optional for the caller, but not for the callee. I think the controversy centers around two things:
a) Specific to this feature: For library maintainers, this means that argument names are now part of the public API, whether I like it or not, and renaming a parameter from $orderBy to $sortBy is now technically a breaking change. Yeah, you can put something in the readme saying "Heads up: I don't support named arguments and intend to rename arguments in semver-minor releases", but should you need to do that?
b) General to all new PHP features: PHP is a great language, with lots of crap code written in it. Will this feature result in people writing better or worse code? Just because something is optional doesn't mean it's a good idea, if it will result in the overall code quality of the ecosystem to get worse. Some people think that named arguments will make it too easy to create monster functions with too many parameters.
I personally support (aka am DELIGHTED by) named arguments, but I'm sympathetic to the concerns of library maintainers and I don't see a better way around it than a remark in the readme (at least until/unless they introduce e.g. a @@NoNamedArguments annotation in PHP 8.1). And I think that the benefits of named arguments far surpass the risk that they encourage people to create methods with too many arguments. But I can see both arguments.
a) is a good point. I often rename parameters after a while to have more precise meaning. But I guess the advantages of named parameters outweigh the potential drawbacks.
As a python dev my mind is kinda blown that php didn't have keyword arguments yet. Especially with the current trend of using kwargs exclusively for better code quality and readability.
Having switched from 80% Java (20% about 5 other languages) to probably 75% Kotlin over the last year, one of my absolute favorite things named arguments.
When you name your arguments, simply supplying them will use the default if one has been specified. If one hasn’t been specified then you must provide it when calling and your compiler/IDE will definitely let you know.
I used to go out of my way with method overloads, JavaDocs, argument @annotations, and Builder classes in order to get this same level conciseness and cleanliness in my Java code. It is extra work (even with Lombok) but I personally can’t stand when I don’t keep it clean like that. Understandably a lot of other developers on my teams don’t always go to the same lengths.
I honestly haven’t ready the details on PHP’s plan for this, but with Kotlin’s names arguments I’ve found almost no need of method overloading anymore- most of those input variations are now covered (except for when you’ve got different types coming in, which usually can be handled with a common interface or base class).
Yeah I'm totally fine with named arguments, I even thought they would come sooner (I've been doing PHP since 1998 so I have some perspective ahah).
And yes overloading is more about different types for me. I guess I got accustomed to it with C# and nowadays Elixir. PHP 8 has union types so I guess it will do.
It's pretty funny that named arguments were, for a long time, a feature present only in Objective-C of all the languages I tried (and I tried many).
It was one of the features of Objective-C I liked and I missed in any other language. I also think they were one of the reasons many developers disliked the language.
When Swift was introduced, it got named parameters from Objective-C and now they are getting more "mainstream", entering in a language much more widespread than the former two.
When I was learning Verilog HDL, named parameters are commonly used and highly encouraged. I wondered why more traditional programming languages didn't use this concept aswell for the same reasons the article goes into.
I first ran into them in Python myself. After using kwargs for the first time, I wondered the same thing. While I try to limit the number of args I pass to functions and methods, sometimes it simply can't be helped. Kotlin is a wonderful example of how both positional AND named arguments can be supported.
It feels like an anti-pattern, but I've been burned so many times by forgetting what order things go in, and not wanting to waste the 10 seconds to go look, that in a lot of methods if I have more than 2 parameters, I'll convert it to an array.
the associative array, thus giving me the named attributes.
It's a bit hackety, but the new named params looks like a much more elegant solution.
One issue also is if you have a method that takes 3 arguments and the 2nd is an integer not a string, and you pass a string you'll get an error... using an array, you can just test if the array key exists or not, and skip functionality as dictates by what's passed in.
139 comments
[ 4.1 ms ] story [ 185 ms ] threadFor python-level functions initially all arguments could be passed either positionally or by keyword, unless they slotted into the special `∗args` (positional-only) or `∗∗kwargs` (keyword-only) parameters (note: the star-prefixes is the important part here the names can be anything).
Now I'm saying this was for python-level functions because (AFAIK) at the C level it was always possible to rather easily define positional-only or keyword-only arguments.
Anyway one of the improvements of Python 3 was to allow parameters other than `∗∗kwargs` after `∗args`, with the effect of making them individually named but keyword-only (PEP 3102). A logical extension of the same was to allow keyword-only arguments without any positional, that's what the `*` pseudo-argument defines:
> The reasoning behind this change is as follows. Imagine for a moment a function which takes several positional arguments, as well as a keyword argument.
> Now, suppose you wanted to have 'key' be a keyword-only argument. Under the above syntax, you could accomplish this by adding a varargs (nb: `∗ignore`) argument immediately before the keyword argument.
> Unfortunately, the 'ignore' argument will also suck up any erroneous positional arguments that may have been supplied by the caller. Given that we'd prefer any unwanted arguments to raise an error, we could do this (nb: add an explicit assertion that `ignore` is empty)
> As a convenient shortcut, we can simply omit the 'ignore' name, meaning 'don't allow any positional arguments beyond this point'.
I think JavaScript got this right where the semantic resolution steps of arguments pretty closely follow the steps for assignment, which means once someone understands the basic idea of object destructuring they get named arguments for free, in the form of destructured object parameters:
I think it's much more beautiful and convenient than your JS example which in all honesty looks necessary cluttered and ugly.
> cluttered and ugly
I won’t argue with you about visuals because I don’t think they’re all that important. But, I think it’s undeniable that reusing the same syntactic concepts across different semantic use cases is quite a lot more logically clean & beautiful than introducing new syntax that other HN commenters with past python experience can’t even understand. If someone sees the JS named parameter syntax at a declaration and has any JS experience, they’ll almost certainly figure out what to do - if someone sees the asterisk, even with Python experience, they’ll think “what the heck is this asterisk?” And again, the other commenters on this thread are clear evidence that this is indeed as unintuitive as I describe.
There's a collection of 0 arguments. the "∗" was introduced specifically as a shortcut for:
forgoing the name simply signals to the language that it should collect nothing.It's a logical extension of the assumption that anything which follows `∗arg` is a keyword-only parameter.
They are close enough cognitively to warrant reuse IMO.
Huh? It's both making parameters more explicit and functions using it more readable and self-documenting.
In fact, the old way, before bare / and * in args list, where functions essentially had mandatory optional-keyword arguments made every function create an API in violation of “There should be one-- and preferably only one --obvious way to do it.”
It is very much not readable, as evidenced by the other python-experienced commenters on this thread having no clue what it means (“readability” doesn’t mean “easy to understand for people who already know what it means”, as achieving that is a very low bar. What should be targeted is “obvious enough to have a clear meaning even to those who haven’t studied the exact section of the spec”, which is possible in the JS approach as they’re reusing the same syntactic constructs throughout the language, but is impossible in the Python approach because they introduce new syntax to work around every little limitation of their existing syntax)
Named argument are more explicit.
I wrote a lot of old python and js code with dict/object as argument, they were dreadful
There's a tension in PHP-land between PHP's roots as a low-ish level, get-it-done, hackish language, with its big standard library and simple scalar types, and the better-organized and quite vocal developers who want it to be more Java-like, with great big frameworks and many deeply-nested complex class hierarchies. Instead of unwieldy hobbyist-hacker balls of mud, you build enterprise-scale balls of mud.
Named arguments don't do a lot for the first group. It looks like the big point in favor is not having to look up a reference for which-arguments-go-where in functions anymore, but a good IDE already does that for you.
But there is a common anti-pattern where you have some polymorphic function or interface, and you want to accrete your arguments somewhere and then bundle them up and call the function, so then you see things like [1]:
...and that is bad because it totally bypasses all of PHP's type-checking and leaves it up to the called function to sanity-check the input types instead of letting the compiler do it, and basically nobody does that anyway.So named parameters would fix that at least, which would be nice. I'm just not looking forward to the impact this is going to have on code readability, because everyone (including this article) is going to go, "oh, now my parameter list is too long!", and break the function signature into one-line-per-parameter, which is vile.
[1]: I'm guilty of this too.
Speak for yourself. If each line indicates what it is the parameter for I couldn’t care less.
Nobody will use this for functions that have just two parameters. It’s the ones that have 10 possibilities that are crazy.
That's it, I'm officially a nobody :-)
You don’t check the input types?
Btw I don't always sanity check the parameters, mostly at the system edges only.
A method with N arguments, where N > 2, with a call that is messy due to the number of arguments could instead take a single argument being a parameter object where the constructor for the parameter object takes the same N arguments.
A constructor with N arguments with a call that is messy due to the number of arguments ...
Writing and maintaining parameter classes is just tedious.
As a JS/TS developer, all I see is avoiding two characters: {}, everything else is possible in JavaScript and well-typed in TypeScript.
PHP 8
JS 1Although there is external tooling that uses static analysis for such more complicated types.
like add(1,2,3,4,5) ->
If you know what arguments you're getting, then the more sound way would be just use an associative array, but either way you really don't get great type checking.This is a much better solution.
I can't help but feel if they just set up a clean way to name construct Params for classes this wouldn't need to happen.
What is the functional difference between a named construct "Params" and just allowing named parameters?
That "just" is doing a lot of work. I've not used PHP for a few years, but off the top of my head I can forsee the following problems:
- `call_user_func` would need to change to support this, e.g. accepting and passing-along named parameters.
- `call_user_func_array` would need to change, e.g. passing along associative-array arguments as named parameters.
- `func_get_args` would need to change, e.g. returning an associative array
Other parts of the standard library, like ReflectionParameter, would also need corresponding changes.
On their own I don't think those are necessarily bad. However, this would break a lot of code which relies on those functions and their invariants. For example, currying and partial application would become more complicated (e.g. http://chriswarbo.net/blog/2014-02-21-partial_application___... ).
I'm not necessarily against this change, but I would make the following points:
Extending APIs is a breaking change, since it breaks invariants/contracts that previously held (in this case by function definitions and call sites). This breaks backwards-compatibility, and that should be acknowledged and stated explicitly, rather than ignored. Here's a quick example of code which breaks, since it relies on the now-violated invariant that the parameter order of a function call matches that of the definition:
Secondly: there's a definite bandwagon effect between scripting languages like PHP/Python/JS/etc., for better (easier to transfer between) or worse (uncompelling monocultures lacking innovation/USP). Just because a feature exists in one, doesn't necessarily make it a good idea for the others (or the first, for that matter!); conversely, there are lots of great features out there which scripting languages don't seem to have picked up, and which are hardly ever brought up in these discussions. For example:- Default arguments are generally supported, e.g. `parseInt(i, base=10)`. Alternative approaches to the same problem, like currying, are mostly absent, e.g. `parseIntFor(base, i); parseInt = parseIntFor(10);`
- Generators are generally supported, e.g. `for x in foo: yield x`. Alternative approaches to the same problem, like delimited continuations, are mostly absent, e.g. `reset; for x in foo: shift(x);`. This is especially interesting, since it's just an extension of exception handling, which is another generally-supported feature!
- Named parameters are generally supported, e.g. `foo(a=1, c=5)`. Alternative approaches to the same problem, like row-polymorphism, are mostly absent, e.g. `foo(['a' => 1, 'c' => 5])`.
As for your argument that it breaks code that today relies on argument order... Even in current versions of PHP, nothing in the language stops me from changing function f($a) to function f($b, $a) between v1 and v2 of my library and your code is equally broken. Whether I as a function author decide to re-order my arguments is a decision informed but not dictated by the existence of named arguments.
I don't understand your rant about bandwagons/monoculture. If you ask me the reasons most popular langauges are moving in the same direction is because popular languages are stealing each others' best ideas, which is a good thing. All your "alternative" solutions read like horrible code to me compared to the now commonly accepted designs.
No, I wasn't talking about C; or about language implementations at all. I was arguing that the phrase "just allowing named parameters" gives the impression that this feature would only affect function calls and definitions, when that's not the case. The most obvious repercussions are for `call_user_func`, `func_get_args`, etc. whose API would need to change. That induces changes to code that calls those functions, and code that calls that code, and so on.
Although PHP is famously un(der)specified, I'm talking about things like "`call_user_func(f, args...)` is equivalent to `f(args...)`"; anything relying on that property will break in the presence of named parameters (since they may be given in a different order), unless patched to take it into account.
> Whether I as a function author decide to re-order my arguments is a decision informed but not dictated by the existence of named arguments.
Sure, but that's just a first-order effect. It's a breaking change for higher order code (where functions and arguments are data, and calling is an operation on that data). That's why I gave an example of higher-order code.
As a silly analogy: if PHP extended its default numeric types to be complex numbers, that wouldn't affect existing values like `$age = 18` or `$length = 50`, since they're "informed, but not dictated by the existence of complex numbers"; it also wouldn't affect calculations using these values like `plus($age, $length)`. Yet it would have profound effects on numeric functions, which may have to be patched to take complex values into account: some functions, like `array_sum`, might work fine as-is; whilst others, like `array_slice`, might make no sense given the new situation (how might we take `3+5i` elements from an array?).
> All your "alternative" solutions read like horrible code to me compared to the now commonly accepted designs.
This sentence is a good example of what my "rant" is about. Why should improvements depend on what's "commonly accepted" (surely that's antithetical)? Could you elaborate any further on what makes those examples "read like horrible code"?
As a more detailed example we can look at `switch`: it's clearly a "now commonly accepted design", due to its prevalence in many "popular languages" like PHP/Javascript/etc. An alternative approach which solves the same problem is `match`, as used by ML. However, ML is not a popular language, and ML also introduced currying which you say "read[s] like horrible code". Does that make `switch` better than `match`?
This is an interesting example since a recent Python Enhancement Proposal is trying to add `match` to the language. This proposal seems credible, since the author list includes Guido van Rossum (the creator of Python): https://www.python.org/dev/peps/pep-0622
Perhaps this is an outlier, since Python doesn't already have an equivalent like `switch`?
I wonder if the existence of that PEP changes your opinion of `switch` versus `match`? Maybe not; after all, Python doesn't have `switch` so maybe `match` is better than nothing. What if I showed you an equivalent proposal for adding it to Javascript (using the keyword `case`, like the non-popular language Haskell)? https://github.com/tc39/proposal-pattern-matching
At what point does a feature go from "horrible code" to "stealing each others' best ideas"?
What makes this even more interesting is that the very first sentence of PEP622 m...
It can be super useful even for functions with low arity e.g. `max(seq, key=func)`.
But anyway, I'd prefer anonymous records like in F#. There's no reason to have static and dynamic mappings have the same syntax, especially if they're not the same to the type checker, even a compiler could statically dispatch the static variant. So I agree with you that dicts are overused.
Are there really people aiming for that, as a goal? Or is it rather just the result of large-scale software? And/or if it's really complex, then perhaps that's just the result of suboptimal design, not the intent?
I'm being a bit hyperbolic, but I've worked that think a if/switch statements are a code smell that should be converted to complex class hierarchies.
In my experiences with C#, things like named params have been nice, even if it's often just syntactic sugar; these things can be quality of life improvements for the programmer without changing anything about the goal.
- no needle/haystack order problems anymore - no need going to docs to understand what a boolean or „magic value“ stands for
One of the hidden issues with the above code comes in the future: if anyone ever changes the order of the 2nd and 3rd arguments in the function definition, and forgets to update this spot, the code will still "work" (no compile error) but instead will have the wrong runtime behaviour. In a pull request, you can't see all the affected spots that didn't get updated, so it is easy to slip through.
Agree, and I'd like to add the original purpose of PHP, and what made it popular in the first place was that it can be used as embedded PHP within SGML-ish processing instruction ie <!php ...>. Only that PHP made such a lousy hack job of it being not HTML-aware and not doing context-dependent escaping, that it would become the primary script injection/XSS attack vector on the web. PHP's forte IMHO is its community and the large installed base of apps (there doesn't exist anything like Magento with its first-party plugins by DHL, UPS, payment providers, etc on Node.js for example). Why PHP wants to become more like Java or JavaScript is a mystery for me, when there's a hell of a cleanup job to be done beforehand, such as fixing <?php ...>.
IMO the same tension exists in Python. The hobbyist-hackers are using the language to write small-ish scripts, and the enterprise-scale developers are working on million-line codebases.
Unfortunately for the hobbyist-hackers, at some point (perhaps when the BDFL started working at Dropbox), the focus of the language shifted from the first group to the second. That's why, for example, Python now has a complex type system - that many developers never use.
What is recent on Python3 that makes it hostile to hobbyists? The new features are optional, it's not like Python is forcing people to adopt mypy.
Python has become a very complex language [0], and the complexity continues to increase. IMO it's too much for people who only write code for, say, a few hours a week.
> The new features are optional
I hope Python isn't becoming a "choose your own subset" language like C++.
[0] https://twitter.com/raymondh/status/1280946969116995584
The point about different frameworks and ecossytem is a strawman. Yes, it's large. No, it doesn't mean that is complex. No, you don't need to know all of it.
> I hope Python isn't becoming a "choose your own subset" language like C++
You are implying that are what is being added to the language have overlapping functionality. You really need a better argument than a FUD-y tweet to justify your concerns.
Python doesn't have a complex type system. How could it when, to this day, it doesn't bundle a type-checker?
Python had a way to annotate functions (PEP 3107), which the community largely used for type-hinting, so Python introduced better support for type hints (PEP 484 and various extensions since).
[0] https://www.python.org/dev/peps/pep-0586/
It's an endless cycle in all aspects of IT, the conflict between small tech and enterprise tech
Large tools from bureaucratic enterprises are useless (that's a given)
Shadow IT built around hacker tools gets the business goals through
Enterprise IT see that these tools are popular and actually meet business goals, but they didn't invent them
Enterprise IT attempts to justify itself and find something approximating the usability of the good tools from some "safe" vendor like Oracle, Microsoft, etc. After all nobody got fired for buying IBM.
Enterprise IT spends a fortune, everyone hates it, but old tools are defunded so can't be used.
Shadow IT build new ecosystem around different hacker tools to get the business goals through
And the cycle continues.
There are plenty useful tools that are large and built by bureaucratic enterprises. Stuff like ERP systems just has so many features to cover and tax laws to implement that it most likely can genuinely not be built by a small team.
(Disclosure: I work for such an enterprise, though I'm not involved in the development of software sold to customers.)
Even if they were optional, that would still be a problem. As above, too many optional features are turning Python into a "choose your own subset" language like C++.
I, for one, never particularly liked this last argument.
As a result you can't really safely use named parameters on calls without more extensive control of what you're being passed: even if you typehint to a class or interface, you can just get passed a parameter-name-changed subclass that breaks your call anyway. Feels like a "strict" type warning would be in order rather than just silent acceptance of the variance combined with a hard error.
As justified, it feels necessary for backwards-compatibility reasons since named arguments are not defined as named, just used as such.
I guess it can just be handled on a static analysis/code-checker kind of level, but the language already has a system for emitting these kind of "code standards" warnings.
> Doctor, it hurts when I bend my elbow like this!
> So don't bend your elbow like that.
It would be interesting to see a fork which removed all the crud though. Something that was easy to use for the person doing occaisional web work or beginner but without the traps and pitfalls.
It could be worse of cause, they could end up like Perl 6 (now Raku), which makes Python 3 look like a successful transition in comparison.
Python 3 got this so, so wrong. It changed enough to break everyone's code, but not enough to make upgrading worthwhile.
It could have changed more, or changed less - done right, either would have been better than what actually happened.
My understanding was that some of that already exists in Hack. Although it seems they might've not gone far enough.
Adding a key to an array shouldn't be a breaking change, if you over supply a function by position today PHP doesn't error
I'd argue that this it the worrying part. How often do you intentionally supply an extra argument, and how often is it an accident? I'd take the warning.
"If your program deals with information, these are among your primary problems: information is sparse, incrementally accumulated, open/extensible, conditionally available, formed into arbitrary sets in different contexts, merged with other information etc."
That might be because usage of associative arrays is more or less discouraged in modern PHP in favor of value objects (especially with property promotion in PHP8). Main reason for that is that this usage is horrible for IDEs and static analysis. Way too easy to rename a parameter and create a runtime error. Typing is also enforced at usage instead of creation, and you have to repeat the type information in every function signature.
Now, I admit cluttered diffs can be a problem, but there are workarounds: more fine-grained commits (especially separate commits for renames only) and better diff display. As an example of the latter: another commnter mentions one argument per line produces better diffs. It does for your standard display with +++/---, but tools like Sublime Merge have this covered and siplay the change in such a way you can immediately see what argument changed it's name, inline in the code. If it's was just a typo fix it will usually only highlight the changed character(s) not the complete name. Even going from all arguments on one line to one agrgument per line is covered, since it'll just display it as added whitespace and won't highlight the argument themselves indiacting they did not change.
It kind of makes sense if you look at the signatures (array_map takes a callable and a variable number of arrays, while array_filter takes a singe array and an optional calable), but yeah, it's horrible to use.
My only peeve is that VS still doesn’t let you style/colour named-argument labels in a different colour, so it does add to visual-noise in the editor.
Adding labels to every argument is just silly, so I do have a personal hard-and-fast rule that any call-site of a method with literal arguments must be explicitly labelled (especially booleans!), and any call-site of a method with consecutive homogenous typed parameters must also be labelled - these rules have worked out fantastically for me.
I don't know how well it fits into PHP, but if it's anything like in python or raku, go for it! :-)
Then the options collapsed into an array https://api.drupal.org/api/drupal/includes%21common.inc/func... and that was in 2008.
And then URLs in 2015 became a value object and while you can still pass absolute => TRUE in options if you really want to, you can just call setAbsolute() on the object.
I guess if PHP core adopts this , phpstorm will quickly adopt as well and typing strpos( will immediately expand to strpos(haystack: , needle: so you can just fill that in. I guess? so maybe a reluctant yes but I am a bit wary of the APIs that will follow from this. I guess I have slowly grown fond of value objects? I must be getting old.
https://github.com/nurettin/pwned/blob/770144a15efe7ab96bf4d...
Do people naturally try and reinvent that feature in its absence?
So for named parameters you have:
1. Javascript would often be written with methods with a single "options" parameter, where "options" is an anonymous object and basically a map;
2. Day-to-day I write Hack. Many functions are similarly written taking a shape as an argument. Shapes are structs, basically, but the typechecker is smart enough to treat them as a well-defined by anonymous type.
3. I've also seen C code that does much the same with things structs.
Empirically there seems to generally be a demand for this kind of feature and it makes sense: long parameter lists are tedious and error-prone (have you ever seen a function that has 3 boolean parameters in a row?) where people try and be helpful by providing "optional" parameters (which are really just parameters with default values).
So sure, I'm all for named parameters.
That's 2^9 states to consider.
I can create an object, then dynamically add various parameters before sending them rather than bogging down the function call. Destructuring in particular ensures this is pleasant for both the function creator and consumer.
You see a similar debate between StandardML and Ocaml though on a more fundamental level. SML uses structurally-typed records, so you can instantiate an inline record without needing to add any types and the function will destructure it for use. Ocaml went with nominal typing making this painful, so they added a complex optional argument syntax instead. Typescript is also structurally typed and I'd guess this is the reason.
https://w3techs.com/technologies/details/cm-wordpress
Granted, I'm not a PHP developer, but I can't understand why a change like this would be controversial. It sounds like it's optional, and would help greatly to reduce mistakes when passing arguments to functions? Having multiple lines of things being passed to a function may look odd to some, but couldn't the same argument be made as with arrays, that one line per "thing" makes sense, if nothing else to clean up diffs when collaborating on code?
It's only optional when you're writing code. Even if you don't use features in your own projects you're undoubtedly going to be reading a lot of other's code, which may or may not use these features, and from that perspective added language complexity is a burden.
a) Specific to this feature: For library maintainers, this means that argument names are now part of the public API, whether I like it or not, and renaming a parameter from $orderBy to $sortBy is now technically a breaking change. Yeah, you can put something in the readme saying "Heads up: I don't support named arguments and intend to rename arguments in semver-minor releases", but should you need to do that?
b) General to all new PHP features: PHP is a great language, with lots of crap code written in it. Will this feature result in people writing better or worse code? Just because something is optional doesn't mean it's a good idea, if it will result in the overall code quality of the ecosystem to get worse. Some people think that named arguments will make it too easy to create monster functions with too many parameters.
I personally support (aka am DELIGHTED by) named arguments, but I'm sympathetic to the concerns of library maintainers and I don't see a better way around it than a remark in the readme (at least until/unless they introduce e.g. a @@NoNamedArguments annotation in PHP 8.1). And I think that the benefits of named arguments far surpass the risk that they encourage people to create methods with too many arguments. But I can see both arguments.
foo('first arg', default, 'third arg');
Named parameters will do but they are more verbose.
Speaking whishes, method overloading would be great.
When you name your arguments, simply supplying them will use the default if one has been specified. If one hasn’t been specified then you must provide it when calling and your compiler/IDE will definitely let you know.
I used to go out of my way with method overloads, JavaDocs, argument @annotations, and Builder classes in order to get this same level conciseness and cleanliness in my Java code. It is extra work (even with Lombok) but I personally can’t stand when I don’t keep it clean like that. Understandably a lot of other developers on my teams don’t always go to the same lengths.
I honestly haven’t ready the details on PHP’s plan for this, but with Kotlin’s names arguments I’ve found almost no need of method overloading anymore- most of those input variations are now covered (except for when you’ve got different types coming in, which usually can be handled with a common interface or base class).
And yes overloading is more about different types for me. I guess I got accustomed to it with C# and nowadays Elixir. PHP 8 has union types so I guess it will do.
It was one of the features of Objective-C I liked and I missed in any other language. I also think they were one of the reasons many developers disliked the language.
When Swift was introduced, it got named parameters from Objective-C and now they are getting more "mainstream", entering in a language much more widespread than the former two.
the associative array, thus giving me the named attributes.
It's a bit hackety, but the new named params looks like a much more elegant solution.
One issue also is if you have a method that takes 3 arguments and the 2nd is an integer not a string, and you pass a string you'll get an error... using an array, you can just test if the array key exists or not, and skip functionality as dictates by what's passed in.