Early-bound <> late-bound

Eric Lippert recently talked about some of the fun and exciting things that go on when you use parameterless default properties in VBScript. He notes that the fact that VBScript is always late-bound means that the behavior of code in VBScript is often different than early-bound code in VB6. He goes on to say:

I hear you exclaiming “The fact that a difference in available type information leads to a difference in run-time behaviour violates a basic principle of programming language design!  Namely, the principle that late-bound calls have exactly the same semantics as early-bound calls!” […] Indeed, as I’ve mentioned before, VB6 and VBScript violate this principle in several places.

Actually, it’s not just VB6 and VBScript that violate this — it’s VB 2002, VB 2003, etc. too. Although we try to adhere to the principle that “early-bound behavior = late-bound behavior” in general, it’s really a question of tradeoffs. The simplest example is the way that overload resolution works. Let’s say you have two methods:

Sub Foo(o As Object)
Sub Foo(s As String)

Now, if I declare a local variable x that is typed as Object, the compiler will look at the two overloads and say “well, obviously the Object overload is the better match because the type of the variable is Object.” But let’s say instead you deferred the call until run-time and at runtime the value stored in x is of type String. Now, the run-time late binder doesn’t actually know what the declared type of the variable x is. All it knows is that the argument that got passed into the late binding helper is of type String. So, naturally, it’s going to choose the String overload as being the best match because it’s got a String to pass to it. So in this case, early-bound behavior <> late-bound behavior.

This isn’t to say that it wouldn’t be possible to make the two behaviors the same, it’s just a real question as to whether it would be desirable. Late binding is not exactly the fastest thing in the world compared to early bound calls. Deferring work until runtime comes with a cost, and we’d prefer to keep that cost to a minimum. To “fix” this situation would require creating type objects that represent the static type of the expressions we’re late binding over so that the late binder could exactly replicate the early-bound behavior. This adds even more overhead to a process that’s not super lightweight to begin with. And, really, does it matter? Yes, you’re getting a different overload called, but it’s hard to argue that the behavior is really wrong.

(Another example of the late-bound/early-bound split is interfaces. Because interfaces have no identity of their own, the late binder can’t see them unless, again, the compiler were to give it extra static type information. We chose instead to not allow late binding against interfaces.)

In the end, we view “early-bound = late-bound” as a goal rather than a rule. Probably 95% of cases work the same whether you choose to do them early-bound or late-bound, and the other 5% should work is a way that the user would consider “correct.” So, we hope, no one really ever had to notice that static typing and dynamic typing aren’t exactly the same thing…

One thought on “Early-bound <> late-bound

  1. Eric Lippert

    Regarding performance of late-bound overloads — I would make the opposite argument. The late-bound call is already going to be incredibly inefficient compared to the early bound call. Given that, what’s a little more time to do correct overload resolution? My attitude is when faced with a choice between correct butslow vs subtly wrong or different but faster, correct is almost always the better choice.

    On the THIRD hand, or perhaps back on the first hand, one also has to consider the dev cost. As you well know, the first 95% of the development takes 95% of the work, and the last 5% takes the OTHER 95%, which is why trying to make it perfect usually slips the schedule. I can certainly sympathize with the attitude that writing a VB-specific late binder that exactly replicates VB’s overload resolution is arguably not the best use of developer team time — time that could be spent looking for security problems or adding more useful language features.

    Like all design problems, it’s a series of tradeoffs.

    Reply

Leave a Reply