8 ms·
Statements vs. Expressions
- wruza 4y agoNo intent to sound negative, but doesn’t explanation by numerous cues mud the water even more? I bet most of students are left confused with this “wrapper statement” thing. Magical thinking induced. If the question is how they are different, then the answer is grammatically, and then the explanation of a grammar follows. If it is why we make such distinction, then the answer is because the language creator decided so, followed by tradeoffs and pitfalls of making a specific language construct to have a value.
- epolanski 4y agoI feel the same towards the article, I think that the moment you need to resort to "think of it as..." it's the moment you lose me. I also don't think he does a good job at explaining the biggest source of misunderstanding. Quoting the article: `Expressions and statements are distinct things.` That is not really correct. A function call, e.g. is both an expression and a statement: `foo(bar)` on it's own is both an expression (it returns a value, even if the value returned is undefined) and a statement. In fact, determining if a piece of code is an expression or a statement in all borderline cases depends on the context. ``` const foo = function bar(){}; function bar(){} ``` On line 1 the bar function is an expression, on bar 3, is a statement. There's nothing really about the function on it's own that can tell you whether it is an expression or a statement. On the other hand, a nice way to differentiate statements from expressions is that you cannot use statements where you need expressions. ``` console.log(if (3 > 2) {}) const foo = while (i < 3) {} ``` both lines are syntax errors.
- azangru 4y agoI've never read the ECMAScript spec; but searching through it, I can see that the terms they use is function declaration vs function expression. According to their terminology, function bar(){} on the second line of your function example is a function declaration.
- epolanski 4y agoYes, and a function declaration is a statement.
- dtagames 4y agoThe way I think of it is that statements involve keywords from the language (like 'let', 'export class', or 'if') while expressions do not. With this definition, it's easy to see that, if the syntax starts with a language keyword, it's a statement. BTW, language functions (like 'Array.join()') are expressions because they don't start with a keyword.
- Measter 4y agoBut that logic falls apart with languages such as Rust, where a lot of expressions start with a keyword.
- dtagames 4y agoAh. Was just thinking of JS/TS in this case. Good point.
- kgeist 4y agoSomething which was once a statement and started with a keyword can become an expression in a new version of a language. For example, in C# 7, throws were made into expressions to allow syntax like this: return a ? b : throw new Exception(); So I wouldn't rely on this rule.
- dtagames 4y agoOuch! I like to rely on the ternary operator as a cheat for if/then just as much as the next guy, but that qualifies as abuse, lol!
- kaoD 4y agoI wouldn't say that's abuse and I missed that many many times in JS/TS, for example on unreachable variants in tagged enums. This usually turns into null with a null check afterwards just to throw the exception, which is more verbose for no reason.
- 4y ago
- bluepnume 4y agoIf this compiles, <something> is an expression: let x = <something>; If this compiles, <something> is a statement: <something>; Both, either, or neither can be true.
- hibbelig 4y agoI can't think of an example where let x = <something>; compiles but <something>; does not compile. I think all expressions are statements.
- lmm 4y agoSome languages forbid "obviously pure" expressions like "0" as statements (since using them as a statement is probably a mistake).
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- 4y ago
- FeepingCreature 4y agoStatements are orders, expressions are questions.
- SeanLuke 4y agoAsk yourself why Lisp has no statements. Is it impossible to issue orders to the Lisp runtime?
- FeepingCreature 4y agoLisp does have statements, such as `setf` or `loop`. Just because you also attach an expression to it because hey, gotta evaluate to something, doesn't mean it's not fundamentally a statement. Sure, syntactically it's an expression-based language, but some of those are expressions in syntax only. If a function terminates in nil, what exactly did it compute? And what exactly is the point of all but the last argument to a `progn`? An expression that is evaluated for the side effects, not the result value, is a statement in every way that matters. (Haskell has a stronger claim because it actually captures the side effects as a value.)
- SeanLuke 4y ago> Lisp does have statements, such as `setf` or `loop`. Just because you also attach an expression to it because hey, gotta evaluate to something, doesn't mean it's not fundamentally a statement. I think the general consensus is that expressions evaluate to values in unexceptional situations, full stop. Whether they have side effects is immaterial. Whether the value returned is useful or not is also immaterial. Perhaps your examples weren't the best, since both setf and loop evaluate to values which are often useful. This isn't just Lisp. For example, the C99 specification explicitly declares that all function calls are expressions, even if they return void. Wikipedia also defines an expression (vs. statement) unambiguously as "a syntactic entity in a programming language that may be evaluated to determine its value.... Expression is often contrasted with statement—a syntactic entity that has no value (an instruction)." https://en.wikipedia.org/wiki/Expression_(computer_science) https://en.wikipedia.org/wiki/Expression_(computer_science)
- niels_bom 4y agoAs a programming teacher I think this is quite a fundamental thing to learn for new programmers. Even though lots of tutorials and books don't mention this explicitly people usually get an intuition for the difference after a while. But in the beginning it's hard for people to grok an expression with a couple of nested expressions in it. Especially if there's some functions calls in there as well. Axel Rauschmayer has a good page (1) on this topic as well. Quite dry, so less beginner friendly. So what I like about Josh W Comeau's article is that it's easy to read with lots of examples and even an interactive widget. Oh and about interactive visualization of expression evaluation: Thonny (2) does this real nicely for Python code. Excellent for beginners imo. [1] https://2ality.com/2012/09/expressions-vs-statements.html https://2ality.com/2012/09/expressions-vs-statements.html [2] https://thonny.org/ https://thonny.org/
- submeta 4y agoThe article left me more confused than enlightened. My understanding is this; Expressions return a value, statements on the other hand are actions, commands, definitions, declarations, assignments, and control structures.
- nivertech 4y agoNot a JS-specific answer to this question: expression - generally returns a value, possibly a void value, or doesn't return control at all. - it consists out of operators, operands, and function calls - can be composed out of one or more sub-expressions, and the order of their evaluation isn't always defined (i.e. to allow compiler optimizations). - usually it doesn't have side-effects (i.e. pure), unless it uses random, time, IPC, system, or HW-specific pseudo-functions, pseudo-variables, or macros as a sub-expressions. Nested assignments and variable increments/decrements/etc. are also not pure. - a result of a function call statement - usually an imperative command - usually indicate a side-effect (e.g. assignment, sending a message, etc.) - can be a procedure call or macro - can be composed out of expressions and nested statements, but with better defined semantics / order of evaluation - a special case: control flow statements - a special case: "return" statement - evaluate a value and returns it as a result of the current function
- chrismorgan 4y ago> statement > - a special case: control flow statements But it’s worth noting that this is not true in all languages: in some languages, almost everything or actually everything is an expression. In Rust, for example, control flow things may be statements or expressions (a mildly fuzzy distinction, but it comes down to whether it produces a value other than (), and whether it needs a semicolon after the closing curly brace), and some control flow things are very useful as expressions: let a = if b { c } else { d }; let e = loop { break f; // f is a value, not a label (which uses lifetime syntax, 'g). };
- nivertech 4y agoTernary operator is an expression, first implemented as COND in Lisp. In some cases it can be optimized to remove branching, especially when vectorized for SIMD/SIMT. int a,b,c,d; a = b ? c : d; // optimized to something like this: a = b*c + (1-b)*d In some PLs even loop statements can produce values (CoffeeScript, Autocode), i.e. similar to list comprehensions, which are undoubtedly expressions. I'm a kdb+/q developer, and most of the q code are expressions without conventional control flow operators (the only place they're used is outside of the main path: i.e. logging, testing, etc.). IMO control flow (i.e. actual branching and looping) is just an implementation detail.
- usrbinbash 4y agoI think the simplest explanation is; "An expression can be used where a value is expected". What is sometimes confusing, is that some statements also double as expressions, that is, also return values; and that it also depends on the language. eg. the assignment statement returns a value in C and Javascript, but not in Go or Python; printf("%d\n", a = 42); // out: 42 console.log(a = 42) // out: 42 fmt.Printf("%d\n", a = 42) // out: syntax error: unexpected =, expecting comma or ) print("%d" % a = 42) # out: SyntaxError: expression cannot contain assignment, perhaps you meant "=="? Also depending on the language, expressions themselves may be valid statements. For example, this compiles in C; int main() { 12 + 42; } ;this is a valid python program: 12 + 42 ;but if I try this in Go: package main func main() { 42 } // ./main.go:3:5: 12 + 42 (untyped int constant 54) is not used
- bluetomcat 4y agoGenerally, an expression returns a value and can be used as the operand of an operator, whereas a "statement" is the terminal "top-level" node that doesn't return a value and cannot therefore be nested.
- deleted 4y ago[deleted]
- oxff 4y agoExpressions `yield` values, statements don't yield values. `square = square(4)` is a statement (a definition to be exact) that associates name `square` with the value `square(4)`. The name `square` itself, when later used, is an expression, because when you call it, it evaluates to a value `square(4)`.
- amadeuspagel 4y agoFor example, you might have tried writing something like this: foo = if (x < y) { ... ... } Doesn't work because statements don't yield values.
- Akronymus 4y agoThats one of the reason why I REALLY like languages where everything is an expression. let foo = if x < y somevalue else someothervalue in f# for example.
- bern4444 4y agoI also love F#. One of the most interesting languages that I've ever seen. This is certainly another great aspect of it.
- bern4444 4y agoSo are ternaries expressions then too? const x = x < y ? 2 : 3;
- amadeuspagel 4y agoYes, indeed.
- daralthus 4y agoI would like to see a programming language where there is no distinction between the two. This might sound silly first, but once you start thinking about homoiconicity or about function approximations, like neural nets that can represent any function (as sort of a compressed lookup table) then you can start to imagine how a sufficiently advanced compiler should be able to inline approximate representations, or switch to calculating on the fly depending on needs and available resources.
- amadeuspagel 4y agoLisp?
- wruza 4y agoNot sure if it was irony, but check out Haskell and alikes. Its execution model is different from a regular “it evaluates at where it is”. Infinite lists like all natural numbers are a norm in it. ones = 1 : ones take 3 ones // [1,1,1] odds = 1 : map (+2) odds take 3 odds // [1,3,5] Haskell has a statement-like “do” notation, but it is a syntactic sugar for a structure of specific operators. Lisp is also a beast to be aware of, though it is not exactly what you described.
- remkop22 4y agoRust actually accomplishes this with the unit '()' type which is similar but not the same as void in other languages. Any statement (or expression) produces a value, sometimes that value is just a unit, which can be passed around just as any other value. Currently the exception to this are 'let' statements but there is a nightly feature where this produces a unit value as well.
- amadeuspagel 4y ago> I think we often blame React for seemingly arbitrary rules, like how components must return a single top-level element. But more often than not, React is just warning us about a JavaScript limitation. Really? With lit you can create a component that has several top-level elements[1]. EDIT: In fact this works even in react/preact with htm[2], so it's just a limitation of JSX. [1]: https://lit.dev/playground/#project=W3sibmFtZSI6InNpbXBsZS1ncmVldGluZy50cyIsImNvbnRlbnQiOiJpbXBvcnQge2h0bWwsIGNzcywgTGl0RWxlbWVudH0gZnJvbSAnbGl0JztcbmltcG9ydCB7Y3VzdG9tRWxlbWVudCwgcHJvcGVydHl9IGZyb20gJ2xpdC9kZWNvcmF0b3JzLmpzJztcblxuQGN1c3RvbUVsZW1lbnQoJ3NpbXBsZS1ncmVldGluZycpXG5leHBvcnQgY2xhc3MgU2ltcGxlR3JlZXRpbmcgZXh0ZW5kcyBMaXRFbGVtZW50IHtcbiAgc3RhdGljIHN0eWxlcyA9IGNzc2BwIHsgY29sb3I6IGJsdWUgfWA7XG5cbiAgQHByb3BlcnR5KClcbiAgbmFtZSA9ICdTb21lYm9keSc7XG5cbiAgcmVuZGVyKCkge1xuICAgIHJldHVybiBodG1sYFxuICAgICAgPHA-SGVsbG8sICR7dGhpcy5uYW1lfSE8L3A-XG4gICAgICA8cD5BbmQgYW5vdGhlciBFbGVtZW50ITwvcD5cbiAgICAgYDtcbiAgfVxufVxuIn0seyJuYW1lIjoiaW5kZXguaHRtbCIsImNvbnRlbnQiOiI8IURPQ1RZUEUgaHRtbD5cbjxoZWFkPlxuICA8c2NyaXB0IHR5cGU9XCJtb2R1bGVcIiBzcmM9XCIuL3NpbXBsZS1ncmVldGluZy5qc1wiPjwvc2NyaXB0PlxuPC9oZWFkPlxuPGJvZHk-XG4gIDxzaW1wbGUtZ3JlZXRpbmcgbmFtZT1cIldvcmxkXCI-PC9zaW1wbGUtZ3JlZXRpbmc-XG48L2JvZHk-XG4ifSx7Im5hbWUiOiJwYWNrYWdlLmpzb24iLCJjb250ZW50Ijoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJsaXRcIjogXCJeMi4wLjBcIixcbiAgICBcIkBsaXQvcmVhY3RpdmUtZWxlbWVudFwiOiBcIl4xLjAuMFwiLFxuICAgIFwibGl0LWVsZW1lbnRcIjogXCJeMy4wLjBcIixcbiAgICBcImxpdC1odG1sXCI6IFwiXjIuMC4wXCJcbiAgfVxufSIsImhpZGRlbiI6dHJ1ZX1d https://lit.dev/playground/#project=W3sibmFtZSI6InNpbXBsZS1n... [2]: https://github.com/developit/htm https://github.com/developit/htm
- jesperlang 4y agoI find it better to actually look at the AST for javascript. These are expressions: https://github.com/estree/estree/blob/master/es5.md#expressions https://github.com/estree/estree/blob/master/es5.md#expressi... These are statements: https://github.com/estree/estree/blob/master/es5.md#statements https://github.com/estree/estree/blob/master/es5.md#statemen... I guess the confusing part for many is how an expression can also be a statement. But if you look at the ExpressionStatement you see that an expression is NOT also a statement. It's just the wrapper statement that makes it look so!
- lucideer 4y agoProbably worth adding "(javascript)" to the HN title - for non-JS see @nivertech's excellent comment here. I may be wrong but I actually think the explanation can be a little simpler than the article posits: an expression (in JS) is something that returns a value. That's it. The value can be undefined/void, but it's still a value being returned. One important detail the article seems to miss that I've come across whie explaining JS syntax to JSX-learners is that, while statements usually contain expressions, expressions can also contain statements. This is because a function definition returns a value (the function instance) & obviously function bodies can contain any JS syntax. This is useful if you (for some odd reason) want to embed a statement in some JSX code (since JSX only supports JS expressions).
- deleted 4y ago[deleted]
- hardwaregeek 4y agoThis is why taking a basic programming language course is pretty important. I never remembered the difference until I had to represent both in an AST. You can teach it without talking about ASTs or interpreters, but it's teaching without any context or motivation, thereby making it harder to learn. I do think mandatory semicolons make the distinction a little easier. If it has a semicolon, it's probably a statement. And for most C-style languages if it has curly braces, it's probably a statement too. Otherwise it's an expression.
- tomxor 4y agoI feel like the examples and description for "statements" fails to demonstrate their breadth. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... In fairness it's difficult to talk about statements accurately and succinctly without being more abstract - and perhaps the more general observation here is how explanation by example alone is not always sufficient. A more general and yet tangible starting point for statements in JS could be "anything ending in a semicolon" (not quite true but almost for normal code), which at least is better at highlighting it's breadth before piling into arbitrary examples.
- substation13 4y agoThey should deprecate Array.prototype.forEach for muddling statements and expressions!
- gumby 4y agoOne of the nice things about Lisp is there are no statements, only expressions. It's easier to think about your code. Lisp was the first language I used (other than assembly code) so when I encountered C I was puzzled why they even bothered to have statements at all. 40 years later I still don't understand the point.
- zabzonk 4y agoUnderstand where you are coming from, but given the nature of C, what is the compiler to do with something like: 1 2 3 which are three expressions, and probably the programmer meant "123". Of course: 1; 2; 3; is OK.
- gumby 4y agoBut that’s the point: what value does a language gain by making the distinction? It removes composability for no gain.
- zabzonk 4y agoThere is some gain in that you need to explicitly say that something is evaluatable, like: (+ 1 2) as opposed to: 1+2 Whether the gain is worth it is, I agree, debatable.
- gumby 4y agoLisp’s homoiconic representation is not relevant to this issue. In C, `1+2` is an expression that returns 3. `1+2+3` acts like two expressions (and perhaps it is; I forget the precise terminology used in the standard). You can write `return foo(1+2)`. But `for` introduces a statement so can only be used where a statement can.
- goto11 4y agoHaskell is also based on an everything-is-an-expression syntax, but ended up re-inventing statements one level up, in the do-notation blocks. To me, this indicates there is some merit to the statement/expression distinction.
- chrismorgan 4y ago> (5 + 1) * 2 That’s six expressions, not five. Josh conflates the the addition expression and the parenthesised expression, but they’re distinct (even if you couldn’t represent the semantics without the parenthesis due to operator precedence). Here’s an approximate representation of the parse tree, indicating which nodes are expressions: MultiplicativeExpression { ← ParenthesizedExpression { ← AdditiveExpression { ← DecimalIntegerLiteral("5") ← "+" DecimalIntegerLiteral("1") ← } } "*" DecimalIntegerLiteral("2") ← }
- beeforpork 4y agoI am puzzled. 'statement' and 'expression' are items of JavaScript syntax (or C or Java or C++ or Perl or whatever). So to explain it, you point to the syntax definition. This isn't going to be explained satisfactorily by analogies or intuition, and it won't be logical at all, because it isn't, and you could define the grammar with no such distinction or a different set of distinctions as many posters already showed here. Besides being arbitrary, I also never found this a useful distinction after having programmed in languages that don't distinguish this. And look at how GCC introduced ({...}) to convert back a statement into an expression. Anyway, to define this, you have to simply list all the things: statements are 'return', '{...}', 'while() {... }', etc., while expressions are '5', 'f(...)', etc. That's it, that's the explanation. No intuition, just a comprehensive list taken from the grammar. If course there is some rough idea behind it, which explains why there is a difference, but this is a history class, not an explanation: statements do some action, i.e., this is an imperative concept. Expressions have a value, i.e., this is a functional concept.
- photochemsyn 4y agoI've been reading this on functional JS: https://news.ycombinator.com/item?id=31944352 https://news.ycombinator.com/item?id=31944352 For example, functions that return functions: const filter = (predicate, xs) => xs.filter(predicate) So it's an assignment, leading me to think it's a statement, but then using filter later would be an expression?
- filoeleven 4y agoThat example is particularly confusing because it’s a function definition written as an assignment for IMO no good reason. The outcome is the same either way, but function filter(predicate, xs) { return xs.filter(predicate) } lets you know immediately that “filter” is a function, not a value. There are valid cases where assigning an anonymous function a name via “const” is appropriate, for example when working with higher-order functions, but most of the time it’s not necessary or helpful. I wish it hadn’t caught on. EDIT: I should have read your comment more carefully. The example function is not a HOF though, it runs xs.filter(pred) when called. A HOF would be function addConst(x) ( return (y) => x + y } const add3 = addConst(3) add3(5) // returns 8 The second line can still be written as function add3(x) { return addConst(3) } I think people use the const form because there’s less text to write. Syntax highlighting makes this form more bearable, but I still don’t like it.
- bern4444 4y agoSomething that clicked for me recently in JS and specifically React was why we use {} for interpolation in a component. So an example: const MyComponent = () => { return ( <> <h1>Hello world</h1> { arr.map(val => <ListItem val={val} />) } <h1>Goodbye world</h1> </> ); } Curly braces often denote expressions. They're everywhere: In functions: const x = () => { }; // or function myFunc() { }; In conditionals (while the conditionals are statements their bodies are expressions) if (x > 2) { return true; } In Loops while (x > 2) { doSomething(); } etc. This is obviously part of the language grammar of many languages and makes it obvious why React uses curly braces as well for interpolating values.