-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Function specialization #7059
Comments
Define "most specialized". The one with the highest number of generic bounds? Then when you refactor your code to collapse several bounds into one via trait inheritance, you've silently changed the meaning of the program. So then perhaps you could count the number of generic bounds by recursively expanding each trait into the ones it inherits from... but now fussing with traits anywhere higher in the inheritance chain can again silently change which function is selected, except arguably worse because it's action-at-a-distance. "First that matches" also doesn't work, because non-closure functions have no concept of order. Note that this program works: fn main() {
foo();
bar();
fn bar() {
foo();
println("bar");
}
fn foo() {
println("foo");
}
} I'm not necessarily opposed to this idea, but there needs to be a concrete proposal that considers these complications. |
In this specific case it is quite obvious what would be the most specific trait, since Signed implies Ord, Zero, Neg and more. I would consider the following rules something to start discussing,
|
This is basically proposing something like the "most specific instance" rule in GHC when you pass in the |
I like the idea of specializing functions, but it seems like there are other ways to do it. Most of the time you just want a version for |
@thestinger Yeah, that is essentially the same thing as I want, just with different syntax. |
In Haskell the reason it's impossible to have complements of constraints (or more generally to branch on the "otherwise" case in any way) is the open-world assumption. There's no way to rule out the possibility of an instance ( What this ticket is asking for is actually more than that, because it's also asking for a kind of implicit C++-style overloading on functions. We already have implicit overloading for the Considering only the trait complements / boolean trait expressions half of it, obviously
Drawbacks:
|
@bjz has a proposal for overlapping impl precedence; reproduced here for posterity:
I think the arguments about overlapping instances in Haskell don't fully apply because Rust doesn't have the (entire) open world assumption like Haskell. In particular, I believe that, for any "pattern" that overlaps, Rust can see all of the possibilities at once, because at least one of the trait or type have to be defined in the current crate. E.g. you can't write // crate1.rs
trait Foo {}
impl<T,U> Foo for (T,U) {}
// crate2.rs
impl<T> Foo for (T, int) {}
// crate3.rs
impl<U> Foo for (float, U) {} and so you can never have the situation where importing |
My own specific desire with it is very simple, to do with augmented assignment (#5992). Given a trait definition like this: pub trait AddAssign<RHS> {
#[inline]
fn add_assign(&mut self, rhs: &RHS);
} What I really want is simply this: impl<LHS: Add<RHS, LHS>, RHS> AddAssign<RHS> for LHS {
fn add_assign(&mut self, rhs: &RHS) {
*self = *self + *rhs;
}
} This has the effect of making A couple of ideas of mine:
|
@huonw, good point. I believe you could still have that situation when importing different modules from the same crate? But maybe that's no longer concern-worthy? The other criticism levelled in the SO answer still holds: if upstream adds a new, more specific impl, that could silently change the behaviour of downstream code. It seems like a good idea to preserve the property that merely adding something new can't change the behaviour of something that already exists. I think this will be a problem with any system where you have 'one impl to rule them all' that you can override on a per-case basis. Would it be so burdensome to have to write an empty (Apropos, GHC has a feature called DefaultSignatures. Basically what that does is it lets you write a default implementation for a method with a more specific signature than the method itself (e.g. additional trait bounds), and the default is generated only if the more specific signature is satisfied. But you still have to declare the FWIW I think a system with explicitly negated traits and strict-but-taking-bounds-into-consideration overlap checking would also allow similar problems:
Now if you add an @chris-morgan, two questions:
Your I believe Rust used to have explicit import/export of |
|
@chris-morgan, wrt
that's exactly what GHC's
if you try to translate that to Rust:
What goes in Anyways, the point is not to bikeshed syntax, I originally wanted to translate it into Rust to help explain what it does. |
FWIW, both @thestinger and I have also found uses for implementing a default method when |
This is the first part of rust-lang#5992, covering the traits and their implementations and documentation of it all, but not including the wiring to make the actual operators (such as `+=`) desugar to the appropriate method call. This comes from my old augmented-assignment branch which had the wiring also but wasn't quite right. Things have changed enough that that wiring is utterly defunct and unworthy of any attempt at resurrection. The traits, however, were not, so I have restored that part of the work. All places in the present code base where any of the arithmetic traits (`Add`, `Sub`, `Mul`, `Div`, `Rem`, `BitAnd`, `BitOr`, `BitXor`, `Shl` and `Shr`) were implemented has an `*Assign` trait implementation added, with the exception (as before and as noted in the code) of `&str` and `&[T]` where the assignment operators cannot be implemented. Note that there is necessarily no default implementation of the `*Assign` traits, as that would preclude more efficient implementations of the augmented assignment operators and render the whole thing utterly pointless (c.f. rust-lang#7059 on function specialisation).
This can & should be converted into a new-style RFC. |
I've been playing with vectors, matrices and BLAS, and I've encounter this issue repeatedly. See this gist for an example. I've independently reach the solutions formulated by @chris-morgan in his first comment. Namely:
|
This is the first part of rust-lang#5992, covering the traits and their implementations and documentation of it all, but not including the wiring to make the actual operators (such as `+=`) desugar to the appropriate method call. This comes from my old augmented-assignment branch which had the wiring also but wasn't quite right. Things have changed enough that that wiring is utterly defunct and unworthy of any attempt at resurrection. The traits, however, were not, so I have restored that part of the work. All places in the present code base where any of the arithmetic traits (`Add`, `Sub`, `Mul`, `Div`, `Rem`, `BitAnd`, `BitOr`, `BitXor`, `Shl` and `Shr`) were implemented has an `*Assign` trait implementation added, with the exception (as before and as noted in the code) of `&str` and `&[T]` where the assignment operators cannot be implemented. Note that there is necessarily no default implementation of the `*Assign` traits, as that would preclude more efficient implementations of the augmented assignment operators and render the whole thing utterly pointless (c.f. rust-lang#7059 on function specialisation).
This issue has been moved to the RFCs repo: rust-lang/rfcs#290 |
Deprecate `filter_map` Since rust-lang#6591, `filter_map` does not even lint `filter().map()`. The cases that are still linted make no sense IMO. So this just removes/deprecates it. changelog: Deprecate `filter_map` lint Closes rust-lang#3424 Fixes rust-lang#7050
Code like this should work, preferably picking the most specialized function (and/or just picking a random one, or the first that matches &c. or just throw a warning)
The text was updated successfully, but these errors were encountered: