Keywords like `is`, `or`, `isnt`, instead of `==`, `!=` and `||`

I think it's quite a self-explanatory suggestion, and one that serves to make Rust code that much prettier.

let my_number = 1u8;

if my_number == 1 {
  // Magic!
}

// becomes...

if my_number is 1 {
  // Magic!
}

// Equivalent is: if my_number != 1
if my_number isnt 1 {
  // Aww :(
}

// if my_number != 2 || my_number != 5
if my_number isnt 2 or my_number isnt 5 {
  // Magic!-ish...
}



// if my_number != 2 && my_number != 5
if my_number isnt 2 and my_number isnt 5 {
  // Not 2, and not 5.
}

C++ has this feature in order to support some keyboard layouts that don't have keys like | or &.

The big problem with this is that it introduces two ways to write code that means exactly the same thing.

Trigraphs have been removed in C++17. Digraphs and alternative tokens (like and) remain, but I don't think we should introduce them into rust.

It is possible to change the keyboard layout to include those keys even if the keycaps don't show them.

1 Like

Which layout would that be? AFAIK every keyboard produced in the last 20 years has those keys. They would be pretty much unusable otherwise.

1 Like

The is keyword would be more useful for matches! syntax sugar.

15 Likes

I think using is for equality check is a bad idea. I would think it checks for reference equality (i.e. two separate vectors can return false for it even if their contents is identical), especially considering it works like this in Python (well, with the exception of "primitive" values).

But I also would like to use words instead of digraphs. Sometimes in complex expressions it becomes difficult to parse a bunch of !s, ||s and &&s. Though introducing a whole bunch of keywords does not seem optimal. There could be a place for a feature which would allow using registered methods in a keyword-like fashion, something like this:

// .method is a placeholder syntax
if (a .and b) .or .not (c .ne d)  { .. }
use core::time::DurationUnits;
let t = 1u32 .seconds;
// desugars into
if (a.and(b)).or((c.ne(d)).not()) { .. }
let t = 1u32.seconds();

// inside libcore
impl bool {
    #[infix_quasi_keyword]
    fn and(self, b: bool) -> bool { .. }
    #[prefix_quasi_keyword]
    fn not(self) -> bool { .. }
}

impl DurationUnits for u32 {
    #[postfix_quasi_keyword]
    fn seconds(self) -> Duration { Duration::from_secs(self) }
}

I also believe that keywords would increase legibility, especially for and, or and not since ! is also used for macro and never type, & is also used for references and | is also used for closures.

2 Likes

We currently have functions with these names; having them as keywords would break existing code.

3 Likes

In the case of adding a is keyword I would prefer it was for something more general than equality, as following Terence Tao's on Partially specified mathematical objects, ambient parameters, and asymptotic notation. @kornel's idea of desugaring to matches! could be good enough.

1 Like

The AST being different based on what is imported or otherwise in scope is almost certainly a dead end for Rust.

I am really not a fan of dropping the apostrophe from isn't.

2 Likes

Digraphs, Trigraphs, and named tokens aren't for keyboard layouts not having the keys, but were for the characters not existing in some charsets (C had the named tokens under the iso646.h header as macros - using the header lets you write C in any ISO646-compatible charset). However, rust specifies its basic source character set as UTF-8, which definately contains all of the sigils that rust uses (and many more). It doesn't have the same need.
Additionally, because these alternative tokens are all lexically (and not merely syntactically) identical, you get some really weird things like compl A(){} (which is a destructor definition) and A(A and){} which is a move constructor. I think this would not necessarily be a good thing to add to rust, which is sigil heavy in some aspects and is easily subject to the same kind of abuse. let y = and x; for example, would unintuitively be a shared reference to a temporary which has the value of a shared reference to x.

7 Likes

Of course, if we use keywords, it should not be just a direct substitution like C macro. These new keyword would only work for logical operations.

To me the goal of using keywords instead of sigils for logical operations would be precisely to reduce ambiguity. & would be only related to references, | only related to closures and ! only related to macros expansion (if we also use an actual named Never type).

Since Rust already has traits for most of its ops, inventing new unrelated keywords for them seems like a poor choice. So instead of is, it should be eq.

Or, it would be even simpler. Instead of adding keywords, add a syntax sugar for calling methods without needing parenthesis or the dot.

2 Likes

We could do this; we could, for instance, say that a `func` b is equivalent to a.func(b), which would allow bespoke infix operator syntax.

However, such operators primarily have value in larger expressions rather than a single binary operator. And larger expressions are precisely where more complexity would come in, most notably that we'd also have to deal with operator precedence.

4 Likes

Also sounds annoying to parse (as do keyword infix ops).

Then again, probably not worse than range syntax.

That sounds rather backwards, since users of other languages will expect them to refer to the appropriate logical operation, rather than some other construct. It also seems to me like it would be hard to read in incredibly nested code - a stream of identifiers, where one just blends into the one to its left and one to its right. Syntactically, there's no ambiguity, and I'm unsure that there's any significant ambiguity from reading it that won't just be exagerated by this.

I love where Python settled: and, or, and not are keywords, while == and != are sigils. I find that combination quite pleasant to read.

3 Likes

Note that I am not telling that introducing all the proposed keywords right now is a good idea. Not all of them seems usefull to me and that would change the language too much. But I believe that it might have been interesting to do that before 1.0 for the boolean operators.

I don't understand why it would be backward : the sigils for lambda, references and macro are already there. I don't see how it would be more confusing than it already is. That would just visually disambiguate these usages from logical operators.

There are quite a lot of language with keywords for logical operator, like Python, Basic, Pascal, Ada, ... that don't seem to disturb much. And they are quite self explanatory anyways, even for if you never heard about them,.

Currently there are no syntactical ambiguity but I remember that there were syntax decision that where constrained by that. I don't remind exactly witch ones but there where syntax problems with some closures improvement proposals that where conflicting with the or operator, as they were problems with comparison operators and generics that led to the infamous turbofish.

For me, the following examples are quite convincing about legibility at first glance of keywords for boolean operators:

let a = b & &c;
let x = || y || z;
let n = ! m!(o);

vs

let a = b and &c;
let x = || y or z;
let n = not m!(o)
4 Likes

This is the huge problem to me.

Choosing in 2012 to have and/or instead of &&/|| would have been perfectly reasonable.

But today? I don't see the churn to encourage everyone to change as worth it, and having a mix -- potentially even in the same crate -- seems substantially worse than what we have now.

We have a very C-like syntax and operator set, and that's ok. Even if that's not the most beautiful set, at least it looks like most other braces-and-semicolons languages. Diverging from that doesn't seem clearly better, even if it makes individual operators better.

24 Likes

As non-English (even non-Western language) speaker, I don't like keywords (or logical operators) like and and or.

I expect programming languages to use sigils to represent very fundamental or special meanings in the code. To me (and possibly many other people who also aren't fluent English speaker), sigils can be quite easily distinguishable from english symbols. For example, when reading foo && !bar && baz == qux, I can quickly recognize identifiers (symbols) foo, bar, baz, and qux, and imagine syntax tree that has them as a leaf.

However, when special and/or fundamental things are represented with english-like keywords, it would take more time to get the code's meaning. For example, when I see foo and not bar and baz is qux, I'll be confused and takes time to understand the expression, because keywords (operators) and identifiers (operands) look similar and my eyes slip on them (even if they are syntax-highlighted).

Same reasoning applies to |args| body syntax, I prefer it over lambda args: body or something like that. (Using same | to opening and ending would not be a good idea, but it is another topic.)

What I wanted to say is: Sigils would help some of non-English-native to read the code, and "naturally readable as an English-like statement" won't be nice feature for such people (at least for me). I think that comparisons, arithmetic operations, and logical operations are fundamental enough and worth using sigils.

9 Likes