Zig and Rust have addressed the problem of how the result of a block expression should be presented, but neither solution seems particularly satisfying to me.
In Rust, blocks may end with an expression, giving them a non-void result. But a block may also end in a statement, the only difference being that the statement ends in a semicolon, in which case the expression still has the void result, and I think that semicolon being the only difference makes it hard to scan at a glance where values come from.
In Zig, blocks may give non-void results by `break`ing out of them with an expression. But break normally ignores blocks and break out of loops only, so to break out of blocks you have to provide a label for it and give that when you break so as to break out of the named block and not the outer loop, e.g. `const x = label: { break :label 35; }`. That creates a problem of one of the most difficult classes in software engineering: naming things. Ideally I think `break` from a block should have its own keyword, e.g. `const x = { give 35; }`
Right now, `if` in expression position is just a syntax error ("unexpected symbol")
But other than that, yeah, detecting "if" in the expression position is pretty unambiguous. No idea why most languages went with "cond-expr ? then-expr : else-expr" bracketed syntax instead.
But e.g. ml-family languages (like OCaml, F#, Haskell) and Rust just have the *if* expression that has a non-void value. If your language accepts expressions as statements (most do?), then I think that should just be compatible out of the box.
Oh, and Lua most famously does not accept expressions as statements. Which, now that I think of it, would actually evade most of the parsing complications.
local x = condition ? value_a : value b
local x = condition and value_a or value_b x = a ? b : c # x is b, same as you would if a {x=b} else {x=c}
lua and/or a,b,c = true, 1, 2
x = a and b or c -- x is b
a,b,c = true, false, 2
x = a and b or c -- x is c
The or is dependent on its previous operand, so b will return false and skips to c, even if you meant for it to be b. you must use an if then else. However, you can have more than a ternary, if there is no need for short-circuit evaluation, as in, any of the operand is not a function CALL like c(), and you want to remain inside an expression, then you can do this insteadselect (select is a native C function, this is faster than the table creation below)
x = select(a and 1 or 2, b, c)
table creation/selection x = ({false,2})[a and 1 or 2]
of course, doing something like local x; if a then x=b else x=c end
does not look so bad> E.g. true and false or 42 returns 42, whereas true ? false : 42 returns the (expected) false.
local x = y and y + 1 or 0
The knuckle heads are already using them everywhere.