Aliasing other things

I'm not sure that it is constructive to equivocate accessibility that works around disabilities and the desire to be able to write code in whatever dialect of English you happen to write in.

By that token, you might start requiring that all code is universally localizable, which will make our problems even worse if you don't sit down and publish a few academic-grade linguistics papers establishing a theoretical basis for the automatic localization tooling you would need to design. You can rabbithole on this argument all day long.

The above is extreme, because it shows how absolutely insane the required effort can be for something whose impact is not quantified with respect to the rest of Rust's present goals. Just because something is a good idea doesn't mean we should invest time in it. We could just staple the entire calculus of constructions to Rust's type system, and that would be useful to some people, but is it really worth it over moving forward concrete type system improvements with known impact, like const generics?

Consider whether you have an XY problem: whether what you're trying to do is something that requires language support (or, whether you're really trying to do something else altogether).

14 Likes

I'm not the one being extreme here. can we stop with the slippery slope fallacies?

the point is to prevent frustration and confusion because you have to deal with different spellings of the same word in the same codebase.

the alternative would be to make the compiler treat them as equivalent and fix them for you. but nobody wants that.

it's an accessibility problem, not a localization problem.

IMO a far better way to solve this problem of inconsistent spellings is to introduce a lint that can enforce a dialect of English to Clippy. Something like:

#![warn(clippy::dialect(US))]

Which will warn struct Colour and other similar things.

This solution avoids the boilerplate and clutter that yours will have and doesn't change what "idiomatic code" means.

I don't think Clippy lints can take arguments, but hopefully that can be changed.

10 Likes

The dialect should be one of the ULocales in the International Components for Unicode (ICU) Data, such as "en_US", which is a specialization of "en". These are also found in the ISO 639 Language List.

Locales could be enumerated as clippy::dialect_en_US rather than clippy::dialect(en_US), which would eliminate the need for Clippy lints to take arguments.

1 Like

Clippy is not your compiler. Also, that's unnecessarily abrasive.

You can always write wrappers around the items exported by a crate if you're unhappy with their dialect. You could even republish your wrappers as your own crate, if you thought others strongly prefer the alternative dialect.


Personally, I agree with the majority of people here who have expressed that this would increase complexity (to the compiler, the language, and to crates) for minimal benefits. I think you'd need to have a much more compelling use case and argument than "color" vs "colur", especially when there are workarounds you can employ today (like I (and others) said: writing your own wrappers).

4 Likes

Everyone: Please be chill. Also, keep in mind that not every argument or idea you disagree with has to be shouted down loudly. If a proposal (or a counter-argument to it) demonstrates value to enough people then it will gain support and can be refined into something that can reach consensus. If not, then it won't. Critiques of early-stage proposals in particular should be done with a goal of improving them, better understanding their motivation, or comparing them to alternatives. Comments that are closer to just "voting no" on a proposal aren't really necessary.

In disagreements, responding with new information is great, but try not to get in a mode where you are trying to convince specific people (or all people) that you are right and they are wrong. Focus on presenting your own ideas/needs/etc. as clearly as possible. Then people reading the thread (not just the person you are responding to) can judge competing arguments on their merits.

For anyone who wants to improve Rust by designing new features, remember that the bulk of the work is not in coming up with ideas, but in building consensus for them. To succeed, you'll need to spend a lot of time on understanding other people's viewpoints and tweaking your designs and communication based on what you learn. (This includes any concerns from the people who would need to implement and maintain your feature.) This "listening" is the part of the process you'll need to focus most of your time and energy on. If you don't invest in it, then the rest of your effort is likely to be wasted.

[Sorry, this turned into a bit of an essay. This is a topic I've been thinking about a lot as a moderator of this forum—not just in the context of this thread. Please read it as directed to the project as a whole, not just to one or a few people.]

25 Likes

Here's an alternative proposal that might actually have a chance: Have an attribute to put on items:

#[alternate_spelling(Colour)]
struct Color;

#[alternate_spelling(colour)]
fn color() {}

There's still just one name, the core language doesn't change at all, but when someone tries to use Colour the compiler can tell them the correct spelling (and the IDE can even correct it automatically).

5 Likes

Taking off my moderator's hat... Going beyond the US/Commonwealth English use case, here's another one that might be useful for comparisons:

Suppose you develop a Rust SDK for, say, a robotics platform that's used in schools by 12–16-year-olds. You want to localize it for a country where few students are taught English. ("Have them learn English as they learn to program" is one common solution to this problem, but suppose you have reason to believe that decoupling these for now will lead to better educational outcomes.) Let's say that learning a limited number of keywords like if and else and match is considered a reasonable requirement, but learning dozens or hundreds of function names in a foreign language is not.

What would it currently take to build a facade that lets these students use types from libstd, your own crates, and a few third-party crates (including some common types/traits used in public APIs by multiple crates), without needing to learn foreign vocabulary for the many types/traits/functions in all of them? Is it possible with wrapper types and wrapper functions? Are there cases that would be improved with trait aliases, method aliases, etc.? And if so, is this an improvement that's worth the cost of the new features?

9 Likes

If there was a system that allows you to load translation tables into the compiler, that'd be fine.

The main problem that everyone seems to be overlooking is this:

trait Foo {
  fn color();
}

impl Foo for Bar {
  fn colour() {
  }
}

which is the only case not currently covered by any workarounds.

(well, and trait aliases. but I think we're working on those already?)

1 Like

My experience from past discussions (not unique to Rust) about internationalizing programming languages and tools is that it's often far less useful than people initially expect. These days everyone relies on internet resources both while learning the language and while writing any serious code, and only so many of those resources will ever be translated into only so many other languages. So in practice "just" translating English-based terms of art like String and Vec often ends up creating a secondary language barrier that paradoxically makes all the other resources less accessible than if everything consistently used the default English language terms (from what I hear, googling translated compiler error messages is the most common way to run into this barrier).

The two big ideas in this space I know of that mostly avoid this sort of problem, and thus should be a real net win for accessibility, both already exist on unstable:

22 Likes

again, I don't want localization. I want a feature specifically designed for things like aphasia and whatnot.

I get the impression that this specific example or something like it was recently a pain point for you and your co-workers, possibly with a crate boundary in between the trait definition and the impl. Your application code needed to impl a trait defined in a library, and people kept forgetting that the crate author used a different spelling for a common word than they were accustomed to, or something like that.

Can you share any more information, anything at all, about the context? I think the negative reactions you're getting are largely because we haven't had the experience you have just had. To someone just sitting in an armchair considering the problem in the abstract, surely people could get used to conforming to the library's expectations?

But I have actually seen something very similar happen in a different language: the plotting library ggplot2 was written by an Australian; being a plotting library, many of its functions take a colour parameter; being an Australian, the author spelled it the British way, and for some years he insisted that he would not make the library accept color as a synonym. R supports arbitrary keyword arguments, so there was no technical obstacle to making color work everywhere colour did, just a bunch of extra typing. Eventually he did give in and add it, because (I think) he was tired of an endless stream of feature requests from American users.

Based on this case study I'm at least somewhat sympathetic to your original feature request but I wonder whether it should be something that you can do at the point where a crate is imported, instead of something that the crate author has to do for you. We do make that possible for a number of other names defined externally (use Foo as Bar) but not for trait members…

I don't understand how your original feature request would accommodate aphasia, can you explain in more detail please? (And I'm also not clear on which of the many different language production and/or comprehension disorders you are thinking of when you say "aphasia". Accommodations for one might actually be counterproductive for another...)

7 Likes

even if it's harmful to some, the harm is limited to the fn definition in a trait, or to the trait alias, or to the type alias, or whatever. it's not gonna extend to the rest of your codebase.

I think it would help if you explained how you plan to use this feature for accessibility. Is this a standard accommodation for aphasia? How does that normally work? Could you point to some resources showing how this helps?

The Rust project does care quite a bit about accessibility and I think there would be interest in making things even more accessible whether it be on the website or the documentation or the compiler.

Let's put aside the color/colour example. Suppose you have a library that you want to make accessible to someone with aphasia. How do you do that and how does this feature help?

7 Likes

I'll bite. The point I'm making isn't that the proposal is as extreme as what I sketched, but rather that the impact of the change (in some quantitative metric, such as number-of-users-enabled) has not been made clear. The cost of a change in tooling and design needs to be weighed against the potential impact.

Hence, why I asked you a question at the end of the post, which I feel you didn't quite take the time to ponder: What problem are you really solving?[1]

[1] And beware, the danger of thinking of a solution and shopping around for a problem that it solves. This is a pitfall that even experienced engineers frequently trip over.

11 Likes

That particular problem is solved by a smart IDE that spell-checks your function/type names and warns you and perhaps even autofixes the error when you misspell something, no?

4 Likes

I think the best solution would be an attribute to improve diagnostics and IDE experience when an identifier is misspelled. Example:

#[incorrect_spelling(Colour)]
struct Color;

Then rustc could provide a nice error message:

error: "Colour" is misspelled. The correct spelling is "Color"

Furthermore, when renaming something in a library, the previous name could be added as an incorrect spelling, so cargo fix could rename all ocurrances automatically.

An IDE could even replace Colour with Color automatically while typing. However, a problem with this is that a Colour struct might be in a different module, and this attribute could prevent us from auto-importing it.

5 Likes

Alternatively, if the goal is to use a different dialect than the library author, a procedural macro could be used to replace certain identifiers at compile time. For example,

#![spell(color as colour)]

would replace all ocurrances of the word colour with color. However, this would probably break IDEs.

So I put together a convoluted example on the Rust Playground, and I have to say that I think I feel Soni's pain a little more: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8f8b47a1d28c19c413518798b964abf2

Here are my observations about things that were nice and things that weren't nice:

  • Nice: The compiler warned me that when I said colour1 I might have actually meant color1 ( defined in scope variable.
  • Nice: If I didn't have the trait in scope but didn't spell the trait method I was trying to use, the compiler suggested the trait to import
  • Not nice 1: When the trait was in scope, but I misspelled the trait method, no help was given.
  • Not nice 2: When the trait was out of scope, and I misspelled the trait method, no help was given.
  • Not nice 3: When trying to implement the color trait, but misspelling the trait method, I got two error messages, one saying I was mising a method, and the other saying the implemented method didn't belong

Of those, I think 1 and 3 could definitely be improved to give better error messages, but I'm not sure about 2.

9 Likes

I can see some value in aliasing names. In fact, rust already allow some form of aliasing using the use keyword. And probably we could extend it to new scopes.

struct Color;

// this already works today
use Color as Colour;

trait Hello {
   fn color(&self) -> Color;

  // proposed: allow to use `use` within `trait` and `impl`
  // of course, this would only allow to import item from the trait itself. 
  // (What would it means otherwise?)
   use color as colour;
}

// For enum and struct it is a bit more complicated since one cannot really put a `use`,
// but one  could put them in a impl
struct Foo {
    color: usize,
}
impl Foo {
   use color as colour;
}

The color/colour example might not appear so usefull, but I see some value when one wants to deprecate names.

impl str {
   // new name
   pub fn trim_start(&self) -> &str { /* ... */ }

   // old deprecated name still available
   #[deprecated(since = "1.33.0", reason = "superseded by `trim_start`"]
   pub use trim_left = trim_start;
}

This syntax would allow to rename and keep the deprecate names of trait items or enum variant which may be desirable sometimes.

2 Likes