7

I am contemplating writing a programming language. Most grammars define expressions as being a kind of a statement. But really I cannot come up with a single example of any useful expression that would pass as a statement.

See https://stackoverflow.com/questions/19132/expression-versus-statement for an easy definition of expression vs statement.

EDIT: Why are so many grammars making the distinction between expr and stmt when as noted in the answers, it makes no sense.

Carlo V. Dango
  • 209
  • 1
  • 10
  • 5
    What's wrong with a hobby project?? It already generates MSIL code for a hello world.. so I'm well on my way :-) – Carlo V. Dango Aug 12 '12 at 17:24
  • 5
    @JimG. How the heck does this warrant a downvote? To remind you, the button says "This question does not show any research effort; it is unclear and not useful". Also, writing programming languages is *awesome*, even if they never see any real-world adaption and suck. –  Aug 12 '12 at 17:29
  • 1
    There's nothing wrong with a hobby project; it's just that people who have a fighting chance of building a viable programming language tend to have more focused questions. // In any case, I highly recommend that you look at Eric Lippert's answer for general advice. Good luck! http://programmers.stackexchange.com/questions/84278/how-do-i-create-my-own-programming-language-and-a-compiler-for-it/84361#84361 – Jim G. Aug 12 '12 at 17:30
  • 1
    @JimG. Well, he already learned something by asking this question, and will (hopefully) learn even more along the way even if the end result is not a "viable programming language". Isn't that good enough? –  Aug 12 '12 at 17:35
  • @delnan: See my comments in chat. – Jim G. Aug 12 '12 at 18:04
  • @CarloV.Dango: *It already generates MSIL...*: Impressive. You've convinced me that this is a legitimate question. – Jim G. Aug 12 '12 at 18:05
  • @JimG Yes, it's very easy using the Reflection.Emit API in C# combined with using ANTLR for lexing and parsing most of the work is done for me.. – Carlo V. Dango Aug 12 '12 at 18:10
  • @CarloV.Dango: Yep. I was wrong. Please see my comments in chat. – Jim G. Aug 12 '12 at 18:14
  • define "useful" – gnat Aug 12 '12 at 18:22
  • 2
    @CarloV.Dango: I just changed my downvote to an upvote. – Jim G. Aug 12 '12 at 18:26
  • 2
    Kudos to Jim G. for changing his mind instead of digging his heels in. – Stuart Marks Aug 12 '12 at 20:14

4 Answers4

3

A function/method call is, of course, an expression. At the same time (in imperative programming languages, which I assume since you seem to take statements for granted) many functions have side effects and those side effects are often enough incentive to call the function. Consider functions like printf which exist primarily for their side effect and whose return value (in this case, number of chars printed) is usually not interesting. Or basically any function/method with no interesting return value -- i.e. void or () AKA unit. (While you could, in a statically-typed setting, differentiate between the two, doing so is entirely pointless.)

In languages with operator overloading, this extends to any expressions involving operators (read: almost all expressions), because these are basically functions as above, only called differently. Sometimes a bad idea, yes, but then there's embedded DSLs.

Also, if you make assignment an expression (bad idea IMHO, but rather common), you practically need it to also be valid as statement. Ditto for pre- and post-increment and -decrement -- these are expressions, yet mostly useful as standalone statements.

EDIT: As for why the distinction exists: Statements are distinct from expression so the grammar can prevent statements from being used as expressions. An example in Python: The "expression"

f(def g():
    pass
)

has multiple problems (primarily: does not work with the - otherwise great and simple - way indentation is handled; can be easily turned into a inner function definition) which make it utterly useless. For comparision, def g(): pass; f(g) is perfectly valid and sometimes (if f wants a callback and you want a no-op for that) useful. And not all languages accept every expression as statement -- many either rule out alternatives which never make sense (cf. Pascal, C#), or give a warning ("statement has no effect").

  • Very well put! Why is assignment a bad idea? eg. using(var conn = new DBConnection(..)) a bad idea? – Carlo V. Dango Aug 12 '12 at 17:28
  • 1
    @CarloV.Dango That example isn't a bad idea, but that's because it's not an assignment, it's a declaration (coupled with initialization). A bad idea is `while (a = b)` (typo'd `a == b`, bad because it is wrong) and almost any case where you use `a = b` as part of a larger expression, because it does not properly shout "side effect". A *possible* exception (some people like it, some don't) us chained assignment like `a = b = c`. –  Aug 12 '12 at 17:33
  • 1
    How about int c; while( (c=readChar()) != -1) { ... } – Carlo V. Dango Aug 12 '12 at 17:37
  • 1
    @CarloV.Dango Terribly unclear in my opinion, and only saves you very little. I use either `while (true) { c = readChar(); if (c == -1) break; ... }` or `c = readChar(); while (c != -1) { ...; c = readChar(); }`. –  Aug 12 '12 at 17:42
  • I am not sure what to think. First time I saw the construct I hated it. In the simple context of this 1-line program it looks like a sweet pearl :-) do you have a website where your language design opinions are posted? – Carlo V. Dango Aug 12 '12 at 17:47
  • No, gotta keep them secret so I can implement a perfect killer language later ;) But I think Python is pretty sweet as mainstream language design goes, so if you care about my opinion, look at what they do. –  Aug 12 '12 at 17:52
3

"What useful expressiveness will be impossible in a language where an expression is not a statement?"

None.

The definitions of "expression" and "statement" are largely arbitrary. Different languages define them differently. I know of no programming language whose way of defining these terms makes any particular construct impossible to express. In some languages, some constructs may be a bit more awkward; in others, it may be easier to write code that's difficult to read because a single statement depends on multiple side effects.

Almost all programming languages are Turing-complete and are able to express the same behaviors as any other Turing-complete languages.

Two good examples are Pascal and C.

In Pascal, an assignment is a statement, not an expression. A subroutine can be either a procedure (which doesn't return a value) or a function (which does). A procedure call is a statement, and cannot be part of an expression. A function call is an expression, and cannot appear in a statement unless you explicitly do something with the result, such as assigning it to a variable. Statements can contain expressions, but expressions cannot contain statements.

In C, an assignment is an expression. All subroutines are functions; functions that return no value are declared with a return type of void. A function call is an expression; it can be made into a statement by adding a semicolon. The result of a function call, or of any expression, can be silently discarded. Any expression can be turned into a statement by adding a semicolon. As in Pascal, statements can contain expressions, but expressions cannot contain statements (but gcc provides statement expressions as a language extension).

Here's a typical C construct that depends on the ability to treat assignments as expressions (item is some arbitrary type, and last_item is a constant of that type):

item x;
while ((x = get_next_item()) != last_item) {
    do_something_with(x);
}

In Pascal, you can implement exactly the same behavior like this (if I remember the syntax correctly):

var
    x: item;
    done: boolean = false;
begin
    while not done do
    begin
        x := Next_Item;
        if x = last_item then
            done := true
        else
            do_something_with(x);
    end;
end;
Keith Thompson
  • 6,402
  • 2
  • 29
  • 35
  • How would you do `a = b` in C without using (C's definition of) an expression as statement? –  Aug 13 '12 at 06:47
  • @delnan: If C didn't have expression statements, surely it would have an assignment *statement*. But to answer your question, `if (a = b);` isn't an expression statement. – Keith Thompson Aug 13 '12 at 07:48
  • Yeah, you'd have to sacrifice expression statements for that (though I'd be a hypocrite to cling onto them, I'm sure many people lean towards finding them useful). Sure, you can define assignment and function calls to also be statements, without pulling in the rest of all expressions. But allowing a *subset* of expressions as statements should already qualify for "an expression is a statement" IMHO, considering that some modern languages do this (and still talk of "expression statements"), and others allow it but give a warning. –  Aug 13 '12 at 07:57
1

If you refer to the definitions that are provided on those pages, its easy to see why its difficult to come up with an expression that would pass as a statement.

Expression: Something which evaluates to a value. Example: 1+2/x

Statement: A line of code which does something. Example: GOTO 100

The expression is only the subject of the statement. If you're not assigning it to something in the end, then there's not a whole lot you can do with it.

You might want to look into lisp though.

chemisus
  • 11
  • 1
  • I don't want to look at LISP anymore :-) I want static typing a light weight syntax. I want to rid excessive () and ; – Carlo V. Dango Aug 12 '12 at 17:26
  • As you should know (since you refer to Lisp), evaluating to *something* does not imply not doing anything, and vice versa. Things like `f()` and `a = b` evaluate to values in many, many languages. In many functional languages, even `launchTheMissles` evaluates to a value (namely, the empty tuple). –  Aug 12 '12 at 17:40
  • @delnan yes, and in Smalltalk there are no void methods, they return 'this' instead. I've searched my soul and prefer void. I have to read up on F# options though before making any decitions in stone. – Carlo V. Dango Aug 12 '12 at 18:13
0

Things like the traditional send call for sockets is a good example:

int send(int socket, char* buffer, int flags);

The 'bytes sent' return isn't strictly vital for the function to do its business.

You could of course say that not checking the bytes sent is an error and that your language should not allow that as input. The issue comes that parsing is done before the types are ever known. When parsing

send( socket, buffer, 0 );

your compiler has no idea if send returns a value or not. In most languages, that simple standalone expression is desirable to be allowed as a statement.

Telastyn
  • 108,850
  • 29
  • 239
  • 365
  • The compiler does know the return type of `send` when it's performing semantic analysis. If the language disallowed using a value-returning function call as a statement, the compiler could easily diagnose the error at that point. The fact that the return type isn't known during parsing is not an obstacle. In Pascal, for example, `send` could be either a function or a procedure; the latter would be legal in this context, but the former would not. – Keith Thompson Aug 12 '12 at 19:49
  • @KeithThompson - the OP asked specifically about grammars of existing languages, which mean lexing or parsing. This could certainly be done at the semantic analysis step, but would not change the grammar to not accept expressions as statements. – Telastyn Aug 12 '12 at 20:35