Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upDefault arguments and keyword arguments #6973
Comments
This comment has been minimized.
This comment has been minimized.
|
There have been no plans for it, though nobody has yet voiced any opposition. I've been thinking about this issue for a long time, and it basically boils down to a request for default arguments. I suppose it would look something like: fn foo(bar: int, qux=2: int, ham=0: uint) { ... }Which could then be called as EDIT: Please see huonw's proposed syntax below, which looks much better and more uniform with the current declaration syntax. |
This comment has been minimized.
This comment has been minimized.
|
Triage 2013-08-05: no progress (that I know of), although it'd still be neat; maybe the declaration syntax could look like fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }where the RHS is any constant expr (i.e. things that are valid in a |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Aug 9, 2013
|
IMO these are really useful, even the C++ system of default arguments where you can only fall back to defaults for trailing unspecified arguemnts would be great... cuts down on the number of alternate named function variants whilst not being as complex as overloading .. being able to declare those in structs and enum variants aswell would be great. would it be viable/useful to implement as expressions ...in terms of earlier specified arguments & generic type parameters (eg.. ability to lookup a zero:: , or write things like slice(&self, start:uint,end:uint=self.len()) /* "foobarbaz".slice(6) == "baz" .. or create_window(parent,x,y,width:uint=parent.width()/4,height:uint=(width_161)/100) /_ default is golden-ratio shaped windows, and 1/4 screen width if you specify no size at all.. */ .... or does that just sound like a recipe for code bloat.. c++ style defaults would preclude haskell-like partial functoin-application, but i heard that was unlikely to go in? |
This comment has been minimized.
This comment has been minimized.
|
Here's another idea for default arguments. Rather than allowing arguments to be completely elided, we allow arguments for which a default exists to be invoked with a So given @huonw's lovely example of: fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }...we could invoke this as However, this idea wouldn't really provide any benefits to adopting keyword arguments, given that it's still strictly positional. |
This comment has been minimized.
This comment has been minimized.
|
Changing title to "Default arguments and keyword arguments". |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Aug 11, 2013
|
not implemented, but i tried adding to the ast & parser; https://github.com/dobkeratops/rust/compare/default_args, is this the right way to go about it ? (Option@expr .. it'll need contstraints on what the expr can be ? ) |
This comment has been minimized.
This comment has been minimized.
|
@dobkeratops that looks good (although the The constraints on the |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Aug 11, 2013
|
Ok the field is named so thats un-necasery annotation. in the name of 'continuous integration' , is it worth me submitting the trivial groundwork as a pr, if I fizzle out on this maybe someone who knows the codebase better could do the other parts a lot quicker another time . Its only destructive if you definitely decide against them ? Someone suggested this sort of thing can go in with a -Z option (..Session::debug_opts...but i didn't find these swere currently propogated everywhere (eg in the parser). I can see you might object to the compiler parsing something it doesn't yet use, i could change it from a warning to an error perhaps... |
This comment has been minimized.
This comment has been minimized.
|
@dobkeratops Note that no official developer has commented on this yet, so it's very unlikely that any PR would be accepted. There's a lot of dicussion to be had here regarding 1) whether we want default arguments, 2) if so, what the syntax should be, and 3) whether we want keyword arguments (because the semantics that we choose for default arguments could preclude this). |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Aug 11, 2013
|
Ok well i'd guess they'd be a very low priority for a long time yet,since they dont block anything (they're certainly not top of my own wish-list). |
This comment has been minimized.
This comment has been minimized.
|
I can see that named arguments aren't critical, but IMO optional args kind of are. Because in some places I think I saw a few methods with similar signatures and similar names just to offer easier interfaces when a given arg is not needed. It would suck if we end up with legacy functions like that just because a feature was missing. |
This comment has been minimized.
This comment has been minimized.
|
There are a lot of subtle parts in here. Anything default arguments are an implicit form of overloading, so (I'm guessing) likely get integrated with the trait resolution algorithm and/or type inference. It might not be super difficult but it's worth careful design, and at this point I'm not sure we have the time budget for it. As a language feature I imagine it's backward compatible; as an api feature it would probably inform api design some, so represent b-c risk. |
This comment has been minimized.
This comment has been minimized.
|
@Seldaek I agree that if we want default arguments, then it's important to implement them before we commit to stdlib backwards-compatibility. However, I know of at least one language (Go) that philosophically rejects default arguments in favor of the functions-with-slightly-different-names-and-signatures approach. However, unlike Rust, Go has a few features that make the absence of default arguments less painful. For one, they have variadic functions.[1] Secondly, you are allowed to omit fields when creating structs, which means that it's easy to pass a configuration struct to a function (the omitted fields appear to be set to compiler-specified(?) default values, rather than being something a user can customize).[2] For the former, we might be able to get away with macro-hackery to accomplish the same result (though at laborious implementation cost). Indeed, the forthcoming replacement for ifmt!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3)This is code that works today. As for the latter, the best analogue that we have would be to use functional struct update as per the following: struct Foo { x: int, y: int, z: int }
impl Foo {
fn default() -> Foo {
Foo{x: 0, y: 0, z: 0}
}
}
fn bar(f: Foo) {
printf!(f);
}
fn main() {
bar(Foo{y: 5, ..Foo::default()});
}I've seen this method offhandedly suggested in the past as a workaround for the lack of default arguments, but it's quite ungainly in practice (compare To reiterate, as one who comes from Python and Javascript I'd obviously welcome default arguments. However, I do somewhat empathize with Go's philosophy of making APIs explicit (in exchange for (potentially greatly) inflating the number of functions in the API itself). [1] https://groups.google.com/d/msg/golang-nuts/NWMReL1HueQ/X9mdYduCOB8J [2] http://stackoverflow.com/questions/2032149/optional-parameters |
This comment has been minimized.
This comment has been minimized.
|
I should also point out that it interacts with closures and taking first class references to functions. |
This comment has been minimized.
This comment has been minimized.
|
@bstrie thanks for the overview of Go. I agree with the point that overloading is probably a bad idea because it creates confusing APIs. Similarly the "overloaded" methods of jQuery that can take a callback in pretty much any argument you want are very weird and a pain to implement. And I sort of agree that wrapper functions to fill in default args are not so bad in some cases. But the problem is while it saves you from some confusing cases, it does introduce a lot of boilerplate in others. One example is |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Aug 12, 2013
|
I can see its a good time trade-off to defer them; I definitely miss both overloading and defaults from C++ .. but I've been happy to trade them for Rusts' other conviniences and the promise of a C++ alternative (after being stuck with 1 main language for so long..) For an experiment I thought about trying to do them as a 'parser hack' ... working at the level of macros? inlining the default expr's at the call sites where arguments are missing? One thing i'm still really getting used to is that methods are in theory much more useable in rust. |
This comment has been minimized.
This comment has been minimized.
|
Just so the idea doesn't disappear: the chatty aspect of If we made it easier to leave out fields in a struct construction, letting them fall to a default, then maybe this approach would be more palatable. E.g. a new form, probably defined solely for structures that implement some appropriate trait (
Then the example is "just" |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Aug 13, 2013
|
I suppose enums also give you some other ways of conviniently passing groups of optional params differently to what a C++ programmer is used to ... and a way of interspersing annotation & values with a call. |
This comment has been minimized.
This comment has been minimized.
|
@pnkfelix Remember that the html::start_server(html::ServerOpts{port: 10088, *});It does look slightly nicer. Still not certain if it helps enough to warrant new syntax, or if it suffices to satisfy all use cases of default arguments. In fact, to use my own example, when initializing something with a lot of options such as an HTML server I'd probably be just as happy setting up a I think perhaps we need to rethink where default arguments would be most useful. For me, it's not where there's a lot of potential arguments; these should be fairly rare, and a few lines to init a configuration struct is fine. Rather, I most miss default arguments when 1) the operation is fairly common, and 2) the operation has, at most, one or two arguments that are almost always superfluous but required for completeness. For example: let foo = Some('a');
foo.unwrap(); // same as today
foo.unwrap('z'); // imagine that this replaced unwrap_or_default
int::from_str("4"); // same as today
int::from_str("4", 16); // imagine that this replaced from_str_radixHowever, now that I've typed these out, I actually prefer the explicitness of the separate methods. :) Maybe I don't really know what I want after all! @dobkeratops, from your experience with C++, can you give us some examples of nice C++ APIs that are enabled by default arguments? |
This comment has been minimized.
This comment has been minimized.
|
Comments in https://github.com/mozilla/rust/wiki/Meeting-weekly-2013-08-13 seem to indicate that the devs see this as a far-future feature. Nominating. |
This comment has been minimized.
This comment has been minimized.
|
OCaml does optional/default argument without overloading, I believe. Optional arguments of type T without a default value are implicitly converted to T option and the function must check if a value was provided. There are also restrictions on the declaration and use of optional arguments to make the compiler know when an argument has been omitted. All optional arguments must have labels (they must be keyword arguments in order to be optional). This complicates OCaml's type system (labels become part of the type of the function) but I think this is an artifact of having to interoperate with the rest of the Hindley-Milner type system. |
This comment has been minimized.
This comment has been minimized.
pauljurczak
commented
Aug 14, 2013
|
Coming from C++, I would really miss default argument values. I often use functions with large number of parameters, where most of them almost always have default values, except of some rare use or test cases. Adding purely combinatorial function name derivatives in lieu of default values, would create a big mess of really long names. |
This comment has been minimized.
This comment has been minimized.
|
I agree that default arguments and/or keyword arguments would be extremely useful. I think we should work towards a pre-1.0 proposal that outlines what needs to be done in terms of overloading and perhaps have a discussion on the syntax. |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Aug 14, 2013
|
Good to see more people commenting in favour :) Another reason I wanted it was to increase the amount of C++ APIs that could be translated seamlessly. Using C++ libraries is one big reason why we're stuck with C++. I suspect the only way we'd get this if the community could implement it without any cost to the core developers. This is why i wanted to submit the groundwork on parsing it into the AST data-structure. It would be ready for someone else to pick it up and try to get a solution. initial feedback discouraged me from making a PR. from c++ I look at what scala and python have on this with envy. A native non-GC language with that elegance would be great. the difficult bit is the type checker, preventing subtle bug possibilities and confusing errors if it was just done as a parser hack, it think. imagine if you could just write things like this..
just hack that sort of thing into the AST without error checks, i'm sure a lot can go wrong .. but imagine if we could fix that to do what was expected :) |
This comment has been minimized.
This comment has been minimized.
|
Just a bug. We can debate the merits, but won't block a release on it. |
This comment has been minimized.
This comment has been minimized.
|
I like the behavior that @tautologico describes in his comment. Applied to Rust, I believe this would translate to the following: fn ga(bu: int, zo: Option<int>, meu: Option<int>) {
let zo = zo.get_or_default(42);
let meu = meu.get_or_default(99);
...
}And these would all be valid calls : ga(10, 20, 30); // 20 and 30 are automagically
// converted to Some(20) and Some(30)
ga(10, 20); // ga(10, 20, 99)
ga(10); // ga(10, 42, 99)
ga(10, None, None); // ga(10, 42, 99)
ga(10, 20, None); // ga(10, 20, 99)
ga(10, None, 30); // ga(10, 42, 30)The rule is that the trailing This proposal allows the caller to choose precisely the parameters he wants to provide and the ones he wants to keep defaulted, independently of its position. Also, I think it's a good thing that the default value does not appear in the parameter list. This way, the default value is not limited to a constant expression, it can depend on the state of the object, or it can depend on other parameters. Real life example of a function that shows a GUI dialog box : fn showDialog(message: ~str,
parent: Option<Widget>,
title: Option<~str>,
type: Option<DialogType>,
icon: Option<Icon>) { ... }
// Display an info box in the middle of the screen.
// Set the title to "information", translated in the current locale
// Set the icon to an "info" icon
showDialog(~"hello, world!");
// Display a warning box in the middle of the screen.
// Set the title to "warning", translated in the current locale
// Set the icon to a "warning" icon
showDialog(~"sick, sad world!", None, None, WarningDialog);
// Display a warning box in the middle of the screen.
// Set the title to "warning", translated in the current locale
// Set the icon to a custom icon
showDialog(~"sick, sad world!", None, None, WarningDialog, bugIcon);In this example:
|
This comment has been minimized.
This comment has been minimized.
|
You're now also proposing adding |
This comment has been minimized.
This comment has been minimized.
fread2281
commented
Sep 8, 2013
|
I'd like to add that IMO default args are a billion times better with keyword args. I don't like the idea of default args but no keyword args. |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Sep 8, 2013
|
one is a stepping stone to the other and defaults are stilll useful on its own, you have some extra flags.. and can leave them off if you just want the defaults.. |
This comment has been minimized.
This comment has been minimized.
|
Default values for positional arguments results in cryptic code. Why not only have defaults for keyword arguments? (If these are implemented). |
This comment has been minimized.
This comment has been minimized.
|
Hands down, Rust is an amazing language. It does so many things right it's not even funny. But I think postponing this feature for post 1.0 is a mistake. Let me elaborate. Default function arguments are critical for good API design; the lack of them complicates what would otherwise be simpler APIs. Examples from the std library:
And others. Having all these extra function names that could be removed with default arguments unnecessarily increases cognitive load. The user now has to know two (or more) names instead of one. If default arguments are postponed for after 1.0, then these functions in the std library cannot be removed because of backwards compatibility. This means that even if default arguments are added in the future and the "vanilla" version of the functions can now do the work of the other variants, the old variants will stick around and developers will have to know about them because someone will certainly use them, even if only by mistake. So that extra cognitive load will remain forever. [N.B. The same argument can be made for parameter type-based function overloading (the current trait-based workaround is too cumbersome and the std library functions don't use it), but this issue is not the place for that discussion.] Rust devs, thanks for working on Rust and making it awesome! |
This comment has been minimized.
This comment has been minimized.
|
I'm adding the "far-future" milestone to emphasize the extreme discouragement that we on the core team wish to communicate to anybody spending a second of time on this issue. If you want to contribute to Rust right now, please tackle one of the 41 open bugs on milestone 1 (well-definedness): https://github.com/mozilla/rust/issues?milestone=12&state=open or one of the 104 open bugs on milestone 2 (backwards-compatibility): https://github.com/mozilla/rust/issues?milestone=13&state=open or one of the 68 open bugs on milestone 3 (features we agreed at one point are crucial for releasing Rust 1.0): https://github.com/mozilla/rust/issues?milestone=14&state=open or even just comment on one of those bugs to ask for clarification or suggest ways to make progress. That's 213 bugs to choose from; making progress on any one of them at this point would be much more valuable to Rust than this issue. And anyone who can close one of these bugs will have our utmost gratitude. |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Oct 7, 2013
|
I can see the core team has way more important things to do but it seems a shame to 'communicate extreme discouragement' :( |
This comment has been minimized.
This comment has been minimized.
|
@Valloric I entirely agree (as I said before), and really hope the rust core team reconsiders or at least re-discusses the optional args because that impacts API design. Named args can definitely wait since they mostly impact the call-site (of course the name of args becomes part of the API design once we have named args, so it does have a slight impact but not a BC-breaking one if the args names are cleaned up when named args are introduced). |
This comment has been minimized.
This comment has been minimized.
|
There isn't consensus that this would be a good language feature. There's also no concrete proposal with all the details worked out for named/default parameters. It's not going to happen for 1.0, because there's an existing set of core languages features to either fix or remove before dreaming up very unessential syntactic sugar. |
This comment has been minimized.
This comment has been minimized.
dobkeratops
commented
Oct 7, 2013
|
'dreaming up' makes it sound like its something fanciful, but these features have been proven in other languages. Its not just syntax sugar - its reducing the amount of code you have to navigate through, reducing the number of symbols you have to learn |
This comment has been minimized.
This comment has been minimized.
I'm not saying that the other language features that need to be fixed/implemented are not more important; they probably are. But it's not either/or. All I'm saying is that this feature should be strongly considered for 1.0 (in addition to the other features) because not having it impacts the quality of the APIs in the std library forever. This is probably a controversial opinion, but IMO programming languages live and die more by the APIs they provide than the core features in them. Python's "batteries included" std library sold the entire language. CPAN keeps Perl alive. .Net makes writing C# code awesome, and LINQ is the best thing since sliced bread. One of C++'s greatest failings is the lack of good, standardized APIs. Etc. The APIs are critical to the success of a language so features that enable the creation of good APIs shouldn't be discarded as "unessential syntactic sugar". |
This comment has been minimized.
This comment has been minimized.
|
@dobkeratops: by saying dreaming up, I'm trying to emphasize that no complete proposal has been made so it's not at a decision-making step |
This comment has been minimized.
This comment has been minimized.
|
This discussion is unproductive. To prevent it draining any more of anyone's time, I'm going to close the issue. If somebody wants to reopen it (or start a new issue) a year from now, or later than that, that would be fine. |
catamorphism
closed this
Oct 8, 2013
This comment has been minimized.
This comment has been minimized.
|
If someone comes up with a proposal with almost all of the details worked out (grammar, semantics), I suggest posting it to the mailing list. If consensus is reached about a certain way of doing this being the right way, then it makes sense to open an issue and implement it as an experimental feature behind a flag like |
This comment has been minimized.
This comment has been minimized.
|
I second @thestinger -- if someone (or a small group of people) has a complete proposal, or a proposal with a few clearly-spelled-out blank spots that are up for discussion, it would be appropriate for that person to run it by the mailing list. That's no promise that we'll implement that proposal, but doing the work of formalizing the idea and explaining how it interacts with other language features would increase the value of the suggestion greatly. |
This comment has been minimized.
This comment has been minimized.
|
@thestinger @catamorphism Thank you both for keeping an open mind! When I find some time I'll prepare a proposal and send it to rust-dev. |
This comment has been minimized.
This comment has been minimized.
KokaKiwi
commented
Oct 20, 2013
|
I created a pad in order to debate about this feature and to write a clear spec about it: https://pad.riseup.net/p/hvbg6dQQnEe7 |
This comment has been minimized.
This comment has been minimized.
|
I'm reopening this. It's not a priority - there's already far too much that needs to be done - but it is a feature that people want, and that isn't completely off the table. I don't wish to shut down any conversation on the subject. |
brson
reopened this
Oct 21, 2013
This comment has been minimized.
This comment has been minimized.
|
@brson Thank you! |
This comment has been minimized.
This comment has been minimized.
|
I've edited the original issue with a link to the etherpad. |
This comment has been minimized.
This comment has been minimized.
KokaKiwi
commented
Nov 3, 2013
|
Hi. |
This comment has been minimized.
This comment has been minimized.
|
@KokaKiwi all of the pads are empty now. Where did the contents go? |
This comment has been minimized.
This comment has been minimized.
|
@cmr, I guess:
|
This comment has been minimized.
This comment has been minimized.
KokaKiwi
commented
Dec 30, 2013
|
I'm actually searching the pad I created on Mozilla Etherpad instance (in order to prevent this case), but I can't find it in my history, and I forgot to publish the link here :( |
This comment has been minimized.
This comment has been minimized.
dram
commented
Dec 31, 2013
|
@cmr @huonw @KokaKiwi, here are two links in my browser history: https://etherpad.mozilla.org/CQEDa85jLX |
This comment has been minimized.
This comment has been minimized.
KokaKiwi
commented
Dec 31, 2013
|
@dram That's what I was looking for! Thanks |
This comment has been minimized.
This comment has been minimized.
|
(Edited.) |
This comment has been minimized.
This comment has been minimized.
chrisballinger
commented
Jan 22, 2014
|
I don't have anything meaningful to add other than this is one of the things I'm watching closely before I invest more time with the language. Coming from languages (Objective-C and Python) that have this feature, I believe that named parameters are an indirect major boost to productivity because of how they force other people's code to be more readable. |
This comment has been minimized.
This comment has been minimized.
|
A concrete proposal should be done through the new RFC process: https://github.com/rust-lang/rfcs There are too many conflicting ideas and diverging threads of conversation here for it to be a useful discussion area. |
tj commentedJun 6, 2013
I didn't see any issues for keyword arguments, any plans for this in the future?
NOTE: The bug tracker is not the place to have a design discussion. Please direct all design discussion to the etherpad (
https://pad.riseup.net/p/hvbg6dQQnEe7) or create a bikeshedding page on the wiki.Etherpads: