Extremely pre-rfc: postfix keywords for all

I've been thinking more and more than mixing prefix and postfix things is just confusing, whether that's -1.abs() or *foo[bar] or whatever. I'd be quite happy to entertain proposals to do better somehow for these things. That might be new lints, new syntax, new impls, ... -- I don't know.

3 Likes

But why do those of us who prefer postfix order for operations that are applied successively left-to-right have to live within the constraints of people who prefer prefix order? I understand that prefix order might seem more natural to people who use a right-to-left writing system, but most European languages do not.

3 Likes

The pointer-dereference operator should always have been a postfix operator, it's a crying shame that Rust copied one of C's most irritating papercuts here.

6 Likes

I was not expecting to like postfix pointer refs / derefs as much as I do seeing it above.

I never really considered how * or & on the left for something on the right really disrupts my flow of reading code, but I know it trips me up sometimes, especially when expressions grow in length. It's extremely clear to me what is being referenced or dereferenced when reading everything left to right.

5 Likes

See:

1 Like

I mean, it's the other way around as well. I'd love it if all control flow jump directives where statements that work via prefix like return, continue and break. But there's also ? and .await that go less visibly at the end or in the middle of things. And one of those is even control flow that can keep things alive after leaving the context.

There's also the fact that postfix only works when you're applying a value to something. So in general, prefix works in more cases.

I'd also like to note that I find sentences like

But why do those of us who prefer postfix order for operations that are applied successively left-to-right have to live within the constraints of people who prefer prefix order?

unhelpful and unnecessarily antagonizing. I wish I had a prefix version of ? and .await. I'd be happy to give up prefix dereferencing for that, as I find the control flow aspects more important (but only slightly).

And I'll once again reiterate that we wouldn't need any of this if we had postfix macros.

3 Likes

Sorry you found my statement antagonizing. I had meant it to be consciousness-raising, pointing out that less-conventional viewpoints can be reasonable. I certainly did not intend to offend.

6 Likes

I would certainly agree with that, and I would very much welcome a shift in philosophy towards Rust offering multiple solutions that can cover a wider variety of use-cases.

In the past this has often been considered fragmenting the language, much to my dismay. There was always more focus on finding one solution that fits many people, and declare that idiomatic, which kind of leaves everyone not in the majority a bit in the dust.

Either way, thank you for clarifying. I certainly understand that these issues aren't unique to my side of the fence.

2 Likes

Moderator note: I've deleted a number of comments in this thread. If people have questions about moderation, then contact the moderators at rust-mods@rust-lang.org. Derailing an entire thread into meta discussion is completely inappropriate.

Moreover, if you can't provide criticism in a constructive and friendly way, then your commentary will be silenced. So please don't do that. Find a way to voice your criticism in a more constructive way. If you aren't sure how to do this, then contact the moderators.

Finally, if other folks in the community point out that your commentary is less than constructive, then resist the urge to debate it. If you truly have concerns about it, then contact the moderators.

10 Likes

Await could not have been implemented as a postfix macro, as it cannot be a macro.

I have mixed feelings about this idea.

On one hand, "there should be one and only one way to do it" is a very important principle in any sort of API design, because dialectification is a real danger. See the myriad C++ style guides, of which clang-format implements several.

Further, things like .match (which I don't think are in the OP but are suggested in the boat's .await blogpost) push us towards making it easier to avoid let bindings. The Haskell point-free style is cute but hard to follow for anything non-elementary.

However, IDEs I've used in the past offer suffix-style completions that expand to the prefix versions, which are generally quite pleasant; if rustfmt could intelligently convert between suffix and prefix forms, with the suffix form explicitly meant to be convenience that would be converted to prefix by on format, then maybe we might be getting somewhere.

That said; it is generally my impression that long chains of operators indicate either:

  • A builder, which isn't really relevant to this discussion.
  • A lack of judiciousness in choosing when to declare well-named let bindings.
  • A lack of well-named convenience functions.
  • A language deficiency that prevents judicious use of let bindings and convenience functions, resulting in a pile of *, &, and as sigils.
3 Likes

Hasn't anybody thought about some sort of pipe operator such as https://elixirschool.com/en/lessons/basics/pipe-operator/?

1 Like

Using the postfix await as an argument to now allow all keywords to be used in postfix position doesn't seem quite right for me.

I suspected that it will go this way, because in the discussion about the postfix await the argument was brought up, that in the future all keywords could be made postfix and then everything will be consistent again.

Consistency is a high and very appealing value, but this doesn't mean that it's always the right value and can't also be harmful.

I'm afraid that allowing postfix keywords will make Rust code harder to read, because the style of Rust code will differ more. There will just be people that prefer one style over the other.

The advantage of postfix keywords - to be able to read the code from left to right - is also to some degree its disadvantage, that it's harder to skim over the code and seeing its structure.

I think it shouldn't be be just strived for an abstract value of consistency, but considering what might increase or decrease the readability of the language altogether.

4 Likes

Two things:

  • It's not really true. Rust can make internal macros do whatever it wants. Specifically in combination with a #[async] attribute. It was decided that it shouldn't. That is different. Please don't phrase things as me being misinformed when it's just a case of the language team making a different decision.
  • future.apply!(f: await f) Here's a postfix macro solving all chaining wishes.

As a related comment: There was a poster here who was removed because he got agitated about .await. This kind of dogmatic response as if you're imparting wisdom from on high that I've read in the same way 100 times now as justification is the reason some people are agitated.

Edit: For completeness sake, here's a possible implementation of a postfix await macro, assuming we have normal await and postfix macros:

macro_rules! .awhat {
    () => { await $self }
}
6 Likes

The original topic of making postfix operators of operators that don't currently have postfix versions:

Outside of unsafe code, it seems like postfix ref and deref would rarely get used, since method and field access already take care to perform that task automatically. With how much cast already helps, it would really just need another two convenience methods on raw ptrs and that would be done.

trait ConvenienceRefExt {
    fn as_raw_mut(self) -> *mut T;
}
impl<T> ConvenienceRefExt for &mut T {
    fn as_raw_mut(self) -> *mut T { self as _ }
}
trait ConvenienceRawExt {
    fn as_ref_mut(self) -> &mut T;
}
impl<T> ConvenienceRawExt for *mut T {
    fn as_ref_mut(self) -> &mut T { &mut *self }
}
x.y.as_raw_mut()
   .cast::<U>()
   .add(idx)
   .cast::<V>()
   .as_ref_mut()
5 Likes

Awesome, That's totally what I want!

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.