-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
- Start Date: 2014-07-03 | ||
- RFC PR #: (leave this empty) | ||
- Rust Issue #: (leave this empty) | ||
|
||
# Summary | ||
|
||
This RFC proposes to add arity-based parameter overloading to Rust. | ||
|
||
# Motivation | ||
|
||
Currently in Rust there are a lot of functions that do the same thing, but take a different number of parameters. | ||
The current design forces those functions to have different names. | ||
This means that sometimes it's harder to look up function names because they are completely unrelated. | ||
|
||
# Detailed design | ||
|
||
Java has a very complicated overloading design that includes overloading by static types. | ||
Overloading on types mixed with type inference might be very confusing. | ||
However, overloading based on arity is very simple and clear. | ||
Nobody will be confused by which method is being called when they differ by how many arguments they have. | ||
|
||
```rust | ||
fn concat(&self) -> String { | ||
... | ||
} | ||
|
||
fn concat(&self, sep: &str) -> String { | ||
... | ||
} | ||
|
||
// compile error, because the first parameter's type | ||
// doesn't match the type of `concat` already declared. | ||
fn concat(&mut self, sep: &str) -> String { | ||
... | ||
} | ||
|
||
// compile error, because the second parameter's type | ||
// doesn't match the type of `concat` already declared. | ||
fn concat(&self, number: int) -> String { | ||
... | ||
} | ||
``` | ||
|
||
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 commentThe reason will be displayed to describe this comment to others. Learn more. It's really unclear how this would work, since 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
differ by a few parameters like `split` and `splitn`. | ||
|
||
Default arguments almost solve this problem, but they don't solve the problem with `unwrap` and `unwrap_or`. | ||
Arity-based overloading allows you to have `unwrap(self, default: T) -> T` as well as `unwrap(self) -> T`. | ||
|
||
This also allows you to return a different type. Again, these are different functions with the same name. | ||
|
||
```rust | ||
fn split<Sep: CharEq>(&self, sep: Sep) -> CharSplits<'a, Sep> | ||
fn split<Sep: CharEq>(&self, sep: Sep, count: uint) -> CharSplitsN<'a, Sep> | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually these functions can already be eliminated by using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @apoelstra, that sounds like a good plan regardless of this RFC. File a bug so that it isn't forgotten. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed as rust-lang/rust#15379 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should probably mention here that in the referenced bug report, @alexcrichton explained that I was wrong on this point: |
||
|
||
|
||
# Drawbacks | ||
|
||
Compared to default arguments, it is much more verbose and gives more power to the user. | ||
|
||
1) Lets you return a different type | ||
2) Lets you omit arguments completely for a different implementation | ||
|
||
However, as you can see from the examples, `to_str`/`to_str_radix` only requires default arguments to be combined into one function. | ||
`split`/`splitn` and `unwrap`/`unwrap_or` require overloading to be combined. One could make an argument that default arguments have | ||
a better syntax and are less verbose. Implementing default arguments instead might be better. | ||
|
||
# Alternatives | ||
|
||
The aforementioned default arguments are a strong alternative since it's a lighter syntax. | ||
|
||
Current APIs have three slice functions: | ||
|
||
```rust | ||
fn slice(&self, begin: uint, end: uint) -> &'a str | ||
fn slice_from(&self, begin: uint) -> &'a str | ||
fn slice_to(&self, end: uint) -> &'a str | ||
``` | ||
|
||
This proposal does not let you have | ||
|
||
```rust | ||
fn slice(&self, begin: uint, end: uint) -> &'a str | ||
fn slice(&self, begin: uint) -> &'a str | ||
fn slice(&self, end: uint) -> &'a str | ||
``` | ||
|
||
This is because Rust does not support keyword arguments. You can't distinguish between a beginning and an end. | ||
If Rust did support keyword arguments you could call those functions like this: | ||
|
||
```rust | ||
foo.slice(begin => 5); //equivalent to current foo.slice_from(5) | ||
foo.slice(end => 9); //equivalent to current foo.slice_to(9) | ||
foo.slice(begin => 5, end => 9); //equivalent to current foo.slice(5, 9) | ||
foo.slice(end => 9, begin => 5); //equivalent to current foo.slice(5, 9) | ||
``` | ||
|
||
Overloading on keywords a la Smalltalk is the most powerful and allows the most freedom in API design. | ||
However, it should be left to another RFC since it is a separate idea from default arguments and overloading altogether. | ||
|
||
# Unresolved questions | ||
|
||
Would it be beneficial to implement both overloading and default arguments? | ||
In a lot of cases, you want just default arguments, like in the case of `to_str` where you just want to write | ||
`fn to_str(&self, radix = 10u) -> String` or `fn to_str(&self, radix: uint = 10u) -> String` without type inference. | ||
|
||
Having to write two type signatures like | ||
|
||
```rust | ||
fn to_str(&self, radix: uint) -> String | ||
fn to_str(&self) -> String | ||
``` | ||
|
||
seems like it is too verbose even if overloading is strictly more powerful. |
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 atrait
orimpl
?