Idea: postfix let as basis for postfix macros

Just for fun an experiment what Rust would look like if everything was postfix (in .await style)

rand::Rng.use;
std::cmp::Ordering.use;
std::io.use;

{
    "Guess the number!".println!();

    rand::thread_rng().gen_range(1, 101).let secret_number;

    {
        "Please input your guess.".println!();

        String::new().let mut guess;

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        guess.trim().parse().match {
            Ok(num) => num,
            Err(_) => continue,
        }.let guess: u32;

        "You guessed: {}".println!(guess);

        guess.cmp(&secret_number).match {
            Ordering::Less => "Too small!".println!(),
            Ordering::Greater => "Too big!".println!(),
            Ordering::Equal => {
                "You win!".println!();
                break;
            }
        }
    }.loop;
}.fn main();

loop and fn are absurd, but let isn't as terrible as I thought, and postfix match is actually pretty cool.

6 Likes

Postfix match looks pretty cool because it performs a value-to-value computation, which conforms more or less to the principle of using postfix for method notation. That's also mostly true for try, at least in the sense that the appearance when part of a statement is value in, value out. The others do not resemble that style.

3 Likes

I like this idea. But I think it only makes sense for expressions (let, match, loop, break with value, ...), not so much for items (use, fn, ...).

Another use case are references:

&mut guess  -->  guess.&mut
*guess      -->  guess.*
1 Like

It's the Reverse Polish Notation form of Rust, for which I suggest the Polish equivalent name Rdza!

5 Likes

But the reverse Polish equivalent should be "azdr"

2 Likes

Which is why postfix let is an expression - unlike prefix let which is an item.

Actually, let is a statement. Items are things that can appear outside of a function, and were the order is unimportant.

Postfix let seems a little funny to me, and it's not immediately obvious what it does. However, postfix match seems quite natural, and seems like it would allow all of the same use-cases. (One could emulate postfix let by using postfix match with a single arm.) Given that, I quite like the idea of using it to enable useful postfix macros that function solely through token substitution, and don't have surprising access to tokens outside of the macro call.

6 Likes

That looks completely backwards to me (no pun intended). I'm not a native English speaker but I read declarations as let foo equal to some expression. I would hate having to read them as some expression let foo.

match is weird, too. I'm matching the discriminant expression, not the variants, i.e. I read it as

match guess.cmp(…) against/with {
    Ordering::Less => …,
}

and not as guess.cmp(…) match with { … }.

I think if someone likes an alternative postfix-like syntax so much that s/he likes to program in postfix, s/he could make a macro (declarative or procedural) wrapping an entire expression or function and call it a day – since this is a complete, entirely different style, and we don't need a whole other, purely syntactic split.

Heck, there's even a macro-based DSL for writing embedded Lisp. Postfix Rust should be a similar crate, it should not be an addition to the core language.

@kornel just changed everything to postfix indiscriminately, but in my suggestion postfix let is not a declaration - it's an expression. For declarations the posix style indeed looks weird, but for expressions it's natural and read natural if you use words like "it", "that" or "the result" to refer to the previous part of the calculation. For example:

let res = foo().let it { square(it) }.let it { round(it) }.let it { sqrt(it) };
let res = sqrt(round(square(foo())));

The first is read as "let res be defined as such: take foo(), square it, round it, than take the square root of the result of that". Which is more natural than "let res be the square root of the rounded square of foo()"

Or, in case of match:

guess.cmp(goal).match {
    Ordering::Less => {
        println!("Sorry too small");
    },
    Ordering::Greater => {
        println!("Sorry too big");
    },
    Ordering::Equal => {
        println!("Spot on!");
        break;
    },
}

Can be read as "compare guess to goal. If it's lesser - print 'Sorry to small'. If it's greater - print 'Sorry too big'. If they are equal - print 'Spot on!'". Unlike prefix match, here you first read about the expression and then only then say that you are going to match it.

As for your concern regarding the "syntactic split", this can be a good reason to go with postfix let. Because it has different semantics than regular let, it won't split the syntax and won't become a matter of personal preferrance.

I might have used the wrong term but the point is not whether it is an expression or a statement. It is equally weird even if it is an expression.

What's so weird in writing a chain of transformations in the order they happen?

2 Likes

One could easily argue the opposite on this example IMO.

The current syntax reads like “Match what you got when you took guess and compared it with against {…}”.

A postfix match would read like “Take guess, compare it with , and match the result against {…}”.

I find the second (even a bit shorter) version nicer since things are stated in the order they happen in; especially when the discriminant expression gets even larger, starting out with match is not necessarily ideal. Ordinary match syntax even reminds me a bit of C-style error handling, where you write if (statement) {...} to handle error codes, however this programming style in C reads (to me) more like some block of code that happens optionally and does somewhat hide its main point: that is, statement is unconditionally executed here.

I guess that following this logic, a postfix if let of some sorts would be nice, too. A postfix ordinary let however would not be something that I’m in favor of.

1 Like

I'd like to second the position that postfix match is by far the most readable of the postfix options here. I'd even go so far as to say match is the only one worth seriously proposing; most of the others I still struggle to read despite already knowing exactly what they mean, especially postfix let.

(I still have no idea if I'm in favor of actually adding postfix macros or postfix match, but the idea of making postfix macros less weird by expanding to postfix match is certainly intriguing and plausible)

7 Likes

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