Collection initializer expressions, redux

WARNING: This is a speculative post. Caveat emptor.

When we last left collection initializers, we were discussing default types for collection initializers. Since then we’ve thought further about the feature and are considering changing the design. The problem is that as nice as type inference is, as we started to dig into what that practically meant it started to become more and more difficult to figure out what a particular collection initializer might mean. Because the meaning of the initializer in the original design depends on the context, it’s meaning could change dramatically in different contexts. Of particular difficulty was figuring out just how a collection initializer would participate in generic method type inference (i.e. inferring the type arguments to a generic method)–an important topic given how much generic methods are used in LINQ.

Without going into too much detail, what we’re considering is significantly simplifying the design. Instead of inferring the meaning of a collection initializer from context, a standalone collection initializer will always infer an array type. So {1, 2, 3, 4} will always be a single dimensional array of Integer. If you want to initialize a collection type, you use an initializer similar to object member initializers (the initializer that uses “With”). For example:

Dim x = New List(Of Integer)() From { 1, 2, 3, 4 }

The use of the “From” keyword is provisional. It may be the right keyword, it might not, we’ll have to think further on that one. However, you get the idea. The downside of the new design is that it’s more typing in the situation where you were initializing a collection in a context like argument passing (although in the case of List, you could also say {1, 2, 3, 4}.ToList()) where you weren’t going to say the type at all. The upside is that it removes significant ambiguity–when you see “Foo({1, 2, 3, 4})” you know what you’re passing and in the case of generic methods, you aren’t going to get surprising results. This also makes the feature work more like the way C#’s anonymous arrays and collection initializers work, for those of you who care about that kind of thing.

Comments?

6 thoughts on “Collection initializer expressions, redux

  1. Elijah

    Paul,

    Doing collection initializers correctly is important. To hell with default type inferencing; I’d be satisfied with solid collection initializers that did not support a default for type inferencing.

    ‘All of this should work:

    ‘VB looks at the left hand side to determine collection type.

    Dim a() As Integer = {1, 2, 3}

    Dim b(,) As Integer = {{1, 2, 3}, {4, 5, 6}}

    Dim c()() As Integer = {{1, 2, 3}, {4, 5}}

    Dim d As List(Of Integer) = {1, 2, 3}

    Dim e As Dictionary(Of String, Integer) = {{"Monday", 1}, {"Tuesday", 2}}

    Class Person

    Public Hobbies As List(Of String)

    Public Sub AddHobbies(ByVal hobbies As List(Of String))

    Me.Hobbies.AddRange(hobbies)

    End Sub

    End Class

    Dim f As New Person With {.Hobbies={"Hang gliding", "Hiking"}}

    ‘VB looks at method signature to determine collection type.

    f.AddHobbies({"Photography"})

    Currently generic method type inferencing doesn’t always work; VB has a nice message stating that explicitly stating the generic types will remove the error. Why not use this technique until you get more time to fine tune inferencing in these scenarios?

    Collection initializers are for RAD and reducing lexical noise. I’d like to see VB’s collection initializers do both well.

    Reply
  2. Kyralessa

    For what it’s worth, I’d go for (if it’s possible) With instead of From. With already works for initialization; in this case it would differ because you’re not passing in member names, just values. So it seems like (not that I know anything of compiler-writing) the context would make it obvious which use was meant.

    Would it be possible to have {1, 2, 3, 4} treated as an IEnumerable(Of Integer)? Because then I could do this:

    Dim x = New List(Of Int32)({1,2,3,4})

    Better yet if, due to type inferencing, I could do this…

    Dim x = New List({1, 2, 3, 4})

    …and have x be a List(Of Integer).

    Reply
  3. Bill McCarthy

    Hi Paul,

    I think this proposed design misses major functionality.

    For example, show the syntax for initializing a Dictionary,

    and/or show the syntax for a List of anonymous types.

    If the design doesn’t accomodate those things, then best have another look at what has been suggested that does accomodate those things.

    Reply
  4. Anthony D. Green

    How valuable will this be to the users; what are the realistic (80% of the time) usage scenarios for this?

    How much effort to develop it (also how much will this detract from implementing other features)?

    How much will this slow down our beloved background compiler?

    The sequence of numbers from 1 to 4 aside, what is this a usage scenario for this feature? Do we really see a lot of code circulating around with people populating whole lists and dictionaries from literals. BTW, without implicit line continuation this feature will only increase clutter for all but the smallest of lists. Wouldn’t it be cheaper to give us array literals and allow us to AddRange them? Arrays are special, they are language intrinsic and are infinitely more likely to be initialized from an immutable set. Collection types or typically initialized somewhere and left there to be populated in a process – clearly you respect the possibility of needing to change the collection later (in size at least) or you would have just used an array.

    I agree with the way your approach is converging on this feature. I really agree with the minimalist spirit used for implicit line continuations – I would rather you do too little and fix the language later than do too much. Perfection is when there is nothing left to take away.

    Reply
  5. Speednet

    I agree with one of Kyralessa’s points:

    Since one of the existing overloaded constructors of List(Of T) is to pass an IEnumerable, why not just use

    Dim x As New List(Of Int32)({1,2,3,4})

    (Note I changed the equals sign ("=") in Kyralessa’s post to "As".)

    I never saw the need for VB to require the awkward syntax of:

    New Int32() {1, 2, 3, 4}

    Why not just use {1, 2, 3, 4}? It certainly couldn’t mean anything OTHER THAN an array of integers.

    If you had an initializer like {1, 2, "3", 4}, it would be an array of Object. And so on.

    Reply
  6. paulvick

    Kyralessa: We can’t use With because of ambiguities caused by the fact that "=" can mean both assignment and equality. There would be some initializers that would be ambiguous between a collection initializer and an object initializer.

    Bill: A dictionary would be:

    Dim x As New Dictionary(Of String, Integer)() From {{"x", 1}, {"y", 2}}

    A list of anonymous types would have to use the List constructor that takes an array.

    Speednet: While that works for List (albeit with a double-initialization) there are other collection types that aren’t always so well designed.

    Reply

Leave a Reply