C# 3.0 is still statically typed, honest!

C# 3.0 is still statically typed, honest!

  • Comments 24

Since LINQ was announced I've seen a lot of really positive feedback and a lot of questions and concerns. Keep 'em coming! We need this feedback so that we can both correct misconceptions early and get the design right now.

One of the misconceptions that I've seen a lot over the last few days in forums, blog posts and private emails is a confusion about what the new "type inferencing" feature implies for the type safety of the language. Apparently we have not been sufficiently clear on this point: C# 3.0 will be statically typed, just like C# 1.0 and 2.0. The var declaration style does not introduce dynamic typing or duck typing to C#.

I think the confusion may arise from familiarity with other languages such as JScript. In JScript this is perfectly legal:

var foo = new Blah();
foo = 123;
foo = "hello";

JScript is a dynamically typed language. You can assign any value of any type to a var.

In C# 3.0, the var statement means "look at the type of the thing assigned to the variable, and act as though the variable was declared with that type." In other words, in C# the code above is just a syntactic sugar for

Blah foo = new Blah();
foo = 123;
foo = "hello";

which of course would produce a type error on the second and third lines.

If you take a look at section 26.1 of the C# 3.0 specification you'll see that the var statement has a lot of restrictions on it to ensure that the compiler always has enough information to make the correct type inference. Namely:

  • the declarator must include an initializer, so that we can infer the type of the variable from the type of the initializer
  • the initializer has to be something that we can figure out the type of – not null or a collection initializer

Compare this to JScript .NET, which has a much stronger type inference mechanism. JScript .NET does not require initializers in var statements; the compiler tracks all assignments to the variable and infers the best type. If, say, only strings are assigned to a variable then it will infer the string type. JScript .NET also infers return types of functions by a similar mechanism. But the goal of the JScript .NET type inference mechanism was to increase the performance of legacy dynamically typed code. If we can infer a type and thereby generate faster, smaller code, we do so.  If not, we don't.

Then why introduce this syntactic sugar in C# 3.0? C# doesn't have a body of legacy dynamic code like JScript and already generates efficient code.

There are two reasons, one which exists today, one which will crop up in 3.0.

The first reason is that this code is incredibly ugly because of all the redundancy:

Dictionary<string, List<int>> mylists = new Dictionary<string, List<int>>();

And that's a simple example – I've written worse. Any time you're forced to type exactly the same thing twice, that's a redundancy that we can remove. Much nicer to write

var mylists = new Dictionary<string, List<int>>();

and let the compiler figure out what the type is based on the assignment.

Second, C# 3.0 introduces anonymous types. Since anonymous types by definition have no names, you need to be able to infer the type of the variable from the initializing expression if its type is anonymous.

We'll discuss the reasoning behind anonymous types in another post.

  • It's exactly what I wanted when I started with Java. It _is_ silly to repeat all that stuff for a simple assignment statement. This is much better; explicitness when you want it, and the compiler does all the work when you don't.

    The number of concerns over "dynamicicity"(?) should be a good indicator that "var" isn't a good keyword. I myself would prefer a whole word (although short, of course), preferably a verb. "Set" would be good, but syntatically carries the same kind of baggage as "dim" and "var", and that doesn't strike me as helpful. "Make" or "help" seem like good places to start, but something less code-like would be niftier. "Strike", "Issue", or "Coin" seem like the kind of direction I'd like to see. Hmmm...

    coin mylists = new Dictionary<string, List<int>>();

    Looks okay. I don't know as I'd immediately rule out the use of prepositions without _some_ thought, either. After all...

    coin mylist as a new Dictionary of string against List of int

    Naw... It'd look terrible on a Burger King cup.

    And as for abuse, it's possible to write everything as statics, and make something completely non-OOP. Abuse? Option? Just thinking about it makes me want to engage in an involuntary personal protein spill.
  • Eric, good explanation why type inference is a good thing. I agree with the previous comment that "infer" would be better a better keyword than "var", but it doesn't matter very much.
  • A couple things I would like to see. Currently anonymous Delegates like the List<>.ForEach the statement is:

    myList.ForEach(delegate(int x)
    {
    //dosomething
    });

    this would be much clearer if we had something like this... myList.ForEach { |x| doSomething };


    Secondly... Duck Typing does make a better generic code. MS needs to catch up on this or face another Java Problem when dealing with languages like ruby and python.

    My third comment, Dynamic Types, adding functions at runtime. As a developer I WANT to do this. why? Look at Ruby's ActiveRecord and then tell me its not neeeded.

    Sincerly,

    Paul

  • Slightly off topic, but as you still refer to JScript.Net - is it still a viable language for Microsoft in the near future?

    Personally I love the language for coding both sides of the client/server divide. I just have more options on the server side (an get to type my variables, build classes etc), but still retain the same overall syntax.

    Also, coding on a Mac doesn't offer the Visual Studio route!
  • I'm really not happy with this 'var' stuff.

    Just give the type that it is.

    Is this one of these 'gee we better make this work for the scripters' move?

  • No, I explained the two reasons why we are adding type-inferred declarations in the post: to make redundant declarations shorter and to enable anonymous types. Making the language easy for scripters to use is not a design goal of C#.

    "Just give the type that it is." doesn't help if the type doesn't have a name.  Now, we could force all tuple types to be declared, but then you potentially have to declare a new boilerplate type for every query result.

    Look at it this way.  You have a query:

    AgeAndPhoneNumber results = from c in customers where c.city=="London" select new {c.Age, c.PhoneNumber};

    and now you want to add address to that.  Do you _really_ want to have to define a new type AgeAndPhoneNumberAndAddress?  Or do you just want to say

    var results = from c in customers where c.city=="London" select new {c.Age, c.PhoneNumber, c.Address};

    and let the compiler take care of the typing for you?

    That's the consequence of forcing users to declare types -- they have to declare types, and that's a pain.
  • I'd suggest borrowing a page from Nemerle on this one: use 'def' to mean 'readonly var', and sidestep the squick factor of reassigning something that isn't manifestly typed.
  • We interrupt the discussion of how the difference between lambda expression and anonymous method convertibility

  • @Eric Lippert: Why can't you have a way to write the tuple type explicitly, like Haskell's "(Int, [String])" and so on?

Page 2 of 2 (24 items) 12