Pattern-esque syntax for terser named arguments/record fields
The pattern-matching syntax introduces the constructs :var foo and :foo to declare a variable whose name matches a field on the object being matched. The purpose of this is to reduce duplication, to users don't have to explicitly write foo: var foo.
I propose using this syntax as a way to reduce duplication when passing named arguments (or constructing records). Specifically, make foo(:argument) equivalent to foo(argument: argument). Similarly, record literals could be declared using (:field) instead of (field: field). This would help reduce duplication and ease acclimation to Dart for users coming from JS, which supports similar behavior when using maps as named arguments (in JS, foo({argument}) is equivalent to foo({argument: argument})).
It's definitely going to be briefer, and is likely to get used a lot.
We'd have to define which expressions counts as having a "name" that can be used as parameter/field name.
- A plain identifier is a given.
- A prefixed identifier too, like
: prefix.varNameor: Class.staticVarName(and: prefix.Class.staticVarName). - Then we might as well allow
: expression.getterNamein general, so any expression ending in.identifier. - And parenthisation should be safe, even if never necessary:
: (expression.getterName)
Those are fairly safe. Then there are the more speculative ones:
:foo.v1 ?? bar.v1- both have the same name, use that. Similar for||,&&and?/:.:foo.v1 ?? (throw "Nope")- only one branch has a name, use that.:await v1- the await doesn't have to affect the name, the name's meaning is likely to still apply.- Other prefix operators?
: ! foo.hasBar- probably not useful since!changes, flips, the meaning, so why should the name still match? - Increment operators:
: ++varName,: varName++can both work, the name and value still match conceptually. - Assignment
: foo = 42. Probably too weird, even if there is a name right there. - But maybe
:foo = barusingbaras the name (or whatever the name of the RHS expression is), and ignore the LHS. - No binary operators otherwise. Generally, we want expressions in tail-like position, so the "name of the expression" still corresponds to the value.
- More?
The analogous feature in JS only works for plain identifiers, but it would be pretty neat to be able to use at least expressions ending in .identifier. Most of the others seem confusingly weird to me.
I really like this idea. As @nex3 suggests, I'd probably want to stick to simple identifiers and maybe identifiers wrapped in prefix expressions (which we allow for patterns). Going beyond that gets weird fast.
Terse bools would be nice too:
(+:foo, -:bar) => (foo: true, bar: false)
Based on an internal discussion, I did a quick scrape of of a corpus of pub packages to see how often this feature might be useful. I only considered the simple version of the feature where the named argument expression needs to be a simple identifier (foo) and not a call chain that ends in a getter like other.stuff.foo.
Looking at all named arguments:
-- Named arg could use (6240345 total) --
5856229 ( 93.845%): No ====================================================
384116 ( 6.155%): Yes ====
Out of all named arguments, about 6% are an identifier expression where it's the same identifier as the argument name (foo: foo).
Looking at only named arguments where the expression is an identifier:
-- Identifier arg could use (663720 total) --
384116 ( 57.873%): Yes ================================
279604 ( 42.127%): No ========================
When the expression is a simple identifier, it's the same identifier as the argument name nearly half the time.
This seems fairly useful to me.