Collection initializer expressions

WARNING: This is a speculative post. Caveat emptor.

Well, I appear to be on a rhythm of about once a month posts, which seems OK for the moment. Moving on to another “future” topic, one of the most annoying things that we cut (at least, from my perspective) from VB 2008 was collection initializers. Collection initializers were a little different than the corresponding C# feature, because our plan was to introduce stand-alone initializers that didn’t have any intrinsic type. Instead the initializers “snap to” a particular type when they’re converted to it, just like lambdas and AddressOf expressions. For example:

' This works since VB 7.0!
Dim a() As Integer = {1, 2, 3, 4}
' This now creates a list.
Dim b() As List(Of Integer) = {1, 2, 3, 4}
' This assigns a new list to b.
b = {5, 6, 7, 8}

' If M takes Integer(), that's what it'll get. If it takes List(Of Integer), that's what it'll get.
M({1, 2, 3, 4, 5})

This is kind of nice, in that you can initialize collection types and arrays without having to state a type at all. We do the same trick C# does about looking for an Add method on a object that implements IEnumerable, so you can also use this syntax to initialize dictionaries:

Dim d As Dictionary(Of Integer, String) = {{1, "One"}, {2, "Two"}, {3, "Three"}}

All this is pretty straightforward, but there is some discussion about what type you should get if there is an absence of a target type. For example, assuming that local type inference is on, what should the following be typed as?

Dim x = {1, 2, 3, 4}

Our initial thought was that we should just infer the dominant type of the initializer and then create an array of that type. So the above example would type x as Integer(). And then the following:

Dim y = {{1, 2}, {3, 4}, {5, 6}}

Would infer a type of Integer(,), a two-dimensional array of Integer. With more nesting, you’d get more dimensional arrays. However, as we talked about it more, it seemed like maybe it might be more useful to infer List(Of T) for the 1-dimensional initalizer, Dictionary(Of K, V) for the 2-dimensional initializer, and nothing for more dimensions. So the type of x would instead be List(Of Integer) and the type of y would be Dictionary(Of Integer, Integer).

Both seem reasonable, so I’m curious what people would think. Remember, we will only fill in a type if we’re in a context (like type inference) where there’s no target type to use. We’ll always use a target type if there is one.

24 thoughts on “Collection initializer expressions”

  1. My gut reaction is a list like {a, b, c} is likely to be a fixed list, so an array makes sence. It extends beyond 2 dimensions, unlike List and Dictionary. And in my fairy-tale land, throwing a .ToList() on the end could be caught be the compiler to do what I want with additional overhead.

  2. I’m really not sure to be honest. I like the idea of something being inferred.

    But I can see reasaons to pick either way when it comes to List vs Array.

    Personally I only use arrays when I have to. For me their use infers the desire for performance (although I have no evidence to back that up) where as a List speaks to the desire for flexibility.

    I hate to sound all C# here (as I develop in VB.Net not C#) but could we have some kind of symbolic suggestion of List vs Array?

    Ok Damn… I’ve just read the 1st comment again…. Yeah do the Array thing by default. You could intelligently pick up on the ToList specifier and make a list instead.

    or would the JIT compiler do that anyway?

  3. Well I hardly ever have any use for two dimensional arrays, especially not ones that are loaded from an initializer expression such as suggested. In those cases I would be quite happy to write Dim items(,) As Int32 etc.

    What I would prefer, and what I do often use is Dictionary(Of TKey, TValue).

    For single dimension, arrays feels right, as creating a List is reasoanbly simple, except in the case of anonymous types. Given arrays wold be simpler to declare, especially with anonymous types, I’d suggest:

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

    Dim x() = {1, 2, 3, 4} ‘array of Int32

    So the easiest for me to write, would be if expressions did default to list and especially dictionary. For arrays, if I specify the parenthesis, ( ), then you cna infer the type and make it an array.

  4. I was a big fan of the intrinsic lists (sequences) in COmega/X#. So I’d definitely say make type inferrence use List(Of T), and then build list intrinsic support into the language. eg:

    Dim a as T*

    Dim b as T+

  5. I’m worried about legacy thinking and guru secrets here. If a programmer should know what type a variable is, then I think making it List or Dictionary is pretty much creating a guru secret. Something you need to just know. There is the IDE ways to extract the type, but I hate to lean on these. Also, I think we have a lot of historic thinking that {1,2,3,4} creates an array of integers. Having it create an array of integers sometimes and a list other times sounds disastrous.

    So, I vote for sticking with inferrence to an array. Having an extension to create ToList or ToDictionary would be spectacular becuase it is explicit. If you could short cut that in the compiler, that would be super-dooper-dandy because I explicitly state my intent, and you accomplish my intent with more efficiency.

    Stick with explicitness.

  6. Pingback: Jason Haley
  7. The ambiguity stems from the fact that VB lacks a succinct way of expressing key-value-pairs, or more generally, tuples. I believe this is where work should be invested.

    Actually, the ‘New With’ syntax from VB 8 partially amends this problem. Perhaps this can be taken a little further to improve tuple initialization (and in the same vein, Dictionary initialization etc.).

    Oh and by the way: I think your code should initialize an array (= fixed size), not a list (= dynamic size). In my experience, a pre-initialized field seldom needs to be extended later on, and a dynamically-sized field rarely gets initialized with a series of values known at compile time (exceptions exist).

  8. `Must haves:

    Dim x = {1, 2, 3, 4} `Integer()

    `Should this be an Integer(,) or an Integer()() – how can we distinguish intent? In the current compiler this would only work for rectangular arrays anyway but jagged are used more often and very painful to initialize. Should the type of array be explicit?

    Dim x = {{1, 1}, {1, 2}, {1, 3}}

    `Nice to have but generally unneeded

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

    Dim z = New Dictionary(Of Integer, String)() {{1, “Monday”}, {2, “Tuesday”}}

    Dictionary(Of K, V) implements ICollection(Of KeyValuePair(Of K, V)) so for the dictionary initializer you could generalize the outer braces to use the same Add mechanism used for other Collections and the inner braces to an implied constructor call – this would allow in the case of a List(Of Person) one to write:

    Dim p = New List(Of Person)() {{“John”}, {“Jane”}, {“Sue”}}

    Instead of:

    Dim p = New List(Of Person)() {New Person (“John”), New Person(“Jane”), New Person(“Sue”)}

    I think it’s very important to aim for a point of elegance where the developer can state something with the least amount of information to unambiguously communicate to both the compiler AND future developers their intent, but certainly no less. I feel strongly that in all but the most fundamental of cases (a 1-Dimensional array) that there should be a declaration of type, be it on the variable declaration OR the initializing expression, rather than making grand leaps of inference.

  9. Re: Dim y = New List(Of Integer)() {1, 2, 3, 4}

    This is ambiguous with array initializers today; the compiler will think you’re creating an array of List(Of Integer), and you’ll get an error saying "can’t convert ‘1’ to List(Of Integer)"

  10. Type inference can be a good thing in a few case but I don’t think I would like it with collection initializer.

    If a syntax is ambiguous when you read code then the syntax is bad.

    The little gain don’t worth the trouble IMO.

    At worst just let the IDE fill an explicit declaration.

  11. I’m conflicted! On one hand I like

    Dim x = {1, 2, 3} ‘x is an array of Integer.

    and on the other I like

    Dim d = {{1, "Monday"}, {2, "Tuesday}} ‘d is a dictionary.

    I hardly ever use multidimensional arrays; I use dictionaries often.

    Perhaps type-inferred non-nested collection initializers should be inferred as arrays and single level nesting should be inferred as a dictionary. Levels of nesting beyond this are not inferred. But this doesn’t feel "right."

    Another commenter had a very appealing syntax:

    Dim x = {1, 2, 3} ‘x is a List(of Integer)

    Dim y() = {1, 2, 3} ‘y is an array of Integer.

    To extend this:

    Dim a = {{1, "Monday"}, {2, "Tuesday"}} ‘a is a dictionary.

    Dim b(,) = {{1, 2}, {3, 4}} ‘b is a two-dimensional array of integer.

    This syntax feels "right."

  12. (Continued)

    The above syntax could also allow for jagged arrays:

    Dim j()() = {{1, 2, 3}, {4, 5, 6}} ‘j is an array or arrays of Integer.

    That is much less noisy than the current:

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

  13. Whichever it auto-created, arrays or Lists and Dictionaries, there would be cases where it wouldn’t be what we want.

    I guess what I’d really like is this:

    Dim x = New List(Of Int32)(5, 7, 9, 11)

    Which, of course, doesn’t require any special compiler tricks at all; it just requires a constructor with ParamArray. Why isn’t there such a constructor in List?

  14. Dim a()() = {{1, 2, 3}, {4, 5, 6}} is totally not a perfect idea. Because the compiler can’t decide the type only based on the "right value" of the assignment statement. We want the array initialization syntax to be a pure expression that have an unambiguous type not only in assignment statements, but also in every semantic environment that requires an array.

  15. I would probably never use type inference with collection initializers due to the ambiguity, and its not really that hard to type the extra "as List(Of Integer)" but i think Bill hit the nail on the head with his suggestion of using

    Dim intArray() = {1,2,3} ‘ =integer array

    Dim intList = {1,2,3} ‘ = list of type integer

    its explicit about what is happening and keeps it so that you can determine what it means in a fraction of a second which is what i like about the vb syntax.

    But then as Konrad said, initializing the variable like that is typically going to be unchanging, and in that case an array would be better performance wise, in the off chance it should be a list it wont kill the programmer to add the declaration type (but then it wont really kill the programmer to add () if they know that the values wont change).

  16. It will be very cool to initialize the collection in one line of code.

    But I would like to ask if something like "yield return" in C# is planned in VB.NET future release. It would be great because I think that this feature is useful and very ellegant.

  17. 1.> I also feel neglected that we don’t have "yield return"

    2.> Bill hit the nail alright (It just feels right)

    3.> Need to find a way to have equally "Feels right" dictionary syntax.

  18. > Why not introduce JSON like syntax for the directionarys?

    Because — as nice as it would be to have this syntax — it would introduce yet another complete DSL into the VB language. And my, don’t we already have enough of them.

    As much as I like them, I believe that Linq and XLinq are already doing more harm than good to usability, mainly because they’re so utterly useless (or rather, implementing them as a full-blown DSL is useless; the pre-existing VB syntax is enough to harness their full semantical strength).

  19. Pingback: Panopticon Central
  20. Your example about the dictionaries is wrong:

    You missed the ‘New’ and the ‘From’ keyword:

    Dim d As New Dictionary(Of Integer, String) From {{1, "One"}, {2, "Two"}}

Leave a Reply