Friday, February 15, 2013

In defense of VB.NET

At the risk of being unpopular, I'll say this: VB.NET does not suck. At all.
No, I won't try to convince you to switch to VB.NET from C# or F#. I won't even tell you that VB.NET is great. But the amount of hate directed towards VB.NET seems unwarranted. Take a look at these recent quotes gathered from a cursory search:

"We identified the presence of VB .NET as our primary source of pain on the development team and committed ourselves to eliminating it."

"C# is far superior for real programmers. VB is far superior for script kitties" (sic)

"To me, Visual Basic seems clumsy, ugly, error-prone, and difficult to read."

 

It's too bad that few people cite concrete reasons for this hatred. Some mention the verbose syntax as an issue.

Syntax

Well, of course it's better to have a compact syntax to express common ideas, but the thing is, in the great landscape of statically typed programming languages, C# and VB.NET have roughly the same verbosity. Scala and ML languages (including F#) are usually considerably less verbose, and Haskell can be even less verbose.

Whether you like the syntax of a particular language or not is a highly subjective matter. For example, lots of people despise semicolons in C-like syntaxes, the eternal  javascript semicolon flamewar is evidence of this. Some other people hate curly braces.
Some particular things I personally find interesting about VB.NET syntax compared to C-like languages:

  • It doesn't require parens around conditions in ifs. This is similar to Haskell, ML languages, Pascal, Boo, Python.
  • Type declarations come after variables and function names, as in Nemerle, Pascal, Scala, Boo, ML languages, Gosu, Kotlin, TypeScript.
    Martin Odersky, creator of Scala, claims this increases readability. Of course, not everyone likes it.
  • Uses "Not" as the boolean negation operator instead of '!', which may be missed when reading code. Because of this, some people programming in C-like languages even go as far as preferring "a == false". Other languages that chose to use 'not' instead of a symbol include Pascal, ML languages, Haskell, Boo, Python.
    On a related note, by writing "a == false" you risk mistyping "a = false", which is an assignment, not a comparison, yet is usually valid code. For example in C#:
    bool a = false;
    if (a = false) {}
    
    You get a warning: Assignment in conditional expression is always constant; did you mean to use == instead of = ?.
    In VB.NET it's simply not possible to confuse assigment with comparison in this context:
    If b = False Then ...
    is always a comparison.
  • Uses "mod" instead of '%' for the modulo operation. Wikipedia has a neat table showing how this operation is represented in various languages. I really wonder how a percent sign ended up being the sign for a modulo operation.
  • Uses the keyword "Inherits" to indicate class inheritance, and "Implements" to indicate interface implementation. C# follows the C++ notation for inheritance, and makes no distinction between interface and class inheritance. VB.NET is like Java in this particular aspect of syntax. F# also makes the distinction between interface and class inheritance.
  • Has a separate, specific operator for string concatenation ('&'). Using '+' for string concatenation is usually critized since it's not a commutative operation as expected by something represented by a plus sign. Some other languages with a separate string concatenation operator: PHP, Perl ('.'), OCaml ('^'), ISO/ANSI SQL ('||'). Dart even removed '+' for strings. Personally I think it's a shame that VB.NET also allows '+' to do string concatenation.
  • And/AndAlso, Or/OrElse. Many people seem to trip over And/Or being non-short-circuit. But they seem to forget that C#/Java also have a non-short-circuit boolean operator, which is also shorter than the corresponding short-circuit operator. They're '&' and '|'. VB.NET's AndAlso/OrElse is exactly as in SML and Erlang. Oz has andthen/orelse, Ada has "and then" / "or else".
  • C-style for-loops are very flexible but also complex and known to be error-prone. I hardly ever use C-style for-loops in C# anymore, preferring foreach (var i in Enumerable.Range(0, 5)) when it's simply about iterating over a range. In VB.NET you just do For i = 0 To 4
  • Braces in C-like syntaxes are a source of endless style discussions, and also known to be error-prone. There's no such ambiguity in VB.NET.
  • VB.NET is case-insensitive. Whether this is good or bad has been debated to death. I can only say that personally, after several years of writing code in case-insensitive languages (Basic, Pascal, SQL) and even more years using many case-sensitive languages, I never found any problems with either case sensitivity or insensitivity.

 

I mention other languages with similar syntax decisions to emphasize that VB.NET isn't really very different or unique, and not everything about VB.NET's syntax is bad. Sometimes, a bit of verbosity can be a good thing. For example, the Haskell and F# communities generally acknowledge that point-free style, while usually more concise, is not always desirable and can lead to unwanted obfuscation.

Features

No, I'm not going to just list all the features of VB.NET. You can check MSDN for that. Instead, I'll try to describe the features I personally use or find interesting and how I use them.

Functional programming

In 2006, Erik Meijer announced that "Functional programming has reached the masses; it's called Visual Basic". In 2007, with the release of .NET 3.5, VB.NET (and C#) gained some features that help with functional programming: better syntax for anonymous functions, syntax sugar for monads (LINQ), local type inference.

While not strictly only about the language itself, having LINQ as part of the standard library is simply terrific. Even Lisp and OCaml code can be more complex and verbose than VB.NET with LINQ. Take a look at this Stackoverflow question. The VB.NET solution is not much more complex than the F# code:

        System.IO.File.ReadLines("ssqHitNum.txt").
            SelectMany(Function(s) s.Split(" "c).Skip(1)).
            GroupBy(Function(x) x).Select(Function(x) New With {x.Key, x.Count}).
            OrderByDescending(Function(x) x.Count).
            Take(18)

Related interesting language features include quotations (i.e. System.Linq.Expressions) and language integrated comonads (more widely known as async/await for Tasks).

Some seem to have taken Erik Meijer's announcement as tongue-in-cheek, but for me it has become a reality: thanks to language support and libraries (LINQ and FSharpx), I regularly use persistent data structures, higher-order functions, Option (Maybe) and Choice (Either) monadically, validation applicative functors, in my day job to write code without side-effects. Granted, it will never be as powerful as Haskell or as convenient as F#, but VB.NET has definitely become a usable functional language. Most of what I wrote about functional programming can be easily translated to VB.NET.

XML literals

The most salient and famous feature of VB.NET compared to C# is probably XML literals. I'm using this to model and transform HTML: it's safer and more composable than string-based "template engines" like Razor. You can see some of this in action in three of my open source projects: NHibernate web console, Quartz.NET web console and Boo web console. Underneath these little projects is a tiny web library I designed specifically for embedded ASP.NET modules. I'll probably blog about it in more detail some other day. Anyway, the "view engine" in this web library is just a 300-line file consisting of a bunch of stand-alone functions around System.Xml.Linq.

Type inference

Type inference seems to be slightly more powerful in VB.NET than C#. We can write this in F#:

let f x = x * x

and F# will infer a function int -> int

In C#, if we try to write:

var f = x => x * x;

the compiler complains that it "cannot assign lambda expression to an implicitly-typed local variable". (int x) => x * x doesn't work either. We have to fully declare the parameter and return types:

Func<int, int> f = x => x * x;

In strict, infer mode VB.NET (what I always use), the compiler will infer the return type for you:

Dim f = Function(x As Integer) x * x

Sure it's no big deal when it's an int, but when the return type is something like FSharpChoice<int, NonEmptyList<string>> it's not funny any more. Luckily, you hack around this C# limitation with a simple helper function (FSharpx includes this by the way).

Constants also enjoy better type inference in VB.NET. Const x = 2 is enough for VB.NET to infer that x is an integer. Not so in C#, you have to be explicit about the type: const int x = 2;

In VB.NET, Nothing doubles as default(T) in C#, that is, the type T is inferred by the compiler.

Project-wide imported namespaces

Aren't you tired of having these lines at the top of all your C# files?

using System;
using System.Collections.Generic;
using System.Linq;

You probably have much more than that on any typical project: System.Web, System.IO, your favorite ORM... VB.NET can cut down the number of annoying repeated open namespaces by storing them once in the project properties. Of course, this should not be abused.

Modules and static imports

VB.NET has modules which are similar to F# modules. They compile to sealed classes with static methods. By default, modules are internal, its functions are public, and other values are private.

When doing functional programming in C#, I end up with a lot of static classes and methods representing pure functions that don't close over any values (otherwise I just write a regular class with readonly fields). It gets annoying having to write "static" all the time. VB.NET modules solve that, giving you a programming model closer to F#. The only annoyance is that VB.NET modules are implicitly opened (as in [<AutoOpen>] in F#) when the containing namespace is open.

You can also import any class and get direct access to its static members without an explicit qualified reference to their class, just as when you open a module in F#. For example, I regularly use static functions in FSharpx so for example I just import FSharpx.FSharpOption to avoid having to write FSharpOption.ParseInt all the time.

Conclusion

If you read this far and are thinking "yeah, but these are still minor differences compared to C#", you are absolutely right. There's little difference between C# and VB.NET. If VB.NET sucks, it sucks as much as C#. Next time you start a new .NET project, consider using F# as the default language, and you'll see a real difference compared to C# / VB.NET.

VB.NET is no toy language. Other than the ones I mentioned, it has lots of features (many inherited from .NET, but still) that we now take for granted: garbage collection, parametric polymorphism with co/contravariance (which was considered by many an academics-only feature not long ago), first-class functions, local type inference.

Other features I didn't mention because I don't find them too special: multiple-method interface implementation, exception filters, and many others in this Stackoverflow question.

I don't use it myself, by VB.NET also works quite nicely as a dynamic programming language. As Erik Meijer put it in this Powerpoint presentation (2006): "static typing where possible, dynamic typing where necessary".

So if you're "stuck" with VB.NET for whatever reason (legacy code, corporate policy), your code does not necessarily have to be crap because of the language.

As a last remark: I find it amazing that today's VB.NET evolved quite directly from a language that looked like this in 1979:

10 INPUT "NAME"; A$  
20 PRINT "HELLO " A$

(this is Commodore BASIC V2, the original BASIC is older than that)