-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Syntax for keyword/labeled/named arguments #478
Comments
My preference is B (just |
I more or less agree with your ordering for characters (preferring
To the extent that argument defaults, names of returns values, defaults for return values, etc might be specified, I'd expect:
Then called:
(note the placement of |
The issue is that the name of the label may need to be different from the name of the variable. For example, in
The return value of I updated the text to hopefully make this more clear. |
is supposed to mean: "F takes two I updated the text to hopefully make this more clear. |
Another clarification: I'm assuming something like the Swift model, where there is an optional syntax to indicate a name in the function declaration, and if it the parameter is named the argument has to be named as well. Not like the Python model, where you have a choice about whether to use the name at the caller. I updated the text to hopefully make this more clear. |
I'm concerned that option B looks too much like an assignment, and that this would mean that the same syntax ( How do we imagine default arguments fitting into this, if at all? Assuming we support default arguments,
... seems unappealing to me. So I'm leaning towards option C being my preferred approach; the
... seems quite nice to me syntactically. |
Regarding argument labels, as I commented on #339, it's not clear to me why they should be provided. I don't understand why developers shouldn't be expected to rename their arguments when they want callers to refer to them by a different name:
The main use-case I can see is in refactoring, when renaming parameters: however, argument labels don't appear to assist in that in Swift. By my reading, keywords are only available under one label, and so not useful when renaming with call-sites that specify by label. Also, incremental refactoring could be handled by providing an overload, such as: fn DoSomething(Int new_name = 3) ...
fn DoSomething(Int old_name = uninit) { DoSomething(new_name = old_name); } (i.e., using I will note though, allowing (requiring?) everything to be specified by argument name creates a refactoring impairment, in that it means renaming parameters is a significant refactoring. As a consequence, it may be preferable to constrain to opt-in at the function site, rather than Swift's opt-out approach. Is there rationale for Swift's argument label feature? Am I on the fringe for being hesitant about argument label support? |
For Swift discussion, argument labels appear to be covered by SE-0001: the goal was to allow language keywords ( Do we want to use that approach? I think we may already have the particular issue covered, if desired, under the "raw identifier" idea which allows identifiers to be retained even if new keywords are added that overlap with them. |
I think I should also note, |
The Swift docs offer this rationale:
However, I think this rationale really doesn't work if the separator is
I'm hesitant about Swift-style argument labels too. Simple examples like the above are appealing, but also make this approach seem fairly ad hoc. For example, However, I think if we assume that a parameter list is a restricted kind of tuple pattern, then it actually seems very difficult to avoid having the parameter name be separate from the argument label, even if the function author wants them to be the same. |
I think there is a communication gap here. I don't perceive this feature as having anything to do with what you are describing.
I listed what I perceive as the benefits at the very beginning of the issue. I just reformatted them so they should stand out more now. |
I'm going to remove the function declaration syntax from this issue, since that is more complicated. |
I've also removed the destructuring, since that should ideally use the same pattern syntax as function declarations. |
An argument against using the dot/designator syntax "A" is that |
I've been having some conversations about this recently, and with the recent resolution of #542 I think we are in a better position to answer this question. What I've been hearing and thinking:
To be clear: I absolutely haven't talked to everyone so please do chime in if you feel differently! That being said, from what I heard there are two top contenders: The struct literal approachInstead of having a dedicated syntax for specifying named arguments directly, we lean into passing an anonymous "options" struct literal as the last argument of a function. For example, using the conventional choice of writing struct literals inside curly braces
Presumably this last argument would be optional if the function specified defaults for all of the fields of the options struct. This argument would not really be special except by convention; you could just as well pass in any value in that position as long as it had a type that could be converted to the struct type expected in the function declaration. Advantages:
Disadvantages:
You might also like this approach as a temporary solution, postponing the inclusion of a dedicated labeled argument syntax until we get more information. Option "B", or "the Python approach"This option is basically: "I think we want a dedicated syntax for writing keyword arguments, lets go with what's popular." You might write:
If we wanted to encapsulate a set of keyword option values in a struct value, presumably you would use them in an argument list using the Advantages:
|
FWIW, I like focusing on these two high level options. I actually think they are both based on fairly proven approaches that have found to be accessible and popular in languages (JS/Go on one hand, Python on the other). That's part of why I think they somewhat stand out as good ways to model this. I also can see reasons to consider the lack of parity an advantage -- if we want to encourage use of positional parameters where they make sense. But maybe its more a consequence of the design choice here: whether named (and non-positional) arguments are at parity indicates whether the language is (somewhat) opinionated about their use. I lean slightly towards encouraging positional parameters when it makes sense, but I know others feel differently about that. My leaning comes from making APIs in Carbon stay a bit more similar to C++ APIs. |
Please let us know your opinion! Vote here: https://discord.com/channels/655572317891461132/709488742942900284/846932433832378410 |
Adding a comment here to just clarify where my opinion came from ... The only part of option "B" that really bothers me is that the identifier looks like an unqualified identifier. I understand that we can look ahead to the |
Just to leave a note here that the leads are explicitly deferring this question and #505 . We're not opposed to named parameters and arguments, but the initial motivation for prioritizing this right away seems better addressed separately, and it seems valuable to more fully understand the expected syntax for things like struct literals and pattern matching generally if possible to better inform any decision. Leaving the question open to make it clear that this is something we can and should expect to revisit in the future. |
"Named parameters", "named arguments" or "keyword arguments" are great for:
They are used productively in many existing languages.
What syntax should we use? Whatever we choose should be consistent for parameter lists, argument lists, struct literals, destructuring, etc. This issue is going to just focus on argument lists in function calls, and struct/tuple literals. The questions about how they should be written in function declarations, destructuring, and pattern matching should be tackled in another issue.
Here are the top three candidates:
A. Designator
=
syntaxHere
F
is a function that takes two integer arguments, labeled.a
and.b
, and returns a pair of integers, with the elements labeled.c
and.d
. We callF
with an argument value of3
for.a
and4
for.b
. We compare that to a pair with elements labeled.c
and.d
. We then declare a variableg
that can hold a labeled pair without destructuring.Advantages:
B. No-dot
=
syntaxHere
F
is a function that takes two integer arguments, labeleda
andb
, and returns a pair of integers, with the elements labeledc
andd
. We callF
with an argument value of3
fora
and4
forb
. We compare that to a pair with elements labeledc
andd
. We then declare a variableg
that can hold a labeled pair without destructuring.Or without spaces around the
=
, following Google's Python style:Advantages:
Disadvantages:
a = 3
means something very different as a statement vs. in an argument list. I believe this partially motivates Google's Python style for not using spaces around the=
when it is a keyword argument.C. No-dot
:
syntaxHere
F
is a function that takes two integer arguments, labeleda:
andb:
, and returns a pair of integers, with the elements labeledc:
andd:
. We callF
with an argument value of3
fora:
and4
forb:
. We compare that to a pair with elements labeledc:
andd:
. We then declare a variableg
that can hold a labeled pair without destructuring.Advantages:
Others
Other approaches used by languages (found from Rosetta Code):
:
to introduce keywords in argument lists.->
:=
=>
The text was updated successfully, but these errors were encountered: