Programming Language Review: Visual Basic


I have been using Visual Basic (VB) at work a lot, and I have some (many) opinions about it. Having created a programming language myself, I find the design decisions and little features/quirks of this language very fascinating.


I read through (most) of the Visual Basic docs on MSDN before I started seriously looking at any VB code, and I am glad I did! There are a lot of weird "features" which aren't present in most other languages, and other things that just don't make any sense. VB shares a pretty good feature parity between C# (VB, C#, and F# the 3 main languages that make up the .NET family of languages), despite Microsoft halting future development on VB.


Saving the best for last, let's dive into some VB code!


Unforgivable


There are some things that VB does which are just plain wrong:


' Using single quotes as a comment. Makes grep'ing for things harder
' Also, no multi-line comments

' Why in the world would you use <> for the "not equal" operator?
Dim isNameNotBob As Boolean = name <> "bob"

Dim str As String = "using ampersands " & "for" & " string concatenation"

These are just minor annoyances, but can be overlooked. They just don't make sense, that's all. But, there are some things which just sinful:


It's Case Insensitive


Yep. It is really jarring to see the same variable with different cases. It just brings a certain feeling of uncertainty when working on a project.


Depending on your IDE (Visual Studio 2022, I am looking at you), it might also just go ahead and "normalize" your symbols for you, changing the casing to the casing of the original variable. In our codebase, we have had a global variable called sql, and in doing refactorings, Visual Studio would rename sql to Sql (note the capital S), since Sql was a namespace which we had available. When you added in the local sql variable back in, it wouldn't change it back for you! You would end up with code like this:


Dim sql = "SELECT * FROM table_name"

run(Sql)

I get that the keywords in Visual Basic are case-insensitive, and that is fine, but identifiers? That is too far IMO.


Note that Visual Studio has nothing to do with the VB language itself, but it does do a good job of accentuating this particular design flaw in VB.

Implicit Function Calls


Another fun fact about VB is that functions that don't take arguments can be called without using any parenthesis:


Function f() As Integer
    Return 1337
End Function

Sub Main()
    Console.WriteLine(f)
End Sub

I don't know why this is the case. Even the docs say not do do it.


Type Conversion Suffixes


Given the following example:


Dim a = "1234"
Dim b$ = "1234"
Dim c# = "1234"
Dim d% = "1234"
Dim e! = "1234"
Dim f@ = "1234"
Dim g& = "1234"

Console.WriteLine(a & " " & TypeName(a))
Console.WriteLine(b & " " & TypeName(b))
Console.WriteLine(c & " " & TypeName(c))
Console.WriteLine(d & " " & TypeName(d))
Console.WriteLine(e & " " & TypeName(e))
Console.WriteLine(f & " " & TypeName(f))
Console.WriteLine(g & " " & TypeName(g))

What do you expect this code to return?


1234 String
1234 String
1234 Double
1234 Integer
1234 Single
1234 Decimal
1234 Long

What is going on here? Well, "type characters" of course! They are these little warts you can sprinkle on your code which let you declare the type of the variable using the name of the variable itself. Not fun.


You can also cast the result of a function just as easily using these same conversion symbols:


Dim name = "Billy Bob"

Console.WriteLine(Left$(name, 5))

This will print Billy (as a string), since $ is the type character for string conversions.


Left already returns a string, so the $ is redundant, but you get the point.

Unfortunate


These next few things are interesting "features", gotchas, or both! They aren't as egregious as the last section, but they might trip you up if you aren't paying attention.


Short Circuiting


And I'm not talking about that one Daft Punk Song.


You might not know what short circuiting is off the top of your head, but you have definitely experienced it at some point. Take the following Python code for example:


def proxy(x):
    print(f"proxy: {x}")
    return x

def expensive():
    print("some expensive call")
    return true

a = proxy(False) and expensive()
b = proxy(True) and expensive()

When running, we get:


proxy: False
proxy: True
some expensive call

As you can see, our expensive function is only called if the proxy method returns true. If it returns false (like in the first example), it doesn't matter what expensive returns, the expression will always evaluate to false. This is really nice if you only want to call something expensive if a certain condition is met, or want to reduce the number of unnecessary computations in your program.


So, what happens in VB you might ask?


Function proxy(x As Boolean) As Boolean
    Console.WriteLine("proxy: " & x)
    return x
End Function

Function expensive() As Boolean
    Console.WriteLine("some expensive call")
End Function

Sub Main()
    Dim a = proxy(false) And expensive()
    Dim b = proxy(true) And expensive()
End Sub

Running:


proxy: False
some expensive call
proxy: True
some expensive call

So, one might think that VB doesn't have short circuiting, but they would be incorrect! VB does indeed have support for short-circuiting, but you need to use a different operator, AndAlso!


There is also an short-circuiting version of the Or operator called OrElse.

Changing our code to the following, we get the result we expected:


Sub Main()
    Dim a = proxy(false) AndAlso expensive()
    Dim b = proxy(true) AndAlso expensive()
End Sub

Running:


proxy: False
proxy: True
some expensive call

Why AndAlso isn't the default behavior, the world may never know. This might be due to with some BASIC backwards compatibility issues, but who knows.


Modules


Modularity is normally a good thing: it keeps our code in little compartments, and allows us to better organize our code. Not in VB. A Module in VB translates to: "make the code in this block available, without the need for an Import, to all VB files in this project".


If you are looking for actual "modularity", use namespaces instead.


Modules (in VB) are really helpful for utilities which need to be available everywhere in the application, but can very easily clog up the global namespace.


Named Return Values


I really like named return values, and I think Golang does a good job of implementing it:


func add(a int, b int) (sum int) {
    sum += a
    sum += b
    return
}

func main() {
    fmt.Println(add(1, 2))
}

This basically will add a and b together, storing the result in sum. When the function returns, sum is returned automatically. Very nice!


VB on the other hand, does not do this nicely:


Function Add(a As Integer, b As Integer) As Integer
    Add += a
    Add += b
End Function

Sub Main()
    Console.WriteLine(Add(1, 2))
End Sub

What I really don't like is the fact that the return value is the same as the function name itself. When you first see this, it might be confusing: Why are we assigning to the name of the function? Does that even work?


Although the feature is "nice", it will be annoying when it comes time to rename that function (and VS 2022 won't rename the Add references if you change the function name).


No New Expressions


Normally in C#, you can write this:


public class SomeClass {
    public int SomeMethod() {
        Console.WriteLine("hello world");
        return 1234;
    }
}

static void Main() {
    int value = new SomeClass().SomeMethod(); // this line
}

Here we create a new instance of SomeClass, call its SomeMethod, assigning the result to value, and destroying our temporary class instance.


In VB though, there is no good way of doing this:


' This line won't compile:
Dim value = New SomeClass().SomeMethod()

' Or this:
Dim value = (New SomeClass()).SomeMethod()

' This is the typical way you would do this (requires temp variable)
Dim temp As New SomeClass()
Dim value = temp.SomeMethod()

The closest you can get is using the Call statement, which allows for calling expressions which don't start with an identifier (ie, New). The only issue with Call is that it treats the function/sub-procedure as a sub-procedure, discarding the result in the case of a function!


This seems to be a bug in the expression parser. There is no reason why you can assign the expression New Something() to a variable, but not use that same expression to call a method on said expression (like New Something().Method()).


It is also unfortunate that you need a temporary variable because the lifetime of this variable now is extended to the end of it's bounding scope, which could potentially the end of the function. In the case of C#, having the temporary instance allows for the garbage collector to free up resources sooner, whereas VB has to wait until the scope is exited.


Underrated


There are some things that I think makes VB really shine, things that more languages should pick up on.


The Power Operator


Most languages use **, or a separate pow function to raise things to a power. This is how you do it in VB:


Dim square = 3 ^ 2

Beautiful.


Even though Python has the and, not and or keywords, it doesn't have xor, meaning it is stuck with ^ for the xor operator, along with all the other C-style languages.


See this blog post for more comparisons of C to other languages.

Using Blocks


Like Python (and C#), VB has a using block for managing resources:


Using resource As New SomeResource
    ' use resource
End Using

Assuming that the SomeResource class implements the IDisposable interface, it can be used in a using block no problem. This is a really nice pattern in Python, and makes cleaning up resources super easy.


Events


One of the best features of VB that actually really surprised me is that it has built-in supports for events. Basically, you attach Handles SomeEvent to the end of your sub-procedure, and it gets called whenever that event is fired!


Public Class UserRepo
    Public Event UserRegistered(ByVal name As String)

    Public Sub Add(username As String)
        RaiseEvent UserRegistered(username)
    End Sub
End Class

Module VBModule
    WithEvents repo As New UserRepo

    Sub AddUserToDataBase(name As String) Handles repo.UserRegistered
        Console.WriteLine($"Adding user {name} to database")
    End Sub

    Sub SendWelcomeEmail(name As String) Handles repo.UserRegistered
        Console.WriteLine($"Sending Welcome email to {name}")
    End Sub

    Sub Main()
        repo.Add("Alice")
        repo.Add("Bob")
        repo.Add("Charlie")
    End Sub
End Module

Note that this code is untested. All of these VB examples were checked using online VB compilers, which uses Mono, Which has lacking support for certain VB features.

Update: I found this cool site called Coding Rooms, which is an online IDE which supports live code-sharing, and so on. You can run this demo here.

This is really nice, because it allows for the caller and the callee to not interact with one another. You just register the event you want to listen to, and when it is fired, your function gets called!


Fin


That's it! There are probably some things I am missing, but this pretty much covers most of the "features" which you will run into on a daily basis.


When I started really learning VB, I tried my best to remain neutral, to not love it or hate it, to look for the good and the bad before I made my decision. I have made my decision. It could be better! It does a lot of things right, but a lot of things are just not that great.