Rust: Default arguments and keyword arguments

Created on 6 Jun 2013  ·  70Comments  ·  Source: rust-lang/rust

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:

Most helpful comment

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 static declaration).

All 70 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 foo(1), foo(1, 3), foo(1, ham=4), foo(6, 7, 8), etc.

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 static declaration).

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.
Perhaps this simpler subset of the required behaviour could be implemented before there is consensus on how/if to call named keyword parameters ... it would get the internals ready ?

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 foo(1, 2, None), foo(1, _, Some(1)), foo(1, _, _), etc.

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 ? )
dipping my toes into the water to experiment with this feature.
i'd be keen on this to increase the subset of C++ api's that could be translated, and they're definitely something i miss from C++, and the named keyword feature would be awesome beyond what c++ does.
It seemed simple and logical to go with the second suggested declaration syntax

@dobkeratops that looks good (although the type Default = Option<@expr> is probably unnecessary).

The constraints on the expr go in rustc::middle::check_const; you'll have to add a function like check_fn that checks each arg. (Also, presumably there should be a check that default args only appear at the end; and I guess there'll need to be something in the type-checker rustc::middle::typeck, and in trans too.)

Ok the field is named so thats un-necasery annotation.
Thanks for the pointers on where to look, I was looking in rustc::middle::typeck::.. mod.rs (check_fn something ).

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 .
I'm always paranoid about diverging changes

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).
Just thought it might be low hanging fruit, and another familiarity for C++ people ..
I suppose macros can sometimes do a similar job (and more).. and I can get into better naming habits for common/uncommon variations

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 fmt!() does this:

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 bar(Foo{y: 5, ..Foo::default()}) to bar(y=5), or my more conservative proposal of bar(_, 5, _)), and of course it forces all callers to create a struct even if they _do_ want to pass all the arguments.

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 *::to_str_radix(num, radix). If we had optional args this could well be folded in *::to_str(num, radix = 10). I feel that in some cases the explosion of function variants really makes things less intuitive and harder to remember. Just my two cents though obviously.

I can see its a good time trade-off to defer them;
but does go _really_ omit defaults philosophically, or is that just them justifying the implementation time-budgetting trade-offs they made? I'd be surprised if anyone thought wading through long_function_names_with_lots_of_trailing_qualifiers is a step forward :) , but if they said "it lets us leverage simple tools, we haven't developed have a powerful IDE yet.." thats another matter.

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?
I realise thats unlikely to be an acceptable design - but it makes me beleive its not _impossible_ to have default args that work with rusts' features.

One thing i'm still really getting used to is that methods are in theory much more useable in rust.
Back in C++ I have a fear of them because of header/dependancy issues,(this is the main reason i'm here), so I often instinctively go for functions first.
Perhaps i'll miss defaults less when i'm using rust better..

Just so the idea doesn't disappear: the chatty aspect of bar(Foo{y: 5, ..Foo::default()}) seems to me to be the ..Foo::default() part.

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 (Zero or Default or whatever), that looked like this:

Foo{y: 5, *) where the * takes the place of the earlier ..Foo::default().

Then the example is "just" bar(Foo{y: 5, *}). Maybe others would prefer the Foo not being there either, but this seems pretty clean to me.

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.
That would seem to suit the cases like creating something with lots of setup parameters well

@pnkfelix Remember that the Foo struct in that example would probably have a much more self-descriptive name, and even with your proposed sugar would look something like this in practice:

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 server_opts struct beforehand and then just calling start_server(server_opts). And I really don't mind adding a line with ..html::ServerOpts::default if my struct initialization already spans several lines.

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.
I agree they've got way more important things to do, & enums recover lost polymorphism with possibilities not immediately apparent to C++ conditioned brains.

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.
..its more functionality in one place, less navigating to figure out what's going on, less nesting depth, less noise in the sourcecode. Not just trivially less typing.
maybe you could even claim its more self-documenting, the function signature tells you more about how its used.

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..

fn substr(a:&str, start:int, end:int=a.len())->~str

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 Option<T> parameters can be omitted. Here, the Option type is reused, but if this cause some problems another more specific OptParam type could be created.

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:

  • the default value of parent is None (no parent)
  • the default value of type is InfoDialog
  • the default value of icon depends on the value of type
  • the default value of title depends on the value of type and on the current locale

You're now also proposing adding Option as a new primitive type. It's entirely a library feature right now, as it's a plain old enum.

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..
agreed they combine well if you have both.
The stickig point is actually implementing them :)
how to retrofit it into the typechecker/type inferer.. or how else to slot it in.

Default values for positional arguments results in cryptic code. Why not only have defaults for keyword arguments? (If these are implemented).

IMO ... its not "either-or".
They're both useful. One is simpler to implement , with less choices to make in the syntax;
so it makes sense for us to as for that, or to try to implement that first, as a stepping stone...

How is ommitting values that are defaulted any more cryptic than normal function calls? C++ programmers are used to sorting parameters for thsi already. Usually some sort of control-flags word wth a sensible default at the end.. just omit the value instead of writing (..,BLAH_DEFAULT) whatever.. clarifies by only listing significant information. People who need Rust are likely to have dealt with C++ already and hence be used to it.

I realized earlier today that my proposal above (which tried to leverage existing struct-extension syntax to provide something close to default arguments, as I view structs as already providing the ingredients for keyword arguments), ignored one important issue: the syntax I was proposing assumed that one would be able to provide a default for _every_ argument (and then they would all be lifted into the single struct parameter).

This is of course not true in general: One quite often has some arguments that are required and others that are optional.

I would still like to figure out some way to leverage the features we already have, rather than muck further with the function declaration syntax, mainly because function declarations are _already hairy_ thanks to the various ways one can write down functions/closures/methods.


My current brain-storming has focused on whether we could leverage the existing struct syntax, perhaps by adding default expressions for fields in the definition of a struct item. That would then allow one to have some fields that could be dropped and others that would be required. Something like:

struct Foo { x: int = 0, y:int = 0, z:int = 0 }

fn bar(f: Foo) {
    printf!(f);
}

struct Baz { x: int, y: int, z:int = 0 }

fn quux(g: Baz} {
    printf!(g);
}

fn main() {
    bar(Foo{y: 5});
    quux(Baz{y: 5}); // ~ERROR: required field, `x`
}

@pnkfelix I like that suggestion, maybe it wouldn't be too difficult to leverage the same internals but generate an anonymous struct. So taking your example, this would be effectively the same code with a bit of compiler magic to infer the struct:

fn bar({ x: int = 0, y:int = 0, z:int = 0 }) {
    printf!(f);
}

fn quux({ x: int, y: int, z:int = 0 }) {
    printf!(g);
}

fn main() {
    bar({ y: 5 });
    quux({ y: 5 }); // ~ERROR: required field, `x`
}

@visionmedia @bstrie Do we really need to be concerned about getting rid of the struct name? Doesn't a rebinding use declaration solve the problem of long names, by dropping the minimum number of additional characters at the call-site down to 3 (for the name + left and right brackets)?

I.e., with ServerOpts example that bstrie posited to me:

fn main() {
  use O = html::StartServerOptions;
  ...
  html::start_server(O{port: 10088});
}

@pnkfelix I like the

struct Foo {
   x: int = 10,
   y: float = 1.0
}

syntax, but I don't think we need it to get sensible compulsory arguments (not named, unfortunately), just pass the compulsory ones separately:

struct FooOptions { ... }
static default: FooOptions = FooOptions { ... };
fn foo(compulsory: int, required: uint, necessary: float, optionals: FooOptions) { ... }

FWIW, I also like the struct with default values idea. I guess that would be a shortcut for:

struct Test {
    m: int,
    y: int
}

static DEFAULT: Test = Test{m: 10, y: 15};

Test{y: 5, ..DEFAULT}

I like the struct defaults inside the definition aswell

I've got those parsing with that syntax aswell but not actually used.. I still dont know my way around the typechecker source etc...

Perhaps the fact there is already a default mechanism there might make it a bit easier to implement.

re. anonymous structs i think they originally had those but had to remove them 'because of strange interactions' ?
i would prefer to have both mechanisms available. perhaps parameter structs would alleviate the need for _keyword_ arguments,but trailing defaults like in c++ would still be handy

for reference -
http://www.parashift.com/c++-faq-lite/named-parameter-idiom.html
lol - interesting but true keyword arguments would avoid the need for boilerplate heavy hacks like this

re. anonymous structs i think they originally had those but had to remove them 'because of strange interactions' ?

Originally all struct types were structural. They were removed (replaced with nominal struct types) because of awkward interactions with traits, I believe.

I like the default values in struct definitions idea. This seems easy to understand and at least avoids having to enumerate every field when constructing a struct - this is a problem for "options" type structs because it means that when a new "optional" field is added then every call site must be modified to initialize it to 0/None/etc.

Coming from Perl (where defaults are an ad-hoc pattern), Python 2 (where you can get foo() takes at least 2 arguments (2 given)), and Python 3 (where you can have named-only arguments _without_ defaults), I humbly propose: add a _teeny_ bit of sugar to allow declaring a struct as the final argument to a function, but let the caller _inline_ the struct fields.

e.g.

impl int {
    struct FromStrOptions {
        radix: uint = 10
    }
    fn from_str(s: str, opts: FromStrOptions) -> int {
        // ...
    }
}

int::from_str("4", radix: 10);

Advantages:

  • No new syntax for the function definition, which doesn't actually have to care at all how the caller calls it. This is akin to how passing blocks with do works: it's purely the caller's concern. (Does there need to be a way to opt in/out, for the case of having a last argument that's a struct but not intending that it be used for kwargs? Would anyone care? I don't think anyone cares for do.)
  • Binary layout and C call semantics and etc. are still pretty well-defined.
  • Overloading by type or arity is no more difficult to implement someday, save that the final argument's type can't contribute to type overloading. Shouldn't interfere with variadic args, either, if those ever happen.
  • Same set of defaults can be reused in multiple functions.
  • Something like **kwargs for free: just create the struct and pass it as a positional arg instead.
  • Required keyword arguments fall naturally out of this: just don't give a default in the struct, and you're forced to pass _something_ by name.
  • No wacky confusion when passing positional arguments by name; you just can't do that.

Disadvantages:

  • Slightly magical. But no moreso than passing 5 and having the type inferred from the function's argspec, I think.
  • Unclear how it'd interact with the existing do idiom. Might be easy enough to say it has to be the last argument _written_, i.e. it works as the second-to-last argument only when used with do.

FWIW int::from_str("4", radix: 10) would conflict with the, previously proposed, use of : as type ascription operator.

Curses, foiled by ASCII.

int::from_str("4", radix☃ 10)

i'd vote for the use of = for keyword args, just disallows C's idea of assignments within subexpressions.. (which maybe doesn't fit as much with rusts' mostly immutable ideas anyway) .. i think Scala has this, for a possible example of how it fits in a c-like syntax?

Is there anywhere to brainstorm how to implement any of this.i wondered if the variable arguemnt style could be implemented internally like a function overload based on the argument count (almost as if the number of args was postfixed onto the function name .. )

We talked about this in the weekly meeting last night.

I think everyone agreed that further complication to the function declaration/invocation forms is not good, and that extending struct declarations to allow for field-defaults (associated with const expressions), as suggested in my earlier comment, would be a reasonable way to get a majority of what's desired here.

The team also decided that resolving this in any fashion is not a priority for the 1.0 release. Project lead @brson suggested that we leave prototyping anything on this task for post-1.0 (e.g. for a 1.1 release).

So that is a strong hint to all contributors: hack all you like, but please do not submit pull requests or r+ anything for this bug until post 1.0 release.

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!

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' :(
+1 to vallorics comments. naming is hard,digging through documentation and learning more names is offputting..; default args/overloarding make it easier; with keyword args you'd have an opportunity to go beyond C++ on this. (I wish github had voting, i could just hit +1 instead of ranting a bit more here lol)

@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

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.

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 once functions.

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.
Since the pad creation, it has been completed by many design propositions, questions, problems, etc...
So I've created a second pad to "summarize" the discussion pad, this pad will be used to describe the feature request exactly.
Pad URL here: https://pad.riseup.net/p/Ca5PBeDjUGxW

@KokaKiwi all of the pads are empty now. Where did the contents go?

@cmr, I guess:

WARNING: This pad will be DELETED if 30 days go by with no edits. There is NO WAY to recover the pad after this happens, so be careful!

:frowning:

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
https://etherpad.mozilla.org/78FA1bozLd

@dram That's what I was looking for! Thanks :smiley:
Maybe the issue should be edited with the new links, I think.

(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.

Was this page helpful?
0 / 5 - 0 ratings