Skip to content
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

Closed
wants to merge 1 commit into from
Closed

Conversation

iopq
Copy link
Contributor

@iopq iopq commented Jul 3, 2014

A better proposal to have better names of functions in the standard lib.

```

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@arcto
Copy link

arcto commented Jul 3, 2014

I don't see why these two functions could not co-exist:

fn foo(a: int, b: int)
fn foo(a: int, b: &str)

@wolenber
Copy link

wolenber commented Jul 3, 2014

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.

@aochagavia
Copy link
Contributor

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

@glaebhoerl
Copy link
Contributor

Functions aren't just items, they are also values. Suppose you wrote:

fn foo(a: int) -> int { ... }
fn foo(a: int, b: int) -> int { ... }

let my_fn = foo;

Presumably that would be an error. How would you disambiguate it?

@arcto
Copy link

arcto commented Jul 3, 2014

@glaebhoerl:

let my_fn = foo(int); //?

@sfackler
Copy link
Member

sfackler commented Jul 3, 2014

That's ambiguous:

let int = 10;
let my_fn = foo(int);

@bstrie
Copy link
Contributor

bstrie commented Jul 3, 2014

I've never heard of a language that overloads based on arity. Can you cite a precedent, or is this entirely original?

@bstrie
Copy link
Contributor

bstrie commented Jul 3, 2014

@glaebhoerl, do there exist no other languages with both overloading and first-class functions?

@utkarshkukreti
Copy link

@bstrie Clojure?

@arcto
Copy link

arcto commented Jul 3, 2014

@sfackler: Wow, I didn't know you could redefine int.

But it seems that in this case the local definition shadows the other?

@wolenber
Copy link

wolenber commented Jul 3, 2014

@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.
@glaebhoerl & others, this is disambiguated in my preferred method.

@bstrie
Copy link
Contributor

bstrie commented Jul 3, 2014

@utkarshkukreti, in what way does Clojure handle the case that @glaebhoerl presents above?

@bstrie
Copy link
Contributor

bstrie commented Jul 3, 2014

@arcto, types and variables live in the same namespace. Shadowing is what's going on here, yes. WONG

@glaebhoerl
Copy link
Contributor

@bstrie C++, and there it would be disambiguated by writing (IIRC) (int (*)(int))foo, i.e. by casting it to the right type.

@arcto
Copy link

arcto commented Jul 3, 2014

@bstrie: doesn't that disambiguate the example?

@bstrie
Copy link
Contributor

bstrie commented Jul 3, 2014

@arcto, er, nevermind, I'm dumb. :) Those are indeed two different namespaces.

@pczarn
Copy link

pczarn commented Jul 3, 2014

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: error: cannot determine a type for this local variable: cannot determine the signature of this function

Nobody will be confused by which method is being called when they differ by how many arguments they have.

```rust
fn concat(&self) -> String {
Copy link
Member

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?

@nrc
Copy link
Member

nrc commented Jul 3, 2014

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.

@iopq
Copy link
Contributor Author

iopq commented Jul 3, 2014

If you postpone it until after 1.0, you can't redo your standard library to have overloading, so there's no point.

@huonw
Copy link
Member

huonw commented Jul 3, 2014

1.0 is not stabilising all libraries, just the language itself. (We have fine grained library stabilisation via stability attributes.)

@arcto
Copy link

arcto commented Jul 3, 2014

This needs to be postponed because a lot of thought will be needed in order to get it right.

@brendanzab
Copy link
Member

I've never heard of a language that overloads based on arity. Can you cite a precedent, or is this entirely original? - @bstrie

In Erlang:

A function named f in the module m and with arity N is often denoted as m:f/N.

The risk of messing this up is almost certainly too high for 1.0 though.

@bill-myers
Copy link

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.

@dobkeratops
Copy link

And at this point, it's not clear why full C++-like overloading should not be allowed as well,

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

@alexchandel
Copy link

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 foo(self) for int and uint. Would the compiler handle these in the same way?

@iopq
Copy link
Contributor Author

iopq commented Jul 5, 2014

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.

@CloudiDust
Copy link
Contributor

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

@CloudiDust
Copy link
Contributor

@iopq, and foo.unwrap_or(bar) looks like "unwrap foo and return the thing inside, if this fails, return bar instead", while foo.unwrap(bar) looks like "use foo to unwrap bar".

I agree with @bill-myers that optional/default arguments are better.

@brson
Copy link
Contributor

brson commented Jul 10, 2014

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.

@brson brson closed this Jul 10, 2014
@pnkfelix
Copy link
Member

filing with RFC issue #323

withoutboats pushed a commit to withoutboats/rfcs that referenced this pull request Jan 15, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.