Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the "Call" keyword required for methods of classes that take parameters?

Tags:

excel

vba

My class has multiple methods. Some methods take parameters. Those methods require the keyword "Call" to work. Why?

MyObject.MyMethod   'Works just fine without Call.

Call MyObject.MyMethod(arg1, arg2)  'Requires Call. Won't compile without it.
like image 844
PBeezy Avatar asked Dec 18 '22 18:12

PBeezy


2 Answers

Call is never required. In fact it's been obsolete since the advent of implicit call syntax in... Visual Basic 4.0, if I recall correctly.

object.MemberName arg1, arg2

Notice the parentheses are gone. This would be illegal, as you noted:

object.MemberName (arg1, arg2)

Notice the whitespace between MemberName and the opening (: the VBE will always put a space there. That's the VBE saying "Ok so I'll take what's in these parentheses, evaluate that as an expression, and pass the result by value (regardless of the member's signature specifying ByRef, explicitly or not) to the member you're invoking". Except (foo, bar) isn't a legal expression, so the code doesn't compile.

When MemberName returns a value that the caller needs to capture, the parentheses behave differently:

foo = object.MemberName(arg1, arg2)

Notice there's no whitespace anymore; the VBE will remove it if you add any.

So with a more familiar and concrete example - note that it has nothing to do with objects & members:

MsgBox "Yes?", vbYesNo ' works fine
Call MsgBox("Yes?", vbYesNo) ' works fine
Call VBA.Interaction.MsgBox("Yes?", vbYesNo) ' works fine
MsgBox ("Yes?", vbYesNo) ' illegal
result = VBA.Interaction.MsgBox("Yes?", vbYesNo) ' works fine

The Call keyword only exists in VBA6/VBA7 for backward-compatibility purposes, like many, many other language constructs (While...Wend comes to mind). That's why Rubberduck (disclaimer: I'm one of the admins of that open-source project) flags its use as "obsolete", and provides tooling to remove it.

like image 156
Mathieu Guindon Avatar answered May 25 '23 01:05

Mathieu Guindon


@MathieuGuindon's answer covers the how part really well, so I thought I'd tackle the "why" part.

VB's syntax overloads the parentheses as a syntactical element. In the case of a procedure call, they are used to surround the argument list:

 argument-list = [positional-or-named-argument-list] 
 positional-or-named-argument-list = *(positional-argument ",") required-positional-argument 
 positional-or-named-argument-list =/   *(positional-argument ",") named-argument-list 
 named-argument-list = named-argument *("," named-argument)

Note that definition for an Argument List (5.6.13.1) falls under the category of Index Expressions (5.6.13), and this isn't simply a misnomer, because the parser has to statically determine what is being indexed. This is complicated by the fact that () is also used for array indexing, and indexed member indexing.

The Call statement itself (5.4.2.1) consumes an argument list, but the fact that the parentheses can also be used as an indexer for a variable (arrays, etc.) means that there is the possibility that the expression to the right of Call can be ambiguous without the following rule:

  • If the Call keyword is specified:
    • If a <call-statement> element’s referenced expression is an <index-expression>, the specified argument list is this expression’s argument list.
    • Otherwise, the specified argument list is an empty argument list.

Forcing the use of the parentheses eliminates this ambiguity. One example would be something like this:

Sub Foo()
    Dim Bar As Object
    Call Bar 1  '<-- What is Bar here? Is this the default member of the object, or the Sub?
    Bar 1  '<-- This in unambiguous, because ( ) are required to use a default member indexer.
End Sub

Sub Bar(paramater As Integer)
    'Do something
End Sub
like image 27
Comintern Avatar answered May 25 '23 00:05

Comintern