5 ms·
Is that really some "special behavior"? As far as i understand the semicolon is just an expression separator like in Erlang and Pascal. In contrast, the semicol
by qznc 14y ago
Is that really some "special behavior"? As far as i understand the semicolon is just an expression separator like in Erlang and Pascal. In contrast, the semicolon is a statement terminator in C. C uses the comma for expression separation. Practically everything is an expression in Rust, even blocks, so
foo() => evaluates to the return value of foo
{ foo() } => evaluates to the return value of foo
{ foo(); } => evaluates to nil
The last case is not a special case. The last expression in the block determines the returned value. There is an expression separator in there, so there must be two expressions which are separated by it. The first one is foo() and second one is ... wait for it ... the empty expression. Which value should EmptyExpression have? Of course: nil, which would be called void in C-land.
- WalterBright 14y agoThere was some discussion about doing this in D, and there was some in C++11, too. I argued that the presence or absence of the ; did not visually stand out very well, and so would be a source of confusion and errors. Hence, ; remained as a statement terminator, and the return keyword served to indicate returning an expression. However, the D lambda syntax does not require a ; when the lambda body consists of only a single expression: http://dlang.org/expression.html#Lambda http://dlang.org/expression.html#Lambda and in practice this has turned out to be well liked.
- pcwalton 14y agoIgnoring a potentially wanted return value with ";" is a hazard. I'd like to institute some set of "warn-unused-result" warnings in Rust to combat this.
- pdw 14y agoWouldn't the type system complain that you're returning nil in a function that is not declared/inferred as returning nil?
- dagw 14y agoWhat if foo() could return nil in some cases?
- victorma 14y agoIt can't in Rust.
- danieldk 14y agoThen you need an algebraic data type having nil is one of its constructors, and that function returning that type. AFAIK, Rust doesn't have nil. So, the closest thing is using the option type.
- kibwen 14y agoIn Rust, nil is the name for the unit type (written as empty parentheses, like "()"), used whenever a function doesn't return anything meaningful. You're correct in that instead of null pointers it has the None variant of the Option enum.
- kibwen 14y agoThis is correct for any "normal" (a.k.a. "item level") function, since their signatures must always be explicit. But you're allowed to infer the signatures of closures, which is the only place where this problem could arise. // This function lives at the top level of the module, so its types must be annotated fn foo(bar: int) -> int { // Here's a closure without type annotations: let baz = |qux| { qux + 1 }; // And a closure with type annotations: let baz = |qux: int| -> int { qux + 1 }; // Which means that this would be a compile-time error: let baz = |qux: int| -> int { qux + 1; }; // returning nil, but expected int // But if you expect this to return int, then there are very few cases where // this might not be a compile-time error: let baz = |qux| { qux + 1; }; log(error, baz(bar)); // will print "()", but did you want bar + 1? io::println(fmt!("%?", baz(bar))); // same as previous io::println(fmt!("%d", baz(bar))); // this one *is* a compile-time error // macros ftw baz(bar); // this will also be a compile-time error }
- qznc 14y agoAn understandable point of view. Though I can think of an even more crazy variation. Declare the semicolon a binary operator and allow overloading it. As funny as that sounds, Haskell provides something like this, as do-notation can behave differently depending on the monad it is in.
- ajuc 14y agoYour post made me check it, and you CAN overload "operator," in C++ apparently :) http://en.wikibooks.org/wiki/C%2B%2B_Programming/Operators/Operator_Overloading#Comma_operator http://en.wikibooks.org/wiki/C%2B%2B_Programming/Operators/O... My mind is blown. You can write bottom-up code in C++ :) EDIT: no, you still can't the expressions are evaluated first, before calling "operator,"
- hollerith 14y agoAlthough everything you say is technically true, readers will tend to get the wrong idea unless we add that (1) most users of the do notation choose Haskell's significant white space rather than semicolons and (2) the (rare) Haskell code that does contain semicolons that behave the way you describe probably also contains semicolons (e.g., in Haskell's let statement and case statement) that have nothing to do with monads or the "sequencing" of side effects. In other words, the semicolons of Haskell are only tenuously related to semicolons in languages like C.
- sp332 14y agoIt's odd because people think of semicolons as the end of a statement, not a delimiter between statements.
- adestefan 14y agoWhich is usually wrong.
- wladimir 14y agoThat's not the point. Most programmers are not language lawyers. It is very bug-prone, which could in itself enough to remove the confusion from a language that advertises itself as "safe".
- loup-vaillant 14y agoBug prone? The compiler will inevitably warn you about your error at compile time. No such bug will have any consequence beyond that. This is also not such a big problem when a human reads the function. We can always see at a glance the return type of the function. Either it's explicitly declared, or it's a lambda whose usage is readily visible. Semicolon or not, you can easily guess if the function is supposed to return its last expression, or not. By the way, the compiler could do the same. Knowing that, there probably will be helpful error messages such as "did you forget the last semicolon?", or "should you remove the last semicolon?". The semicolon is really just a small confirmation. That's why they didn't chose a heavier syntax.
- wladimir 14y agoOk, I misunderstood then. I thought it would subtly return no value instead of a value, and you'd only discover this at run time. That would be bug prone. If it simply generates a compilation error, no problem.
- danieldk 14y agoThe thing is that in a safe language, a value always evaluates to a value of the type of the expression[1]. E.g. in Haskell, if a function returns a list: foo :: Int -> [a] It should always evaluate to a list. There is no such thing as a null pointer. The only thing that comes close to a nullable type is an option type such as Maybe: data Maybe a = Just a | Nothing The function bar :: Int -> Maybe [a] can either evaluate to a Just [a] or Nothing. [1] Actually, I lied a bit, since there is bottom: http://www.haskell.org/haskellwiki/Bottom http://www.haskell.org/haskellwiki/Bottom But bottom does not fullfil the same role as, say null pointers.
- comex 14y agoThat's what makes it elegant: it lets you omit the return statement (safely) but otherwise looks exactly the same as C, even though it's semantically different.
- eternalban 14y agoparsimony =/= elegance
- comex 14y agoUh, it depends on taste. There are a few reasons to want it, such as lambdas as WalterBright mentioned, but I like it because when this is done in a language where statements and expressions are unified, ";" can neatly replace "," from C, and comma can be reused for something else. (Unifying statements and expressions has its own benefits, but we can start with avoiding GNU C's hideous "({ foo; bar; })", which evaluates to the value of bar...)