Thursday, July 23, 2009

C#er

Was chillin' with the impish abock last weekend when, all of a hullabaloo, he geniused something wonderful.

"Behold!" he cried:

var button = new Button {
    Label = "Push Me",
    Relief = ReliefStyle.None
};
button.Clicked += (o, a) => Console.WriteLine ("ouch!');


To which I replied, "?"

"Watch..." said he:

var button = new Button {
    Label = "Push Me",
    Relief = ReliefStyle.None,
    Clicked +=> Console.WriteLine ("ouch!")
};


"?!" came my response.

"Is not it better?"

"Yes," quoth I, "but gentle abock, this wundercode... it doth not compile!"

"... YET!"

Well friends, yet is over. I am here today to tell you that yes, IT DOTH COMPILE. This is what you get when Scott forgets to pull the git repos for his real projects before a plane flight: unsolicited language features. And there are other goodies:

As with anonymous methods via the delegate keyword, you may omit the parameters to a lambda if you aren't going to use them. This is also helpful when the delegate type has no parameters. For example:

Func<string> myFunc = () => "blarg";


Just look at those parenthesis! Chillin' there all higgledy piggledy. They look like some unseemly ASCII art. But now, presto chango:

Func<string> myFunc => "blarg";


See what I did there? That's called an assignment arrow. It is better. Don't argue with me, because you're wrong.

For my next trick, you can do the same kind of thing with lambdas and event handler registration.

myButton.Clicked +=> Console.WriteLine ("higgledy piggledy");


Because who ever uses the EventHandler arguments? A big, fat nobody, that's who.

Last but not least, you can now do all of this plus regular event handler registration inside of object initializers. abocks around the world rejoice!

There Is No Syntax Without Corner Cases


So there is at least one possible ambiguity with this new syntax:

class Foo {
    public void Add (Action<string> action) { ... }
    public Action<string> Bar { get; set; }
}

// Meanwhile, in some unsuspecting method:
var foo = new Foo {
    Bar => Console.WriteLine ("HELP ME!")
};


Question: Is that an object initialization, or a collection initialization?

Answer: It's ambiguous.

Solution: It's an object initialization. If you want it to be a collection initialization, throw some parenthesis around "Bar." This would be a good candidate for a compiler warning. And if you want to make it an unambiguous object initialization, you could do:

var foo = new Foo {
    Bar = () => Console.WriteLine (
        "What does this ASCII art even mean?")
};


Patch


The patch for all of this is available here. Apply to mcs, recompile, then use gmcs.exe passing -langversion:future.

Future


There has been on-again-off-again talk about adding non-standard language features to the C# compiler under the guard of -langversion:future. The main concern voiced is the ability to maintain such extensions. I will definitely discuss this patch with Marek and co. to see about landing it in mainline. I'll keep you up to date.

Are You Bock Enough?


In the meantime, I call upon manly man Aaron Bockover to make the only manly choice available: fork C# and ship the compiler. Because you're not really a serious media player until you have your own special language.

12 comments:

Ed said...

So this is really two largely separate ideas, right?

Supporting event handlers in property initialization blocks makes perfect sense.

The new => and +=> syntax is more controversial. Correct me if I'm wrong, but it strikes me as a lambda-like replacement for the parameter-ignoring delegate { ... }, which I've also wanted.

I wonder if syntax like (...) => whatever() would be less confusing and more generally useful. A few more keystrokes than you are proposing, obviously.

Unknown said...

Honestly, I think we're all getting distracted by the '=>' assignment operator.

I hope one day to do this in a way that seems strait forward, like:

Bar = { Console.WriteLine(
"What does this ASCII art even mean?") }

Let's look beyond the assumption that the '=>' is necessary or even good.

Amber said...

unsollicited

hmmm

indeed

very nice but a bit on the spur. there is a reason why Hejlsberg c.s. keep fretting on small syntax convolutions, semantic changes. They don't want c# to be the next C++ quite that soon.

R. Lawson said...

I want XML literals in C#. That's the only thing VB9 really has on us.

Mythz said...

man that syntax is ugly and unintuitive. If *it has to* to be any more terse I would adopt Scott's suggestion instead:

Bar = { Console.WriteLine(
"What does this ASCII art even mean?") }

The only thing I would like that's not in c# 4.0 is String Interpolation (http://boo.codehaus.org/String+Interpolation)

Other than that, you really shouldn't be forking from the core language specification at all IMO. Any few bytes of code you save will be dwarfed by the pain you introduce when writing 'mono specific' c# code.

Leave the language design up to the C# design team, they've done a great job so far.

Aaron Bockover said...

Firstly, there is nothing wrong with experimenting with the language.

Anders et. al. have done a fantastic job of balancing language features with skeptical conservatism and also taking in some really great features over the iterations.

C# is a very well crafted, clean, and beautiful language. No one wants to break that.

While I do think more functionality should be allowed in the object initializer syntax (especially event handler support, _maybe_ even method calls on that object), I was merely expressing a syntactical annoyance with the lambda syntax.

I particularly like the { } syntax for anonymous methods in Boo, but I think the => operator is still good for clarity.

+=> maybe not so much - it was just an idea of mine. But since += implies you will be assigning a delegate to an event, then maybe the lack of => at all is acceptable in this case:

x.Foo += Console.WriteLine ("Hi");
x.Foo += { Apples (); Oranges (); };

But for non-event assignment of delegates, this syntax would be confusing and ambiguous (does Apples return a delegate to be assigned to Foo, or is Apples being invoked inside of an anonymous method?):

x.Foo = Apples ();

Proposing to wrap that in { } is more Boo like, solves the ambiguity above, and isn't too bad, but here I think the => works really well (not to mention being more terse):

x.Foo => Apples ();
x.Foo => { Oranges (); Peaches (); };

Generally, I like the => since it says to me "Foo will be going to (e.g. invoking) Apples" whereas a simple = says to me "Apples will be going to foo (e.g. assigning)." => Conveys direction and intention.

Of course, to clarify, none of Scott's changes are actually going into the mainline Mono C# compiler. We're just exploring the space.

Scott was joking about including the experimental compiler and changing all our source code to use these tiny new conveniences.

Mythz said...

Actually you're right:

x.Foo += { Apples (); Oranges (); }

does look better. Although since intList = { 1 } actually means add '1' to the intList collection, I thought the 'add' concept could also be applied when adding to an event handler. But for the sake of readability I think you're right we should always have the += amd -= when dealing with events.

No definitely nothing wrong with the experimenting with the language. I believe that's what MS is doing with their research languages, before they take the best stuff mainstream into c#.

I just worry about lots of individuals extending a language to add a feature which they think is cool. Without any authoritative leadership we could end up with C#++.

Otherwise if you're just exploring new features with a hope that some of them will make it into a subsequent release then please someone send a patch for some String Interpolation goodness ;)

Ed Ropple said...

abock:

So what you're telling us is that you're not manly man enough for such festivities?

Color me disappointed, sir. ;)

zproxy said...

This is awesome :)

Think:
- compiler as a service
- meta programming
- compiler addins.

If you do this right, microsoft might find itself playing catchup with mono at some day. :)

Anonymous said...

"Don't argue with me, because you're wrong."

Oh crap...

Anonymous said...

Assigning Event Handlers in the Object initializer using +=>? Awesome idea! Does it support Arguments for the Events where we DO need the EventArgs? (i.e. all Keyxxx Events)?

Overloading the => Operator and creating ambiguity? meh.

Keith J. Farmer said...

Does it work with interfaces? Perhaps composition of base types?

new (MyBase, IFoo, IBar)
{
MyBaseImpl(string, int) = (s, i) => { ... },
IFoo.FooValue = 1,
IBar.BarValue = 2,
SharedFooBarEvent +=> (s, e) => DoSomething(s, e)
}