Named arguments are coming in PHP 8
stitcher.io
stitcher.io
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]:
call_user_func($function, ['length' => $n, 'foo' => $bar, ...]);
...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.
It can be super useful even for functions with low arity e.g. `max(seq, key=func)`.
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
save(
value: 3
)
JS 1 save({
value: 3
})Although there is external tooling that uses static analysis for such more complicated types.
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 ...
call_user_func_array($function, ['length' => $n, 'foo' => $bar, ...]);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.
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.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:
function call_with_strings() {
$args = func_get_args();
$f = array_shift($args);
$strings = [];
foreach ($args as $arg) {
$strings[] = strval($arg);
}
return call_user_func($f, $strings);
}
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 makes it clear that this is a case of stealing a good idea:
> This PEP proposes to add a pattern matching statement to Python, inspired by similar syntax found in Scala and many other languages.
To add a further wrinkle, PEP3103 was rejected for proposing essentially the same idea back in 2006: https://www.python.org/dev/peps/pep-3103/
Perhaps it was too soon to adopt ideas from Scala, since it had only been out for a couple of years by that point. However, `match` wasn't invented by Scala; it's been around in multiple languages, some more popular than others, dating back to 1973!
As further evidence of this, there was another rejected PEP for the same basic idea, from way back in 2001: https://www.python.org/dev/peps/pep-0275
I wonder whether your line of reasoning would come out in in favour of PEP622 today, whether it would have come out in favour of PEP275 twenty years ago, and whether it will argue in favour of `match` twenty years from now?
- no needle/haystack order problems anymore - no need going to docs to understand what a boolean or „magic value“ stands for
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.
I, for one, never particularly liked this last argument.
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.
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.
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.
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.)
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++.
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).
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++.
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 ...>.
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.
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?
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.
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.
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.
funcy(null, null, null, null, null, null, cake, null, lie)... def foo(*,a,b,c)...
or def foo(obvious_and_required_thing, *, a,b,c) ...
and noticed an immediate improvement in clarity.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:
const foo = (positionalArg1, {namedArg1, namedArg2 = 'foo'}) => { ... }
foo(1, {namedArg: 'bar'})
// resolved same as
const [positionalArg1, {namedArg, namedArg2 = 'foo'}] = [1, {namedArg: 'bar'}]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:
def foo(a, *ignore, b):
if ignore:
raise TypeError
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.
*args
creates a catch-all variadic function parameter called `args` which matches all positional parameters not heretofore assigned *
(same operator, without a name following) indicates that no more positional parameters may be matched after this pointThey 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
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'.
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.
I don't know how well it fits into PHP, but if it's anything like in python or raku, go for it! :-)
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.
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.
> Doctor, it hurts when I bend my elbow like this!
> So don't bend your elbow like that.
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.
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.
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.
That's 2^9 states to consider.
They're absolutely critical to writing code that is easy to read. The name of a function tells you what it does, arguments equally need names to tell you what they do.
Even if I have a great IDE, I don't want to hover/cursor over the line to see what each argument does, each random "0" or "null" or "1" or whatever. There are also plenty of times I'm viewing code that isn't in an IDE, like a diff.
Obviously they're not always needed, like in functions that take only one or two required obvious parameters.
But otherwise, they can make the intent of a line of code obvious at a glance, and are a huge step toward making code self-documenting.
I just can't believe this is a change that's only picking up now, rather than 20 years ago.
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.
<?php
$user = [
'age' => 25,
'name' => 'Brent',
'email' => 'brent@stitcher.io',
];
function send_email(string $name, string $email) : void
{
//...
}
send_email(...$user);
^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."
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.
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.
https://github.com/nurettin/pwned/blob/770144a15efe7ab96bf4d...
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 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 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.
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.
My understanding was that some of that already exists in Hack. Although it seems they might've not gone far enough.
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.