Typing Hard Can Trip You Up

Typing Hard Can Trip You Up

  • Comments 19

Speaking of weird JScript gotchas, here's a weird VBScript gotcha that I alluded to earlier.

foo = "1"
bar = 1
If "1" = 1   Then Print "True!" Else Print "False!"
If "1" = bar Then Print "True!" Else Print "False!"
If foo = 1   Then Print "True!" Else Print "False!"
If foo = bar Then Print "True!" Else Print "False!"

This prints out "True!" for the first three and "False!" for the last.

My psychic powers tell me that you are now thinking "What the heck is up with that?" 

This weirdness is for compatibility with some similar weirdness in VB6.  VB6 violates an important principle in programming language design, namely that the semantics of an operation should be the same whether the type information about the operands was known at compile time or run time.  VB6 has different semantics for operations involving variants than for operations involving "hard typed" values when comparing strings to numbers. (Booleans count as numbers -- as I discussed earlier, they are treated as -1 and 0.) 

But that should have no impact on VBScript, right?  Because in VBScript, everything is a variant, so there should be no such issue.  Well, not exactly.  All variables are treated as though they were declared as variant, sure.  And internally, all data is stored in variants.  But as far as comparison operations go, VBScript follows VB6's lead and treats literals as hard-typed values.

The relevant comparison rules in VB6/VBScript go like this:

  • Hard string ~ hard number: convert string to number, compare numbers
  • Hard string ~ soft number: convert number to string, compare strings
  • Soft string ~ hard number: convert string to number, compare numbers
  • Soft string ~ soft number: any string is greater than any number

Though they violate the principle that semantics should be consistent regardless of when the type information is deduced, the middle two rules do make some sense -- the "soft" side is converted into the type of the "hard" side for the comparison.  The first and last rules are both arbitrary, and either is defensible.  What I don't understand is why the first and last aren't consistent with each other.  That just seems egregiously wrong to me.  Someone who knows more about the history of VB than I do will have to chime in here!  This has always irked me about VB6/VBScript, but there's nothing I can do about it now.  (And of course these rules also violate the transitive property of the equality operator, which is irksome in itself.)

It gets even weirder when you consider Boolean/string comparisons:

bobble = "True"
robble = True
If "True" = True   Then Print "True!" Else Print "False!"
If "True" = robble Then Print "True!" Else Print "False!"
If bobble = True   Then Print "True!" Else Print "False!"
If bobble = robble Then Print "True!" Else Print "False!"

That produces "True!" for the first two, "False!" for the last two.  Again, what the hey?   Why is this different from the case above?

That's a bug.  VB6 does what you'd expect, and is consistent with the integer behaviour.  We forgot to emit code that marks hard-typed Booleans as hard-typed.  Therefore VBScript uses the rules for soft typing regardless of whether the Boolean is a literal or not.  This is yet another on the long list of small, unintended deviations from the VB6 subset.

Unfortunately, by the time we discovered the bug it was too late -- the bug had shipped to customers.  We considered fixing it, but realized that it was more important to not take the chance of breaking existing scripts than to fix this rather unimportant incompatibility with VB6.  (That wasn't the only time we passed on fixing a bug because the fix would break backwards compatibility.  But that's another story.)

  • Are there reasons that you don't want to implement == and make the future less ambiguous?
  • This is trivial but I've been wanting to
    ask this for a long while...

    I had trouble getting hexadecimal expressions
    for JScript error codes in a programmatic way.

    Please consider these scripts and results.

    VBS:

    on error resume next
    WScript.CreateObject("abc")
    WScript.Echo(Err.Description)
    WScript.Echo(Err.Number)
    WScript.Echo(Hex(Err.Number))
    on error goto 0

    Result----

    Automation Object named "abc" does not exist.
    -2147352567
    80020009

    JS:

    try{
    WScript.CreateObject("abc");
    } catch (e) {
    WScript.Echo(e.description);
    WScript.Echo(e.number);
    WScript.Echo(e.number.toString(16));
    WScript.Echo((e.number + 0x100000000).toString(16));
    }

    Result----

    Automation Object named "abc" does not exist.
    -2147352567
    -7ffdfff7
    80020009

    ----

    So VBS hex() function is not equivalent to
    JS Number.toString(16). I had to add 0x100000000
    (which I'm not sure is exactly the right way).

    Funny part is if you don't catch the exception
    and let it go through, ASP or WSH runtime will
    show the error codes in the usual hex format.

    Do you happen to have any info on this?
  • While I was at the above I happened to find
    that division by 0 in JS doesn't produce
    any errors...

    WScript.Echo(0/1) returns "1.#INF".
  • I already wrote an article explaining all that.

    http://blogs.msdn.com/ericlippert/archive/2003/10/22/53267.aspx

    And yes, 1 / 0 = infinity in JScript. Again, this is consistent with JScript's design principle: got an error? Muddle on through.
  • Oh you covered it already...my apologies.
    Thanks for the info though. Wish I had read it
    3 years ago.
  • You're welcome. I wish I had posted it three years ago.
    :)
  • First off, I want to thank Eric for exposing the "what makes it really work" info out there for those of us who still love scripting. In my case - VBScript/ASP. I have one question: Microsoft saw fit to introduce JScript.NET, why have they forsaken us with the lack of VBScript.NET or it's equivalent?
  • We did: VB.NET is the new VBScript.

    We had this conversation already.

    http://weblogs.asp.net/ericlippert/archive/2004/03/11/88308.aspx

    We invented VBScript because VB6 was too heavyweight to redistribute and use in web pages. But going forward, the .NET framework will be ubiquitous. (It's not yet, obviously, but it will be.) There is no additional redistrubution cost for using VB.NET.

    Can we make VB.NET a better language for scripters? Sure, and I hope we do. But that's the way forward -- by improving VB.NET, not by fracturing the language yet again into ANOTHER slightly incompatible version.
  • I just recently found your site and didn't see the previous article. I understand what you're saying, although reluctantly. I've become so use to going between admin scripting and web development, without using different languages, that it seems like a huge leap to change my way of thinking into the .NET world. I feel like there are still so many things you can still do with ASP/VBScript and admin scripting with VBScript.
  • > I just recently found your site and didn't see the previous article.

    Excuses, excuses. What, you haven't read all 200 articles yet? Get cracking!

    :-) <-- Mr. Smiley indicates that Eric is kidding.

    > I feel like there are still so many things you can still do with ASP/VBScript and admin scripting with VBScript.

    Well, I'm glad that you still find the old tools useful.

    > it seems like a huge leap to change my way of thinking into the .NET world.

    I agree, and I would love to see more high-level scripting languages become popular in the .NET world, as well as more scripty features in VB.NET.
  • Do you think I should put to rest my dream of a compilable wrapper for vbscripts? I've been curious to know how Windows XP's control panels load HTAs from a dll. I see alot of potential there to code rich client apps in HTML/script, but distribute them in a secure executable fashion. I'm sure it's overkill with VB.NET looming over my shoulder.
  • Eric,

    The Variant handling in VB 6 always confused the hell out of me.

    Below was the code I have always been confused by:

    Dim x As Variant
    Dim s As String

    x = ""
    s = ""
    MsgBox VarType(s) = VarType(x)
    MsgBox x = True
    MsgBox s = True

    I could never understand why the last and second last lines act differently. I will reread your article and see if it enlightens me. ;-)

    Thanks
    Matthew
  • The var types are the same because that gives you the RUN TIME variant type of the CONTENTS, not the COMPILE TIME variant type of the VARIABLE.

    And then the difference is because in the one case you're comparing a hard string to a hard bool, in the other you're comparing a soft string to a soft bool.

    I agree that it's confusing. It's confusing because it breaks the rule that behaviour should be the same whether the type was known at compile time or run time.
  • > Do you think I should put to rest my dream of a compilable wrapper for vbscripts?

    The whole point of vbscript is that it is not compiled. If you want a language that compiles into DLLs or executables, use VB6 or VB.NET or C# or whatever.

    > distribute them in a secure executable fashion

    I'm not sure what you mean by "secure" in that sentence. Do you mean "signed"?

    WSH supports code signing of vbscript files, FYI.
  • Tsk, You mean that the whole point is that it is not compiled until you RUN it. ;)

    One of the intriguing things about VBScript as it has evolved, due in no small part to Eric's efforts, is that the vast majority of code people write in VBScript can be easily ported "downhill" - if you can say something in VBScript, you can say it precisely the same way in VB6. There are some minor exceptions (eval and execute) but other than that, code formed as procedures can pretty much be copy-pasted into VB6 and work.
Page 1 of 2 (19 items) 12