VB WTF



  • I'm usually the one defending VB as at least an OK language and blaming all the bad VB code in the world on the hordes of inexperienced programmers that pick VB as the language with which to torment the rest of the wolrd.

    However, VB does have some funkinesses.  Especially the older versions.  My favorite is a VB6 "feature".  It turns out that only about 2% of the VB population actually knows when to use parentheses when calling routines in VB6, that's why they changed the rules in VB.Net.  Here is a little snippet to illustrate the WTF:

    <FONT face="Courier New"><FONT color=#0000ff>Sub</FONT> Test()
      <FONT color=#0000ff>Dim</FONT> a <FONT color=#0000ff>As Integer</FONT>
      a = 5
     
      CallMe a
      MsgBox a
      CallMe (a)
      MsgBox a
    <FONT color=#0000ff>End Sub</FONT></FONT>


    <FONT face="Courier New"><FONT color=#0000ff>Sub</FONT> CallMe(<FONT color=#0000ff>ByRef</FONT> x <FONT color=#0000ff>As Integer</FONT>)
      x = x + 1
    <FONT color=#0000ff>End Sub</FONT>
    </FONT>

    <FONT face="Courier New"><FONT face="Times New Roman">If Sub Test is called, it will behave strangely.  I'll leave it up to you guys to explain why it will raise two MsgBoxes, both displaying "6".  This behavior confused VB6 programmers so much that most simply guessed whether they should use parens or not and fixed the code when it blew up rather than actually understanding how it worked.  It used to take me at least 30 minutes to explain this behavior in classes, and a lot of people still didn't get it.</FONT></FONT>

    <FONT face="Courier New"><FONT face="Times New Roman">BTW, this behavior isn't dead yet.  Until Office switches over to a .Net version of VBA, macro programmers will have to continue to wrestle with it.</FONT>

    </FONT>

     



  • I want to say 'First!', but then I'd be like those other loosers



  • @foobar2005 said:

    I want to say 'First!', but then I'd be like those other loosers


    Too late.


    I hae no idea.
    (a) doesn't get passed as argument while a does?
    Parentheses on an int have special meaning?



  • @dhromed said:

    @foobar2005 said:
    I want to say 'First!', but then I'd be like those other loosers


    Too late.


    I hae no idea.
    (a) doesn't get passed as argument while a does?
    Parentheses on an int have special meaning?




    Actually, it's quite complicated. I found a blog posting about it here:



  • It's indeed ridiculously complicated. How can they even make up something as silly as that rule 3.3? It's almost like they are annoying the users of VB on purpose.



  • My shot:

    In CallMe a, the variable a is passed by reference as you would expect. In CallMe (a), (a) is first evaluated as an expression, because of the parentheses, then the result of that expression is passed by reference... yielding no change in the variable if the value is changed.

    Though why it would be legal to pass an expression by reference, I don't know.

    This syntactical oddity of VB has annoyed me more than once... you are required to use parentheses in a function call (just like most other languages), but are required to omit them in a sub call (why??). However, because parentheses are also used to group expressions, if the sub has only a single argument you can get away with putting parens in the function call and not be aware that you're misusing the syntax.

    <FONT face="Courier New" size=2>CallMe a             ' valid
    CallMe(a)            ' valid
    c = CallMe(a, b)     ' valid
    CallMe(a, b)         ' invalid</FONT>



  • @RiX0R said:

    My shot:

    In CallMe a, the variable a is passed by reference as you would expect. In CallMe (a), (a) is first evaluated as an expression, because of the parentheses, then the result of that expression is passed by reference... yielding no change in the variable if the value is changed.

    Though why it would be legal to pass an expression by reference, I don't know.

    This syntactical oddity of VB has annoyed me more than once... you are required to use parentheses in a function call (just like most other languages), but are required to omit them in a sub call (why??). However, because parentheses are also used to group expressions, if the sub has only a single argument you can get away with putting parens in the function call and not be aware that you're misusing the syntax.

    <FONT face="Courier New" size=2>CallMe a             ' valid
    CallMe(a)            ' valid
    c = CallMe(a, b)     ' valid
    CallMe(a, b)         ' invalid</FONT>


    Bingo.  (a) is seen as an expression, so it cannot be successfully passed by reference.  I can answer your why question too -- backwards compatibility.  In VB6 and earlier, there is only one rule for using parentheses on calls:

    Rule: You must use parentheses around arguments if you are using the return value, if you are ignoring the return value (or if there is none), then parentheses must be ommitted.

    This is the rule because realllllllllly old BASIC didn't have functions.  Old BASIC also didn't use parentheses for sub calls (unless you used the Call statement).  So, when functions were added, itwas noticed that a function couldn't be called while embedded in an expression without parentheses or the statement would be ambiguous.  Example:

    MsgBox MsgBox "Are You Sure", vbYesNo

    It is ambiguous which MsgBox gets the vbYesNo parameter.

    MsgBox MsgBox ("Are You Sure", vbYesNo)

    Is unambiguous and acceptable to VB.  Notice how the inner MsgBox must have parentheses, but the outer MsgBox must omit them.  By the same rule this:

    MsgBox (MsgBox ("Are You Sure", vbYesNo))

    is lagal.  The extra parentheses are useless, but don't cause an error.  However, this:

    MsgBox (MsgBox ("Are You Sure", vbYesNo), vbOK)

    is not legal because the only valid interpretation of the outer parantheses is that they enclose parameters and this is not legal since the return value is being ignored.



  • Horrible horrible mess, was ol' VBx for that sort of thing. I guess
    it's a good lesson that trying to make things easier without thinking
    out the consequences often just makes things worse. By trying to make
    it easier ("I know, n00bs find those funny bracket things confusing,
    let's make it so that they don't have to use them unless it's really
    required"), they lead to a situation that makes things harder, more
    confusing, and less predictable for all.



    I'm so happy that they fixed it all up in .NET.



    If you can't make an 'improvement' clean and precise with solid and clear rules, DON'T DO IT!!!




  • Jesus.

    This is bad. Really bad.

    Scrap the distinction between Sub and Function, and all problems go away.

    Backwards compatibility be damned.
    Glad that VB.Net requires parens at all times. Syntactic ambiguity is the devil*. "Consistency is key." :)

    *) that actually includes JScript's lack of a true [code]elseif[/code] statement, but I've never run into any intuition problems with it, even in my n00b days.



  • @dhromed said:

    Scrap the distinction between Sub and Function, and all problems go away.


    But but..... you forgot GoSubs!


    <font size="2">Private Sub Foo()
        GoSub Bar
    Exit Sub

    Bar:
        MsgBox "Hello world!"
    Return

    End Sub</font>


    Gotta love it!



  • @dhromed said:


    ...Scrap the distinction between Sub and Function, and all problems go away....

    Actually, Subs and Functions suffer from this problem equally.  This biggest mistake people make when trying to deal with this problem is try to break the rules down into one set of rules for Subs and one for Functions.  All you do is make twice as many things for you to remember.  There is only one rule and it applies to both:

    "If you use the return value, you must use parentheses.  If you ignore the return value, then you must not use parentheses"

    Just consider a Sub to be like a C function that returns void.  It does return something, but that something is nothing.



  • @foobar2005 said:

    I want to say 'First!', but then I'd be like those other loosers

     

    spelling loser "looser" makes you a loser, loser



  • ok wait,

    since it is expecting a reference and you are forcing it to pass a value...

    is it using that value as a reference and instead of changing the variable you want, which it is obviously not, is it just changing some random other thing? 



  • It's changing the temporary variable created internally by VB to hold the result of the expression.  Since this variable is inaccessible to the programmer, it looks like the value is being discarded.  VB isn't the only language to do this, Transact-SQL in MS SQL Server does the same with OUTPUT parameters on stored procedures.



  • So


    [code]someSub (a)[/code]

    Is basically equivalent to

    [code]someSub(temp = a);[/code]

    in another language, while

    [code]someSub a[/code]

    is equivalent to

    [code]someSub(a);[/code]


  • ♿ (Parody)

    @jsmith said:

    <FONT face="Courier New"><FONT face="Times New Roman">It used to take me at least 30 minutes to explain this behavior in classes, and a lot of people still didn't get it.</FONT></FONT>

    Explain? No need to explain, that's what The Policy is for. Sure, junior "know it all" developers will think you're a pedantic loon for requiring all Function/Sub calls to use Call (as in, "Call CallMe(a)"), but atleast no one will get bitten by this behavior.

    One of my favorites is the bug that lets you do ...

    If <somecondition> Then
      Function DoIt()
        DoSomething()
      End function
    Else
      Function DoIt()
        DoSomethingElse()
      End function
    End If

    It works fantasticially when <somecondition> is false ... not quite as good when it's true, though.



  • @Alex Papadimoulis said:

    @jsmith said:

    <font face="Courier New"><font face="Times New Roman">It used to take me at least 30 minutes to explain this behavior in classes, and a lot of people still didn't get it.</font></font>

    Explain? No need to explain, that's what The Policy is for. Sure, junior "know it all" developers will think you're a pedantic loon for requiring all Function/Sub calls to use Call (as in, "Call CallMe(a)"), but atleast no one will get bitten by this behavior.

    One of my favorites is the bug that lets you do ...

    If <somecondition> Then
      Function DoIt()
        DoSomething()
      End function
    Else
      Function DoIt()
        DoSomethingElse()
      End function
    End If

    It works fantasticially when <somecondition> is false ... not quite as good when it's true, though.



    This bug example I don't understand. What goes wrong and why, when <somecondition> is true? I don't see a difference in the parens between both cases.


  • @Quincy5 said:

    @Alex Papadimoulis said:

    @jsmith said:

    <font face="Courier New"><font face="Times New Roman">It used to take me at least 30 minutes to explain this behavior in classes, and a lot of people still didn't get it.</font></font>

    Explain? No need to explain, that's what The Policy is for. Sure, junior "know it all" developers will think you're a pedantic loon for requiring all Function/Sub calls to use Call (as in, "Call CallMe(a)"), but atleast no one will get bitten by this behavior.

    One of my favorites is the bug that lets you do ...

    If <somecondition> Then
      Function DoIt()
        DoSomething()
      End function
    Else
      Function DoIt()
        DoSomethingElse()
      End function
    End If

    It works fantasticially when <somecondition> is false ... not quite as good when it's true, though.



    This bug example I don't understand. What goes wrong and why, when <somecondition> is true? I don't see a difference in the parens between both cases.


    Functions are compiled/interpeted before anything else, then set aside as sleeping code.
    Consecutive function delcarations will see the last one come out on top.

    Therefore, the only thing to ever execute is DoSomethingElse(), because the Doit() containing DoSomething() is overwritten by the second Declaration of Doit().

    Of course, in this particular snippet, nothing will happen at all, because DoIt() is never called; only declared.*

    I really hope that most compilers issue a warning about duplicate function names.


  • @Alex Papadimoulis said:

    One of my favorites is the bug that lets you do ...

    If <somecondition> Then
      Function DoIt()
        DoSomething()
      End function
    Else
      Function DoIt()
        DoSomethingElse()
      End function
    End If

    It works fantasticially when <somecondition> is false ... not quite as good when it's true, though.



    This looks like some hack attempt at doing conditional compilation in previous versions of VB that didn't support such things.  If its not, I'm not sure why one would even try something like this other than to see what happens.


  • @jsmith said:

    @RiX0R said:

    My shot:

    In CallMe a, the variable a is passed by reference as you would expect. In CallMe (a), (a) is first evaluated as an expression, because of the parentheses, then the result of that expression is passed by reference... yielding no change in the variable if the value is changed.

    Though why it would be legal to pass an expression by reference, I don't know.


    Bingo.  (a) is seen as an expression, so it cannot be successfully passed by reference.  I can answer your why question too -- backwards compatibility.  In VB6 and earlier, there is only one rule for using parentheses on calls:



    I think it is a little bit different the expression can be passed by reference but is something else then teh variable a. So while the expression changes value a is left unchanged so two times 6  is printed to the screen. I must say that with a ridiculous rule like that you're making life for the programmer extremely difficult.
    One of the biggest WTF's of VB <.net in my opinion is that de default parameter passing is byRef.




  • @Waterfall008 said:

    @dhromed said:
    @foobar2005 said:
    I want to say 'First!', but then I'd be like those other loosers


    Too late.


    I hae no idea.
    (a) doesn't get passed as argument while a does?
    Parentheses on an int have special meaning?




    Actually, it's quite complicated. I found a blog posting about it here:


    Actually, as that post would attest, it is quite simple -- once you understand the logic of the rules.  These rules are convoluted and unintuitive, and therefore its a good thing that they have been disposed of in VB.NET, but still quite simple.  I must say I wasn't aware of some of the rules, like the one about dereferencing variables, but I had always avoided the messy "inconsistencies" (as I thought of them), by just using Call for every subroutine call, and return value assignments for every function call.

        dZ.


Log in to reply