Irony, thy name is C#
Intro:
So i was having a conversation with the team about a small change I'd like to make to C#. Right now, C# has the concept of "contextual keywords". I.e. identifiers that can have special meaning when used in special locations, but which are otherwise completely legal identifiers for the user in all other conditions. Good examples of this are 'partial', 'yield', and 'where'. In C# 2.0 "yield" is now used in iterator contexts to indicate that you are yielding a value, or terminating iteration. However, in C# 1.0 "yield" was a perfectly valid identifier. In fact, I used it all the time because i had a type called "class Yield" which indicated the 'yield' of a gramamtical production in a language. So how could we add this "contextual keyword" without breaking code that had been previously written? Simple, we added it in a way such that if you tried to use it with its new meaning you would be writing code that couldn't exist in previous versions of the language. Specifically, to use "yield" you must use it like so:
yield return 4;
yield break;
By forcing you do use "yield" before a "return" or "break" keyword we ensure that we won't be conflicting with any code that you've written in the past (where this would have been illegal).
So this is great. We can add more functionality to the C# language without impacting you negatively.
Now, i was doing some C# coding and i was trying to use a common java idiom that i find very natural. Specifically:
InputStream in = ...;
OutputStream out = ...;
But, lo and behold, i'm not allowed to! Turns out 'in' and 'out' are reserved keywords and i can't use them as variable names. So i was giving it a little thought and i realized: "hey these don't have to be keywords, we could make them contextual keywords!". Why is that the case? Well, let's look at how 'out' is used. 'out' can only be used in three places, the declaration of a method or delegate and the call site of to a method, a-la:
void Foo(out int i) { //method declaration
int num;
Foo(out num); //method call
However, as a user, you are not allowed to write code with arbitrary identifiers preciding an argument to a method, or preceding the type in a method declaration. So we could make 'out' contextual, without screwing up any of your code, and while now allowing it as an identifier name. What would be interesting is that we would now allow this type of code:
void Foo(out out out) { //method declaration
But no one will do that (or else they'll be beaten in the code review). Regardless, such cases exist today with things like "where". You could say:
void Foo<where>() where where : where
and we won't stop you. Just in practice this will never happen. (and, if it did, the IDE colorization would help you out a lot).
A similar story holds for "in", which is reserved solely for the "foreach" construct. I won't delve deeper into this since i think you get the idea.
Story:
So we're having a conversation about things we'd like to see in the next version of the language. I bring this up, mention the benefits and how it would just be a nice thing to do with very low cost. There is agreement, but a lot of wondering "is the benefit so small that we shouldn't even be looking at this". Ok, so we talk about it for like 2 minutes and then move on. At some point we get to another suggestion of mine whereby i think that we should allow a delegate to bind to a property. i.e. you could have the following:
delegate int ProducesInt();
delegate void TakesInt(int i);
class Person {
int Age { get { ... } set { ... } }
void Foo() {
ProducesInt pi = new ProducesInt(this.Age);
TakesInt ti = new TakesInt(this.Age);
Of course, this isn't allowed today because the argument to a delegate must be a method. But this always ends up getting in my way and i have to create nasty workarounds. Since we're the compiler we know what the delegate binds to, and we know about the synthesized methods created for the getter and setter, and so it would be pretty trivial to pass the right method object to the delegate's constructor. In the cases above we would just traslate that to:
ProducesIntpi =newProducesInt(this.get_Age);
TakesIntti =newTakesInt(this.set_Age);
So, we got to talking about it a bit more, and we came back to the old discussion where we get a lot of requests from customers for an easy way to refer to a property's getter or setter easily in the language. Me, thinking i was so brilliant, said: "how about the following idea:
ProducesIntpi=newProducesInt(this.Age.get);
TakesIntti =newTakesInt(this.Age.set);
By using the 'get' or 'set' keyword after referring to a property, we will then get the appropriate method off of it that you can now use. Handy for this situation and others as well!"
And, instantly, someone responded: "because 'get' and 'set' are contextual keywords, and it's perfectly valid for them to be members of a type. You now have an ambiguous situation where you might be referring to the getter of the property, or a field on the object that is returned."
Bam. Right after I'd argued passionately about moving more keywords to be contextual.
I hadn't considered that reserving keywords could actually be useful because they did allow us a lot more flexibility with creating new language features. If we'd reserved 'get' and 'set', then this would be trivially easy to solve, but now we're stuck in a place where doing that is no longer as elegant or easy, and we don't want to bog down the language with more uglyness.
So i backed out of my request for 'in' and 'out' to become contextual. After all, it's not really hurting people that badly, and it does give us more flexibility in the future if we find that those keywords would be perfect for something else. It also meant that i could focus more on getting features that i really want into the language (like covariant return types. have you voted yet???).
Comments
Anonymous
March 20, 2005
There is always code like http://blogs.msdn.com/michkap/archive/2005/02/07/368570.aspx where every single identifier can look like the exact same term -- whether namespace, class, variable, or procedure. :-)Anonymous
March 20, 2005
It would be nice to be able to make delegates accept properties, but only in the few cases where I want it to work like that. It is a nice to have feature, not a must have.Anonymous
March 20, 2005
Good to know that at least one person in Microsoft also thinks that delegates to properties should be allowed. I'd be happy with using "get_Age" and "set_Age" but the C# compiler seems to have some artifical ban on referring to those methods directly in any context.
I don't know why this isn't supported, as it's a perfect type-safe and fast method of data binding. No reflection would be required.
I'd take it a bit farther, and have a PropertyBinder<T> with special compiler support such that doing this:
PropertyBinder<int> ageBinder = person.Age; //creates an instance by calling PropertyBinder<int>.ctor(person.get_Age, person.set_Age)
int age = ageBinder.Value;
ageBinder.Value;
Perhaps take it a step further and have a binder that could bind to multiple objects (since delegates can do this) and enumerate all the bound values, see if all the bound values are equal, set all bound values at once, etc. The "+" operator can be overloaded in the same way it is for delegates.Anonymous
March 20, 2005
The comment has been removedAnonymous
March 20, 2005
This is quite amusing. I just wrote about this same issue in VBScript.
http://blogs.msdn.com/ericlippert/archive/2005/03/16/396903.aspx
In VBScript,
For [i=1]=.For To Step Step Step
is perfectly legal, for similar reasons.Anonymous
March 20, 2005
So how about this:
ProducesInt pi = new ProducesInt(this.Age.out);
TakesInt ti = new TakesInt(this.Age.in);Anonymous
March 20, 2005
Robert: THat's fantastic. I love it.
highly doubt it would happen. since in/out get/set would just confuse people,Anonymous
March 20, 2005
Blog link of the week 11Anonymous
March 20, 2005
I think the syntax you posted at the beginning was fine.
ProducesInt pi = new ProducesInt(this.Age);
TakesInt ti = new TakesInt(this.Age);
.net just chooses the appriate field method based on the delegate signature.
What is wrong with that?Anonymous
March 20, 2005
James: I'm sorry if my point wasn't clear. Yes, my original suggestion still works. The point was to see if we could go with a cleaner approach which would work in all situations. The intuitive solution was shot down for the exact things i was earlier advocating. That was what was funny.Anonymous
March 20, 2005
Why not make using get_Age and set_Age a warning rather than an error in this case? At least then we could put in a #pragma and be on our happy way.
This is just yet ANOTHER reason why I'd like inline IL in C#.Anonymous
March 20, 2005
You can use @in and @out as variable names if you need them badly.Anonymous
March 20, 2005
This is not exactly what you want, but would be a time saver for the intellisense spoiled people like myself..
Scenario 1:
InputStreamTAB
Normally this would produce:
InputStream<either TAB or spaces>
But with TAB completion:
InputStream inputStream
pressing TAB again:
InputStream inputStreamTAB
gives, if InputStream can be instantiated:
InputStream inputStream = new InputStream(
Otherwise you could get a list of derived types or whatever..
WHATS WITH THE "" prefix ???
Well that "" will act as a signal to the developer that this variable name was inferred from the class name and is candidate for renaming!
So if you have a second to answer back in the comments, what you think of this or does VS already have this kind of variable name "generator" ? I know the Paste Parameter Tip but the completion etc stuff could be taken a bit further..Anonymous
March 20, 2005
To make it context keywords why not :
ProducesIntpi=newProducesInt(get this.Age);
TakesIntti =newTakesInt(get this.Age);Anonymous
March 20, 2005
mmm, delegates on properties would be nice to have. Not my number one priority by a long shot, but definately handy to have.Anonymous
March 21, 2005
Andrew, I currently do like that. 2-3 letter abreviations of the classnames. However after a while there can be many and "pi" could both refer to ProducesInt as well as ProcessInvoker etc.. So better would be autogenerated processInvokerA processInvokerB etc..Anonymous
March 22, 2005
I would like delegates to overloaded methods. I don't care if a delegate is implemented as a type and that's why it's not possible. A delegate looks like a method to me, hence it should obey the same rules.
And please don't add more keywords to C#. You are killing the language.Anonymous
March 23, 2005
Thomas: where did you get the idea that we were adding new keywords to the language? No new keywords have been added since c# 1.0
Also, as you can see, my preface was about removing keywords from the language so that you could use them.Anonymous
March 24, 2005
yield is not a new keyword?Anonymous
March 24, 2005
Thomas: No, it isn't. As i explained, it's a "contextual keyword". i.e.
"Right now, C# has the concept of "contextual keywords". I.e. identifiers that can have special meaning when used in special locations, but which are otherwise completely legal identifiers for the user in all other conditions."Anonymous
March 25, 2005
Calling it keyword or contextual keyword is splitting hairs. I see how they are different, but when you use them as keywords, they are keywords. And I think C# has too many of them already.Anonymous
January 20, 2008
PingBack from http://websitescripts.247blogging.info/cyrus-blather-irony-thy-name-is-c/Anonymous
June 08, 2009
PingBack from http://cellulitecreamsite.info/story.php?id=5454