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
base: master
from

Conversation

Projects
None yet
@iopq
Contributor

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

This comment has been minimized.

@apoelstra

apoelstra Jul 3, 2014

Contributor

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.

This comment has been minimized.

@iopq

iopq Jul 3, 2014

Contributor

Just because they share the same name doesn't mean they have the same documentation. Ditto for traits. Those are two separate functions.

```rust
fn split<Sep: CharEq>(&self, sep: Sep) -> CharSplits<'a, Sep>
fn split<Sep: CharEq>(&self, sep: Sep, count: uint) -> CharSplitsN<'a, Sep>
```

This comment has been minimized.

@apoelstra

apoelstra Jul 3, 2014

Contributor

Actually these functions can already be eliminated by using take() on CharSplits. In fact the implementation of CharSplitsN is basically a copy of the implementation of take() plus a copy of the implementation rev() (for rsplit and rsplitn). So it's possible to eliminate this duplication today without any new language features. Just drop splitn, rsplit and rsplitn because they're redundant.

This comment has been minimized.

@bstrie

bstrie Jul 3, 2014

Contributor

@apoelstra, that sounds like a good plan regardless of this RFC. File a bug so that it isn't forgotten. :)

This comment has been minimized.

@apoelstra

This comment has been minimized.

@apoelstra

apoelstra Jul 3, 2014

Contributor

I should probably mention here that in the referenced bug report, @alexcrichton explained that I was wrong on this point: split().take() and splitn() in fact have different semantics.

@arcto

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Contributor

aochagavia commented Jul 3, 2014

@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

This comment has been minimized.

Contributor

glaebhoerl commented Jul 3, 2014

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

This comment has been minimized.

arcto commented Jul 3, 2014

@glaebhoerl:

let my_fn = foo(int); //?
@sfackler

This comment has been minimized.

Member

sfackler commented Jul 3, 2014

That's ambiguous:

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

This comment has been minimized.

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

This comment has been minimized.

Contributor

bstrie commented Jul 3, 2014

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

@utkarshkukreti

This comment has been minimized.

utkarshkukreti commented Jul 3, 2014

@bstrie Clojure?

@arcto

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Contributor

bstrie commented Jul 3, 2014

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

@bstrie

This comment has been minimized.

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

This comment has been minimized.

Contributor

glaebhoerl commented Jul 3, 2014

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

@arcto

This comment has been minimized.

arcto commented Jul 3, 2014

@bstrie: doesn't that disambiguate the example?

@bstrie

This comment has been minimized.

Contributor

bstrie commented Jul 3, 2014

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

@pczarn

This comment has been minimized.

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 {

This comment has been minimized.

@huonw

huonw Jul 3, 2014

Member

These have a self parameter but are not inside a trait or impl?

@nrc

This comment has been minimized.

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

This comment has been minimized.

Contributor

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Member

brendanzab commented Jul 4, 2014

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

This comment has been minimized.

bill-myers commented Jul 4, 2014

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

This comment has been minimized.

dobkeratops commented Jul 4, 2014

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

@CloudiDust

This comment has been minimized.

Contributor

CloudiDust commented on active/0000-arity-parameter-overloading.md in 6b3072d Jul 4, 2014

Nitpick: unwrap and unwrap_or have different semantics. The former has a possibility to fail, while the latter has not. They should not be sharing a name even if this RFC is accepted. Actually misuses of function overloading like this are one of the reasons why Rust doesn't have ad-hoc function overloading now, IMHO.

@alexchandel

This comment has been minimized.

alexchandel commented Jul 5, 2014

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

This comment has been minimized.

Contributor

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

This comment has been minimized.

Contributor

CloudiDust commented Jul 6, 2014

@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

This comment has been minimized.

Contributor

CloudiDust commented Jul 6, 2014

@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

This comment has been minimized.

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

This comment has been minimized.

Member

pnkfelix commented Sep 25, 2014

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