Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

And they still haven’t! Although expressions over statements would be a breaking change - I can’t see any mainstream language making this switch.



Barry Revzin has written a paper to try to give C++ expressions like this. It's ugly, even by the standards of C++ but it would work. The rationale is that C++ 29 or C++ 32 wil probably get pattern matching and pattern matching doesn't have great ergonomics if everything is a statement rather than an expression. There are plenty of other things C++ will need to fix once it gets pattern matching, but the direction makes sense if you insist on trying to teach this particular old dog new tricks.

Also, I think Rust would consider itself to be a mainstream language, though they aren't techically "making this switch" because the language has always been expresion oriented from the outset. The Book has about two paragraphs about the statements in the language and then a whole chapter on expressions because almost everything is an expression.


I actually think it's becoming fairly common these days—IIRC Ruby and Rust both prioritize expressions over statements.


They do, but the fact that there is even a distinction at this point is rather baffling. If your type system has the unit type and the bottom type, all statements should just be expressions having one of those two types.


Well, Rust barely has statements [1]. Nearly everything in Rust is an expression, and AFAIK statements /are/ essentially expressions that yield `()` [2].

[1]: https://doc.rust-lang.org/reference/statements.html

[2]: https://play.rust-lang.org/?version=stable&mode=debug&editio...


> AFAIK statements /are/ essentially expressions that yield `()`

This isn't true. The inverse is true, "expression statements" can turn an expression into a statement.

What you're seeing in the playground is just how blocks are defined[1]:

> The syntax for a block is {, then any inner attributes, then any number of statements, then an optional expression, called the final operand, and finally a }.

In this case, you have one statement, and no optional expression.

And so:

> The type of a block is the type of the final operand, or () if the final operand is omitted.

So that's how this works.

Now, that being said, I don't think it's too terrible of a mental model to think of this situation in that way. But if we're getting into nitty-gritty details, that's not actually how it works.

1. https://doc.rust-lang.org/stable/reference/expressions/block...


Oh I see. I assumed being able to explicitly yield unit meant statements were essentially just syntax sugar for unit. Thanks for the correction.


No problem! This really only comes up in very edge cases anyway, which is partially why I don’t think it’s the worst mental model for most people. As long as you know that you can’t put let in expression position, that’s really the only place it goes wrong. Most people who haven’t used a language like Ruby would even think of items as being expressions in the first place.


Out of curiosity, why does Rust make the expression/statement distinction? Does it interact with the borrow checker somehow?


> Does it interact with the borrow checker somehow?

Nope. The borrow checker cares about the control flow graph, expression vs statement doesn't matter.

> why does Rust make the expression/statement distinction?

I am not 100% sure. If I had to guess, older Rust was much less expression oriented, and then, over time, got moreso.

But also, I think it kinda just makes sense in general for the kind of language Rust is. Like, in Ruby, where everything truly is an expression, importing a file and then evaluating it has side effects. Whereas in Rust, 95% of statements are declarations, and of those 95%, the only ones you really use in a normal execution context are let statements. The rest are stuff like "declaring functions" and "declaring structs" and those don't really get evaluated in a language like Rust.

let being a statement is nice because it means it can only happen at the "top level" of a block, and not say, inside a loop condition.


> Like, in Ruby, where everything truly is an expression, importing a file and then evaluating it has side effects.

In the context of ML, I think it's a more useful baseline. So declarations are still declarations, but e.g. ; is just a sequential evaluation operator.

> let being a statement is nice because it means it can only happen at the "top level" of a block, and not say, inside a loop condition.

I would argue that it's actually a downside - it means that a loop condition cannot have common subexpressions (that nevertheless need to be evaluated on every iteration) factored out.


I guess if you required ; after all of them, sure. Rust doesn’t, so I didn’t think of that.

I’ve always found code that does this to be more complicated to read and understand than rewriting it in a different way. YMMV, of course.


> Like, in Ruby, where everything truly is an expression, importing a file and then evaluating it has side effects.

That's not so much “everything is an expression” as “everything is at runtime”.


Eh that’s fair.


People really like their early return statements however.


Kotlin is also nicely expression-oriented


Still waiting on real pattern matching in TyoeScript.


You won’t get it till TC39 adopts it




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: