-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Arity-based parameter overloading #153
Conversation
``` | ||
|
||
So `to_str_radix(&self, radix: uint) -> String` can be now written as `to_str(&self, radix: uint) -> String` while | ||
`to_str(&self) -> String` still exists. This will let Rust get rid of the sheer multitude of functions that only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's really unclear how this would work, since ToStr
and ToStrRadix
are completely separate traits, and most of the things that implement ToStr
have no concept of "radix".
I also don't see any subjective benefit to giving these functions the same name. Right now if you look in the documentation for numeric types you will see implementations of the ToStrRadix
type (as well as ToStr
for any which do this "default base 10" thing, though a quick glance through did not find any examples). You can click on the ToStrRadix
trait to get documentation for what to_str_radix
does and on ToStr
to get documentation for .what to_str
does. I don't see how you can sensibly merge this documentation, so your function merging is only skin deep, so to speak.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just because they share the same name doesn't mean they have the same documentation. Ditto for traits. Those are two separate functions.
I don't see why these two functions could not co-exist:
|
I've always thought a good way of handling optional arguments would be a special case for the Option type. If I have a function: fn example(a: int, b: int, c: Option<int>) -> int { } I should be able to call it in two ways. example(5, 10); // Calls example with 5, 10, None
example(5, 10, 15); // Calls example with 5, 10, Some(15) Again this works on the arity of the call-site, but allows a single function signature to handle the situation. |
@wolenber that would need a check at runtime in the function to check if the third parameter is passed. One of the advantages of overloading is that there is no runtime cost. |
Functions aren't just items, they are also values. Suppose you wrote:
Presumably that would be an error. How would you disambiguate it? |
|
That's ambiguous: let int = 10;
let my_fn = foo(int); |
I've never heard of a language that overloads based on arity. Can you cite a precedent, or is this entirely original? |
@glaebhoerl, do there exist no other languages with both overloading and first-class functions? |
@bstrie Clojure? |
@sfackler: Wow, I didn't know you could redefine But it seems that in this case the local definition shadows the other? |
@aochagavia I admit it would add a runtime cost, but it's a purely optional feature that doesn't need to be used. In inner loops where it matters, it could easily be avoided. Outside of them, it's some nice syntax sugar. |
@utkarshkukreti, in what way does Clojure handle the case that @glaebhoerl presents above? |
|
@bstrie C++, and there it would be disambiguated by writing (IIRC) |
@bstrie: doesn't that disambiguate the example? |
@arcto, er, nevermind, I'm dumb. :) Those are indeed two different namespaces. |
Let's disambiguate it... with type inference? let my_fn = foo;
my_fn(1, 2); // infer arity from here
// alternatively
let my_fn: fn(int, int) -> int = foo;
// and with partial type inference
let my_fn: fn(_, _) -> _ = foo; Ambiguous usage would cause: |
Nobody will be confused by which method is being called when they differ by how many arguments they have. | ||
|
||
```rust | ||
fn concat(&self) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These have a self
parameter but are not inside a trait
or impl
?
Thanks for submitting the RFC. Whilst overloading (of some kind) may be something we add to Rust in the future, it is something that has been discussed before and there is broad agreement that it wouldn't be added before 1.0. Therefore, this RFC is extremely likely to be closed as postponed. |
If you postpone it until after 1.0, you can't redo your standard library to have overloading, so there's no point. |
1.0 is not stabilising all libraries, just the language itself. (We have fine grained library stabilisation via stability attributes.) |
This needs to be postponed because a lot of thought will be needed in order to get it right. |
I think this might not be the best approach. The fundamental advantage of optional arguments is that they guarantee that f(x) behaves like f(x, a) for some a, while this RFC allows f(x) to behave in an arbitrary fashion completely unrelated to f(x, y). This means that instead of only having to learn how f(x, y) behaves, one must separately learn about how f(x) behaves, while with optional arguments you just need to learn what the default values are. [Note that you can in fact actually reproduce any arity-based overloading using just default arguments (by using Option types for arguments and Either-like enums for returns), but bad design is far more obvious with default arguments since the function will have an ad-hoc if or match to switch behaviors] And at this point, it's not clear why full C++-like overloading should not be allowed as well, since its main drawback (the fact that functions are no longer identified just by their name) is already suffered. Optional arguments might be a good idea though. |
combined with multiple dispatch traits or the new 'where' RFC, you might be able to recover C++ style overloading. (make some intermediate functions to shuffle arguments into the self for dispatch) You'd probably also be able to setup a macro for generating curried versions, which appeals to some |
Isn't this equivalent to generic function specialization? e.g. fn foo<T>(a: T);
fn foo<int>(a: int) { /* etc */ }
fn foo<uint>(a: uint) { /* etc */ } This seems similar to generic trait implementation, which gives you |
I'm not making a particular statement about the libraries, but I could make the argument that unwrap/1 can fail while unwrap/2 cannot. They have completely different signatures and you can annotate/document them separately. All overloading does is allow you to use the same name. |
@iopq, I think if two functions have different semantics, then they should not be using the same name. As people would have wrong expections. They don't have "completely" different signatures in my opinion. Yes we can document them, but it's better if we can tell the difference directly from the names. |
@iopq, and I agree with @bill-myers that optional/default arguments are better. |
Closing. There are a number of approaches to overloading, but it is a non-essential feature and the Rust team does not want to address overloading at this time. Thank you. |
filing with RFC issue #323 |
…ream Future::flatten_to_stream
A better proposal to have better names of functions in the standard lib.