9
$\begingroup$

In many C style languages the , binary operator takes 2 expressions and simply returns the second one:

let a = 12, 42;
console.log(a);
// Output 42

This is quite useful, but many new languages don't have it. So I'm wondering if there is something wrong with it why my language shouldn't have it.

$\endgroup$
2
  • $\begingroup$ That's invalid JS btw, you would need (12, 42) because otherwise you're trying to declare a variable named 42. $\endgroup$
    – naffetS
    Commented May 17, 2023 at 23:53
  • 2
    $\begingroup$ The main disadvantage of the comma operator in C is that it is easily confused with the use of commas to separate arguments in a function call. $\endgroup$
    – Chris Dodd
    Commented Aug 9, 2023 at 21:34

7 Answers 7

16
$\begingroup$

The benefit of the comma operator is that it allows you to include a sub-expression for its side-effects only. In languages without a comma operator, there are often ways around this ─ suppose f is an expression you want to evaluate just for its side-effects, and then g is the expression whose value you actually want:

  • In languages with list or tuple literals, you can write [f, g][1].
  • In languages with logical operators permitting it, you can write (f || true) && g, or (more cryptically) f && g or f || g when f's truthiness is constant.
  • In languages with let-in expressions, you can write let _ = f in g.
  • In most cases, you can define a function like comma(f, g) which just returns g.

So the comma operator does not really add expressiveness to the language, it just allows you to write this kind of expression explicitly instead of using an idiom. On the other hand, this kind of expression is rarely needed in real code written by humans; personally, I only use JavaScript's comma operator in Code Golf challenges or to simulate let-in expressions like (x=f, g) when compiling to JavaScript.

Also, including the comma operator like this may mean less syntax space is available for other things. For example, in Python, (f, g) or f, g are expressions which create a tuple, so they can't also mean something else.

The comma operator would also be almost entirely superfluous in languages where expressions cannot have side-effects, such as pure functional languages ─ the first operand would only matter if it was eagerly evaluated but diverged. (Haskell's seq behaves like this, and is considered harmful by some.)

$\endgroup$
5
  • 2
    $\begingroup$ It's not entirely superfluous in non-strict pure functional languages if it behaves like seq. It would still be a strange design choice, though. $\endgroup$ Commented May 17, 2023 at 16:43
  • $\begingroup$ Regarding using a function comma(f,g), or more precisely, function argument evaluation, in some languages the order of evaluation is not specified, but the comma operator evaluates left to right. $\endgroup$
    – Pablo H
    Commented Aug 9, 2023 at 16:47
  • $\begingroup$ It tends to be used in C when you have places that only allow one expression, like the parts of a for-loop header. It's also frequently used in JS with reduce() along with short-form arrow functions, so you don't have to write a multi-statement function body. $\endgroup$
    – Barmar
    Commented Aug 9, 2023 at 20:45
  • $\begingroup$ "the first operand would only matter if it was eagerly evaluated but diverged." I don't understand what you mean by divergence here. $\endgroup$ Commented Dec 11 at 1:21
  • $\begingroup$ @KarlKnechtel A computation is said to diverge if it does not complete normally; it might complete abruptly (throwing an exception, terminating the program, etc.) or it might fall into an infinite loop. $\endgroup$
    – kaya3
    Commented yesterday
11
$\begingroup$

Readability

The main problem programmers have with the comma operator is this would permit having multiple expressions in a single line, which is generally frowned upon.

x = 10;
y = 20;
z = 30;
if (a) {
    b();
}

Vs:

if (x = 10, y = 20, z = 30, a) {
    b();
}

Better alternatives

If a language would add the ability to perform expressions 'inline' in other expressions with the comma operator, they could just have statement expressions which perform the same task but are more powerful, as they can include statements instead of just expressions. Example:

int x = ({
    int y = 8;
    if (z) {
        f();
        y += 10;
    }
    y;
});
$\endgroup$
2
  • 1
    $\begingroup$ Are you suggesting that your "Better alternative" is more readable than the version with the comma operator? $\endgroup$
    – Barmar
    Commented Aug 9, 2023 at 20:47
  • $\begingroup$ @Barmar Not really. I am saying that it's more capable, which is objectively the case, because GCC-style blocks can handle statements such as for and variable declarations but comma operators can only handle expressions. $\endgroup$
    – CPlus
    Commented Aug 10, 2023 at 1:16
8
$\begingroup$

In C and similar languages, , is used to separate expressions, while ; is used to separate statements. In these languages, expressions and statements are different things, so it makes sense to have different separators for them.

In many new languages, e.g. Rust, almost everything is an expression. So you can use a single separator (usually ;) to separate everything. 12, 42 in C is equivalent to { 12; 42 } in Rust.

$\endgroup$
1
  • $\begingroup$ This is usually referred to as ISWIM or ML syntax as they were the first languages to use it, as far as I know. $\endgroup$
    – Chris Dodd
    Commented Aug 9, 2023 at 21:39
8
$\begingroup$

The comma operator is easy to confuse with the syntax for generating lists. Even in the example you gave, one could easily think that a is a sequence of some sort containing 12 and 42, rather than just 42.

In fact, in Python (and probably some other languages), a = 12, 42 does exactly that.

$\endgroup$
1
  • 4
    $\begingroup$ It's also easily confused with the syntax for separating arguments, so you need double parentheses: func((a=12, 42)) $\endgroup$
    – Barmar
    Commented Jul 22, 2023 at 14:13
2
$\begingroup$

It's useful to have operators that will unconditionally evaluate both arguments and then unconditionally yield the left one, or unconditionally yield the right one. The comma-operator syntax used in C may not be optimal for this, however. There are at least three variations on such constructs for which I can see some real uses:

  1. Perform the left, then perform the right, then yield the value of the right operand.

  2. Perform both operations in either order, and then yield the right operand (if the surrounding code needs it, which it wouldn't in most cases).

  3. Perform the left, then perform the right, then yield the value that had been produced by the left operand.

I'm not sure what syntax I'd favor for those operations, but I see no reason for the language to have a nicer syntax for the first than for the others.

$\endgroup$
1
  • $\begingroup$ I concur with the usefulness. To choose which form is better, consider something that is nearly equivalent but much less controversial: the statement separator ;. Ideally you want printf("Hello"); x = 42 to be consistent with printf("Hello"), x = 42. $\endgroup$
    – G. Sliepen
    Commented Dec 1 at 10:56
2
$\begingroup$

I think the only time I've used the comma operator is in for statements:

for (x=1,y=2,z=3;  x<17;  ++x,y*=2,z/=2) { … }

This is at least semi-useful (having all the iteration adjustments together, inside the for).
But other uses seem to be more confusing than useful.

$\endgroup$
1
  • $\begingroup$ Some languages with C-style for loops specially allow the "initialiser" and "step" parts to be comma-separated lists of expressions, when the language itself doesn't have a comma operator for expressions in general. E.g. Java does this. $\endgroup$
    – kaya3
    Commented Aug 10, 2023 at 13:33
0
$\begingroup$

I don't know how much the comma operator is used in practice for this, but surely one of the original intended purposes would have been to assign an intermediary variable to avoid repeating common sub-expressions, or to avoid awkward placement or excessive nesting.

This would have been in-keeping with the design goal of C of having a fairly general mathematical expression facility, and certainly avoiding repetition would have been even more important before optimising compilers.

For example (using an arbitrary formula):

result = m=(x+1)*y, (m*2)+(1/m);

True enough, it's not difficult to spread over more lines as separate statements in this context, but for complicated scientific formulas which should be treated as a unit, it provides a neat solution, particularly on a greenscreen terminal where there wasn't the luxury of vertical space.

In conjunction with the facility it provides for multiple operations to occur (such as multiple assignment) in a context where there can be only a single statement, it's not hard to see what the advantage was at the time.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .