Why do we have the distinction between statements and expressions in most programming languages?

For example, in Java, assuming f and g return ints, this still won't compile because it's a statement and statements don't return values.

// won't compile
int i = if (pred) { 
} else {

but in Scala, it's very happy with treating if as an expression.

// compiles fine
val i: Int = if (pred) f(x) else g(x)

So if there's no problem treating an if statement as an expression, why was there a distinction in the first place?

  • Lisp, Haskell, Ocaml don't have instructions (only expressions). And IIRC, the very first C compiler -or perhaps B- neither (but I may be wrong) – Basile Starynkevitch Aug 22 '14 at 7:01
  • So what happens if I just write int i = if (pred) { f(x); }? – Simon B Aug 22 '14 at 11:14
  • @SimonBarker, that produces a type mismatch error at compile time. – Karl Bielefeldt Aug 22 '14 at 13:05

The if could return a value here because both of the functions also return values. But there are void functions as well, and f and g might have different return types, which would make it unclear what the type of this if would be. In effect, if you ban statements from a programming language, you also ban void methods, strict typing and some more common constructs that a lot of people don't want to see go.

(And yes, I am aware that other people would be very glad to see them go and will tell me and you and everyone at great length and high volume how glad they would be - this is a Holy war, please don't fuel it.)

  • 1
    Why are you banning strict typing? – Tobias Brandt Aug 22 '14 at 7:36
  • An if whose branches return different types is simply illegal in such a language. void methods don't exist, instead the unit type () is used which has a value (only a single, and hence meaningless, one) to be returned. And unless you pursue purely functional programming (in which case none of your concerns even arise), you will have a operator to "ignore" a value, i.e. execute an expression for side effects and then return (). Armed with that, you can trivially write if (c) { f(); } else { g(); } where f and g return different types and ; is the execute-and-ignore operator. – user7043 Aug 22 '14 at 8:31
  • If f and g have different types, then the type of the if expression will be the most derived common supertype. Easy. – Jörg W Mittag Aug 22 '14 at 21:06

Because it is confusing, therefore it is easier to introduce bugs.

One of the most famous in C is: if (a = b) instead of if (a == b). With a distinction between expressions and statements, the compiler would have complained.

Going on with C, there is a specific form of if whenever it should return a value:

pred ? f(x) : g(x)
  • A distinction between expressions and statements doesn't necessarily help with if (a = b), as long as assignment is an expression (it's one in Java and C# which address this problem through other means). Making assignment a statement would help, but equally well one can restrict if to boolean values (Java, C#) and in a expression-oriented language, additionally make it the moral equivalent of a "statement": An expression that returns the unit type (), which is what Rust does. – user7043 Aug 22 '14 at 8:24
  • 1
    @delnan - You are correct. In my mind an assignement is a statement, not an expression, i.e., let a take the current value of b, without the second part: and return that value. – mouviciel Aug 22 '14 at 8:56

Not the answer you're looking for? Browse other questions tagged or ask your own question.