Delen via


Compound Assignment, Part Two

Last time I discussed how the compound assignment operators of the form “x op= y” have some perhaps unobvious behaviours in C#, namely:

(1) though logically this is expanded as “x = x op y”, x is only evaluated once
(2) for built-in operators, if necessary, a cast is inserted, so that this is analyzed as “x = (T)(x op y)”
(3) for built-in operators, if “x = y” would be illegal then so is “x op= y”

I am pleased to announce that we are at long last filling in some holes in the C# language. We at present support compound assignment on the addition, subtraction, division, remainder, multiplication, left shift, right shift, bitwise and, bitwise or and bitwise xor operators, but that leaves out a large number of operators. In the next version of C# we will be adding compound assignment operators for most of the remaining binary (and ternary!) operators.

Doing so requires us to relax some of the rules above; I'll describe why below.

Let's start with an easy one.

Equality/Inequality

In the next version of C#, x !== y will mean x = (x != y).

Now, for all the built-in types, x != y returns bool, so on the built-in types this syntax only works if x and y are both bool. This was actually the easiest of the new compound operators to implement because it already was implemented! x !== y on bools is the same as x ^= y, which we already had.

Similarly, x === y is the opposite; it is the same as x = !(x^y). Or is it the same as x = (!x)^y? Turns out, both! Is that ever weird!

I am slightly worried that people conversant with JScript will confuse the meaning of === in JScript (equality without type conversion) and === in C# (compound assignment with equality) but I hope it will not be too confusing.

The compound equality and inequality operators are trivial; what about the other comparison operators? Can we make them into compound operators?

Comparison

Were there any justice in this world, x <= y ought to mean x = x < y, but unfortunately <= is already taken. Similarly with x >= y. Regrettably we do not have a compound assignment syntax for these, but it's probably just as well, as you'll see in the next point:

x >== y and x <== y mean x = x >= y and x = x <= y, respectively. Now, for all the built-in types upon which they are defined, <= and >= return bool, but there are no <= or >= operators defined on bool (or object) and therefore, the compound operators are useful on no built-in types. However, we will support them. If you create a user-defined type, call it C, such that C has a user-defined explicit or implicit conversion from bool, and a user-defined <= operator, then you can say x <== y for expressions x and y of type C. It expands as x = (x <= y), of course, modulo that x is computed only once.

Conditional

One more before we leave the "bool" trap. An exciting opportunity here is to extend the compound assignment operators from binary operators to ternary operators. Now, C# only has one ternary operator, the conditional operator, but in general we can treat any "infix" ternary operator as two binary operators. That is, rather than thinking of x ? y : z as a ternary operator, think of it as two binary operators, ? and :, which always appear together. Once you think of it like that then you can see how we can make this into a compound assignment operator:

x ?= y : z means x = x ? y : z

Clearly they must all be bools or types convertible to bool. In addition, we strengthen the requirement of the third rule: for built-in types both y and z must be assignable to the type of x.

A frequent question on StackOverflow is why the conditional operator does not take into account the type to which it is being converted; in this syntax, it does because again, a cast is inserted if necessary if the types are built-in types. Hopefully that will decrease user confusion at least slightly.

Coalescing

x ??= y means x = x ?? y

This one is actually pretty straightforward and very useful. The type analysis of ?? is a bit odd (see the spec for details) but the semantics of the operator already require that the left and right sides have type compatibility, at least modulo nullability. The operator basically means "if the left side is null, replace it with the right side, otherwise keep it the same and use its value".

Thus far we've seen the easy ones. In the remaining operators we completely remove the restriction that "y" be assignable to "x"; the reasons will become clear.

Type comparison

x is= Y means x = x is Y

again, since "is" only returns bool, the only types this works on are bool and object. For example:

object x = "hello";
x is= Exception;

means x = (x is Exception), so x becomes a boxed "false". Similarly:

x as= Y means x = x as Y

This one essentially keeps x the same if it is of type Y, and turns it to null if it is not.

We relax the restriction that x = Y be legal for built-in operators in both these cases, since it practically never will be.

Call, index and member access

x ()= y means x = x(y)

This one takes some thinking. Clearly x must be of delegate type so that it can be invoked. And the delegate invoked must return a delegate assignable to x. I did an article on delegates like that a while back; essentially this operator works best on combinators:

delegate D D(D d); // D is a delegate which takes a D and returns a D.
void M()
{
D x = q=>q;
D y = r=>s=>r(s);
x ()= y;
// means x = x(y), which in this particular case, just assigns y to x.
}

Clearly in this example it is very useful that y be assignable to x, but it is not required.

Now, you might say, doesn't this already have a meaning? That is, x() = y means assign the value of y to the variable x(). But in C#, the result of a method invocation is never a variable, it is always a value (or void).

This is not true in general in the CLR; as I noted last time, the type system supports methods that return an alias to a variable. If we ever want to add variable-returning methods to C#, this is going to be tricky. We considered not adding this compound assignment operator because it might make it more difficult to add the variable-returning-method feature in the future. But a great feature today is worth it; we might never implement the variable-returning-method feature and it seems a shame to deprive people of this awesome syntax as a result of a hypothetical future feature.

Similarly to invocation, we can do indexing. x []= y means x = x[y]. The typical case is that x is a type that contains an indexer which returns an instance of that type.

A slightly odd one that was controversial in the design meetings was member access. x .= Y means x = x.Y -- clearly Y must be a field or property of a type compatible with x, or a method group such that x is a delegate type compatible with it. (That would restrict Y to be names of methods of a delegate type, like Invoke.)

Stuff we're not supporting

That leaves the new, anonymous method and lambda operators. After much debate we decided to not support x new= Y, x delegate={y} or my personal favourite, x=>=y. Note that the latter would mean x = x=>y, which violates the rule that the same simple name not have two meanings in the same block.

We hope you enjoy these new operators; I'm hoping we can do a second preview release of the compiler soon so that you can experiment with how these operators interact with async/await!

.

.

.

.

 

UPDATE: HA HA HA HA HA HA HA HA HA HA HA HA HA HA HA! I totally crack myself up.

In case it is not clear -- and based on the number of comments I got of the form "Are you serious or is this an April Fools Day joke? " it was exactly the right amount of unclear -- this is a joke; we are not adding any of these operators. Part One is of course perfectly serious

One of the most common comments is that "??=" actually is useful. Indeed, it would be pretty useful, though we have no plans to do it at this time. It was a comment suggesting ??= on last year's April Fool's Day post that inspired this one.

Comments

  • Anonymous
    March 31, 2011
    I'm not really seeing (m)any practical uses for any of this. Do you have some examples of how this might be used in practice?

  • Anonymous
    March 31, 2011
    Is it a coincidence that this is posted on the 1st of April? HA HA HA HA HA HA HA HA HA HA HA HA HA HA HA HA HA HA! I crack myself up! -- Eric

  • Anonymous
    March 31, 2011
    :)

  • Anonymous
    March 31, 2011
    Would it be possible to overload these operators for my class or do they fall into the group of non-overloadable operators?

  • Anonymous
    March 31, 2011
    Is this an April's Fool joke?

  • Anonymous
    March 31, 2011
    That's a joke... right?

  • Anonymous
    March 31, 2011
    I hope this is a hoax, because most of them are even less useful than that famous "goes to" (-->) operator.

  • Anonymous
    March 31, 2011
    April's fool ! That was pretty obvious after reading the section about the first new operator...

  • Anonymous
    March 31, 2011
    SSS the main purpose of these operators will be to separate the wheat from the chaf during the interview process

  • Anonymous
    March 31, 2011
    Finally, the humble bool type gets some long-deserved love. While you are at it, I hope you also insert the long missing short-circuited version of the xor operator. I understand it cannot really provide short-circuiting, but at least it would make our code look more cheerful. ^^

  • Anonymous
    March 31, 2011
    It is not a joke!!! Rather I'd suggest some useful extension also. The async-compound operator, for example. We may write as X====Y, where that is the syntactic sugar of X===Y evaluated asynchronously. To be honest, there would be the parallel-async-compound too. By writing C1=====C2, we may avoid tons of code, instead of evaluating asynchronously, in parallel mode, the equality of enumerable objects. That will open the door to C# 6, where an huge application, will be written as: X=============================================Y  //to install plugins just add more = Amazing!... Eric you're great!

  • Anonymous
    March 31, 2011
    Eric, I am deeply interested in your work, especially your efforts geared towards simplifying the C# language. Could you also consider, along with the compoind assignment rewrite, adding the missing short-circuit conditional expression operator  ^^ (XOR) ? C# already defines the short-circuit && and || operators, it's a shame XOR has no love. Best Regards, Florian

  • Anonymous
    March 31, 2011
    @Mario: Great minds think alike... I would subscribe to your newsletter.

  • Anonymous
    March 31, 2011
    AprilFool ?= RealThing

  • Anonymous
    March 31, 2011
    Shoudln't the compound operators also have a compound operator? x+==y, which would be the same as x=(x+=y), or x=(x=(x+y)) ? Then we'd also need x+===y.

  • Anonymous
    March 31, 2011
    I will be deeply upset if this fantastic development is merely a "joke".

  • Anonymous
    March 31, 2011
    Actually there is one operator in there that I'd actually like to see in a future C# version:  ??=

  • Anonymous
    March 31, 2011
    Well played.

  • Anonymous
    March 31, 2011
    Hm, April's fool or not? There seems to be quite some thought put into this. Yet apart from a few useful ones (especially ?= and ??=) I don'r really see the point yet, but the last sentence about "how these operators interact with async/await" just doesn't seem to fit the April joke part. Anyways, will it be possible to invoke methods and indexers with multiple arguments? x ()= y, z; Wow, how confusing is that?

  • Anonymous
    March 31, 2011
    The comment has been removed

  • Anonymous
    March 31, 2011
    @Lucero: Putting lots of thought into something does not mean it can't be an April Fools joke. There are lots of April Fools RFCs that would prove that, many of which have some degree of effort put into them: en.wikipedia.org/.../April_Fools&

  • Anonymous
    March 31, 2011
    @Quick Joe Smith: Useful as that might be, it won't happen as it would slow down array allocation, as the provision of a default struct constructor would require that the default constructor be invoked on each element within an array, which would (obviously) slow things down. If the default constructor wasn't invoked in arrays, that would be a really weird language "wart." This would also require CLR support, and I doubt that the CLR folks would want to add such support.

  • Anonymous
    March 31, 2011
    The comment has been removed

  • Anonymous
    March 31, 2011
    This really looks like an april fools day joke. I agree it would be nice to have the "??=" operator, specially if that expanded to something like: lock (something) {    x = x ?? y; } But also "??=" could be used in expressions, so that would allow us to write thing like "(x ??= y).DoSomething()". Wait a second, think I'm seeing the Singleton Pattern over here.

  • Anonymous
    March 31, 2011
    Once again a great april fools joke, well done :)

  • Anonymous
    March 31, 2011
    Mr Lippert, you are a card. Thanks for the entertainment! :D

  • Anonymous
    March 31, 2011
    Jaysus Eric you've far to much time on your hands mate! I was half way through last years "-->" & "++>" operators before I saw them for what they were, I'm quite proud I spotted this one immediately. . . . also, having been pranked twice already today I was ACUTELY aware it was April 1st!

  • Anonymous
    March 31, 2011
    It's fabulous that you went to the trouble of a (genuinely useful) "part 1" blog post as the prelude to an April fool post. (I've just tried to find a "C# starts looking like Yoda" April fool post from the past, but maybe that wasn't you...) On the other hand, I'm really hoping that the "second preview release of the new compiler" isn't a joke. It'd be really nice to be able to use async on VS2010 SP1 soon... I don't suppose you can give us any indications on that front?

  • Anonymous
    March 31, 2011
    statements like x !== y look more like typos than usefull constructs. Eric waited until 1.00 a.m on April 1st, so I hope that's joke for C#'s sake.

  • Anonymous
    March 31, 2011
    Eric, didn't you pull this gag last year on April 1st?

  • Anonymous
    March 31, 2011
    I was at the part about === before I realized what day it is. Good stuff.

  • Anonymous
    March 31, 2011
    @Fede, I disagree. ??= shouldn't have an inherent lock, any more than += should. But you can already do the locking version of ??= using the Lazy<T> type. There's a constructor parameter that lets you specify whether the Lazy should be threadsafe. But I, too, dearly wish that the ??= part was not an April Fools joke. I want to be able to lazy-initialize something without changing the semantics of every call site. Obviously you can write out "x = x ?? y", but it pains me not to have the same concise syntax as += and friends.

  • Anonymous
    March 31, 2011
    A colleague has pointed out one operator this doesn't work well with: assignment itself.  int x = 10;  Console.WriteLine(x == 10); Does that mean "compare x with 10 and call Console.WriteLine(bool)" or does it expand to:  int x = 10;  Console.WriteLine(x = x = 10);

  • Anonymous
    March 31, 2011
    What a bunch of nonsense :D

  • Anonymous
    March 31, 2011
    Wha't no LINQ love? Maybe C# version N+2 can finally have "x where= z =>condition(z)".

  • Anonymous
    March 31, 2011
    The comment has been removed

  • Anonymous
    March 31, 2011
    You got me for a bit; good stuff! The ??= and as= operators seem like they could actually prove useful. Love the //= operator suggestion from the commenter above! How about the ;= operator? :)

  • Anonymous
    March 31, 2011
    Yaaaa!!!...That's an idea!...The //= operator!...Why not the x /*=y and the x */= y also? I guess that it will cost an extra effort for the C# guys.

  • Anonymous
    April 01, 2011
    The "Compound Assignment, Part One" post really tricked me... I always, ALWAYS forget to avoid blogs on April 1st :-)

  • Anonymous
    April 01, 2011
    Wow... I was with you all the way down to "x ()= y means x = x(y)"  nodding and saying "not that useful, but interesting"... Looking back I should have known as soon as you released any details about .NET 5 :-)

  • Anonymous
    April 01, 2011
    Another vote for making ??= real. Love the where= suggestion from Raymond too.

  • Anonymous
    April 01, 2011
    Aaah I was looking for good jokes all day and finally found one :)

  • Anonymous
    April 01, 2011
    The omission of ->= to accompany .= is worrying. Is unsafe code being deprecated?

  • Anonymous
    April 01, 2011
    As well, it seems that a number of common and useful scenarios are not covered by these handy shortcuts. For example, consider:    x = !(x is y);    x = -x(y);   x = &x.y; I would therefore suggest that the op-assignment operator be made fully composable with arbitrary unary operators, such that the above could be properly rewritten as:   x !is= y;   x -()= y;   x &.= y; respectively.

  • Anonymous
    April 01, 2011
    Excellent addition to C#! I disagree with the meaning of the === operator, though. x === y is just syntactic sugar for x = (x == y), which is clearly just syntactic sugar for x = (x = (x = y))). This might be a breaking change from the previous version of C#, but it is necessary for consistency.

  • Anonymous
    April 01, 2011
    @Jonathan Pryor: Only if a parameterless constructor was defined for a given value type, otherwise array allocation should not be affected. I for one would like that choice. The only other choice, it seems, is to make it a class instead.

  • Anonymous
    April 01, 2011
    Awesome. I'm sending this to my coworkers... on Monday

  • Anonymous
    April 01, 2011
    I actually got to this sentence "We relax the restriction that x = Y be legal for built-in operators in both these cases, since it practically never will be" when it hit me. Eric would never use "practically never" in a compiler discussion, and then I checked the date :)

  • Anonymous
    April 03, 2011
    Haha, nice one. You had me going for a few minutes there.

  • Anonymous
    April 05, 2011
    That wasn't fair; I read this four days late.

  • Anonymous
    April 06, 2011
    here I am again, one year later, still requesting the '??=' operator... Maybe April fools day isn't the best time for feature requests?

  • Anonymous
    April 06, 2011
    Although this was a joke, I think it would be good if C# implemented all of the functions of APL using the same single (often Greek) characters for expressing complex array transforms. This would complete the recent trends on making C# a much more complex language than [IMHO] it needs to be. It seems that the KISS principle has been abandoned, and that Scoitt Meyers' advice from nearly 20 years ago of "Dont MollyCoddle your users. Give them ONE way to accomplish a given function - When there are alternatives that do not have significant and obvious differences, then more time will be spend (when aggregating the developer community as a whole) on figuring out which is "Best", when in reality there is little or no benfit to one method over an other.

  • Anonymous
    April 11, 2011
    The comment has been removed

  • Anonymous
    October 05, 2011
    The comment has been removed