-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Default arguments and keyword arguments #6973
Comments
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. |
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 |
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? |
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. |
Changing title to "Default arguments and keyword arguments". |
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 ? ) |
@dobkeratops that looks good (although the The constraints on the |
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... |
@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). |
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). |
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. |
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. |
@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 |
I should also point out that it interacts with closures and taking first class references to functions. |
@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 |
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. |
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" |
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. |
@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_radix However, 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? |
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. |
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. |
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. |
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. |
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 :) |
Just a bug. We can debate the merits, but won't block a release on it. |
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:
|
You're now also proposing adding |
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. |
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.. |
Default values for positional arguments results in cryptic code. Why not only have defaults for keyword arguments? (If these are implemented). |
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. |
I can see the core team has way more important things to do but it seems a shame to 'communicate extreme discouragement' :( |
@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). |
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. |
'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 |
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". |
@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 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. |
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 |
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. |
@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. |
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 |
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 Thank you! |
I've edited the original issue with a link to the etherpad. |
Hi. |
@KokaKiwi all of the pads are empty now. Where did the contents go? |
@cmr, I guess:
😦 |
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 :( |
@cmr @huonw @KokaKiwi, here are two links in my browser history: https://etherpad.mozilla.org/CQEDa85jLX |
@dram That's what I was looking for! Thanks 😃 |
(Edited.) |
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. |
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. |
ignore `&x | &y` in unnested_or_patterns replacing it with `&(x | y)` is actually more characters Fixes rust-lang#6973 changelog: [`unnested_or_patterns`] ignore `&x | &y`, nesting would result in more characters
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:
The text was updated successfully, but these errors were encountered: