Sunday, April 11, 2010

VB11 Wish List - Part 1

With the release of visual studio 2010 coming this week, it's high time to publish wish lists. The Visual Basic .Net of today has come a long way from the BASIC of yonder old days, and not all of it involves making the name longer. However, there's still so much that could be done, or could be done better. These are my wishes and my ideas, and hopefully they will affect Microsoft's decision process in some small way. Unfortunately, I have so many ideas there's no way they could all be implemented. Oh well, here we go!

#1) Public-Get/Private-Set and ReadOnly Auto Properties

One of the features introduced in VB10, and imported from C#, is auto properties. Unfortunately, VB10 auto properties are quite limited compared to their C# counterparts. It is impossible to specify an auto property with a private setter and a public getter (Oh well, at least it's only the most common case (sigh)). It's also not possible to create a ReadOnly auto property, which is odd considering auto properties otherwise act like variables (mostly).

I want the ability to define auto properties with different access levels on the getter and setter and ReadOnly (fixed at construction) auto properties. I suggest the following syntax:

'Public getter, private setter
Public Get Private Set Property Value As Integer
'Fixed after construction time
Public ReadOnly Property Value As Integer

#2) Flexible 'Implements'

You've probably had this happen to you before: you want to implement something from an interface, but your class doesn't quite match. You have a function but you need a property, you have a property but you need a ReadOnly property, you have a function but you need a subroutine or a function with a weaker return type, etc, etc. You work around the problem by implementing the interface member using a private function/sub/property which delegates to your existing stuff.

All of these not-really-a-mismatch cases should be allowed. The compiler should transparently implement the boilerplate code to match the interface member to the implementing construct.

#3) Infer Redundant Operators

Implementing a comparison operator without its partner currently causes a compile error. If you define '=' but not '<>', or '<' but not '>', or '<=' but not '>=', or vice versa, then you get a completely unnecessary compile error. I hate that compile error. The missing operator is trivial: invert the arguments for an ordering operator or invert the result for an equality operator. The compiler should be doing these trivial transformations for you. Defining an equality operator and an ordering operator should give you all of the comparison operators, no questions asked. At the very least, operators' partners should be inferred.

#4) Comparison Operator: <=>

Ruby has a very nice comparison operator: <=>. It returns the sign of comparing two values: -1 when the first argument is less, +1 when it is greater, and 0 when it is equal. I'd like to see it in VB/.Net.

Select Case value1 <=> value2
Case -1
Case 0
Case +1
End Select

#5) Iterator Blocks

I'm very surprised this didn't make it into VB10. I know the VB team had excellent ideas about it at least as far back as 2008. Iterator blocks? Awesome!

Function FromTo(ByVal low As Integer, ByVal high As Integer) As IEnumerable(Of Integer)
Return Iterator
If low <= high Then
Return low
Return Each FromTo(low + 1, high)
End If
End Iterator
End Function

#6) Linq Zip Operator

I often want to zip sequences together, but I have to break the linq flow and use explicit calls to Enumerable.Zip. There should be a linq operator for zipping:

Dim evenItems = From item In items
Zip index In Naturals
Where index Mod 2 = 0
Select item

#7) IDisposable Event Subscriptions

You should clean up your event handlers. Failure to do so can result in memory leaks (handlers can't be garbage collected while a long-lived event dispatcher has a reference to them). Just ask the Princeton team from the 2007 DARPA challenge, because they had exactly this type of memory leak. So, ideally, removing event handlers should be as painless as possible.

Unfortunately, removing event handlers isn't painless. First, every properly removed handler requires three lines (store delegate, add delegate, remove delegate). You must store the delegate you are going to add and remove, because otherwise you will try to remove an equivalent-but-different delegate and end up removing nothing. Second, every RemoveHandler line is an identical copy of an AddHandler line, except you replace the 'Add' prefix with 'Remove'. Finally, you add and remove events in different parts of the code, which makes it harder to keep the add/remove lines synced.

In order to overcome the far-away-sync pain, I started using the following pattern for event subscriptions: package the RemoveHandler call into a delegate, and package that inside an IDisposable. You end up with a 'subscription', which can be disposed to remove the event handler. Therefore, instead of using RemoveHandler, you dispose the subscription. This enormously simplifies removing groups of event handlers, because you can keep all the subscriptions in a list of IDisposable. For example:

Dim handler = AddressOf SomeMethod
AddHandler SomeObject.SomeEvent, handler
Me.subscriptions.Add(New DelegatedDisposable(Sub() RemoveHandler SomeObject.SomeEvent, handler))
... elsewhere, in the disposal code ...
For Each subscription In Me.Subscriptions

Unfortunately, you still have to track the delegate (albeit in a hoisted local instead of a class member), and you still have to repeat yourself in the RemoveHandler call. All of this would be solved if 'AddHandler' returned an IDisposable which, when disposed, removed the added handler. There would be no more need to track the delegate or repeat the handler arguments. Instead of three lines of code per handler, you end up with just one:

Me.subscriptions.Add(AddHandler SomeObject.SomeEvent, AddressOf SomeMethod)

To Be Continued