What are the rules governing usage of parenthesis in VBA function calls?
Asked Answered
S

8

43

I've just had an irritating 30 minutes on a "compiler error" in VBA (Access 2003) caused by my use of parenthesis around the arguments I'm passing to a Sub I defined.

I've been searching to find a decent article/tutorial/instruction as to when parenthesis are necessary/appropriate/inappropriate/forbidden, but can't find any clear guidelines.

Sublapsarianism answered 24/3, 2011 at 1:34 Comment(2)
Here's my favorite post on this topic: dailydoseofexcel.com/archives/2012/05/01/…Unwept
To be clear, you're asking about parenthesis () not brackets [].Brig
T
25

From Here:

Using the VBScript Call Statement to Call a Subroutine The use of Call statement is optional when you wish to call a subroutine. The purpose of the Call statement when used with a Sub is to allow you to enclose the argument list in parentheses. However, if a subroutine does not pass any arguments, then you still should not use parentheses when calling a Sub using the Call statement.

Call MySubroutine

If a subroutine has arguments, you must use parentheses when using the Call statement. If there is more than one argument, you must separate the arguments with commas.

Call MySubroutine(intUsageFee, intTimeInHours, "DevGuru") 

Calling the Function There are two possible ways to call a function. You may either call the function directly, by name only, or you may call it by using the VBScript Call statement.

Calling a Function by Name When calling a function directly by name and when there is no assignment to a returned value, all of the following are legal syntax:

MyFunction
MyFunction()
MyFunction intUsageFee, intTimeInHours, "DevGuru"

If you want a returned value, you can assign the function to a variable. Note that if there is one or more arguments, you must use the parentheses.

returnval = MyFunction
returnval = MyFunction()
returnval = MyFunction(intUsageFee, intTimeInHours, "DevGuru") 
Terrorstricken answered 24/3, 2011 at 2:5 Comment(1)
Thanks - it looks like my problem was because my function is not returning a value, but I was still using brackets around my argument list. This seems a rather odd syntax decision...Sublapsarianism
A
81

There is perfect logic to the Parentheses Rule in VB(A), and it goes like this.

If a procedure (function or sub) is called with arguments, and the call is on a line with other statements or keywords, the arguments must be enclosed in parentheses. This to distinguish the arguments belonging to the procedure call from the rest of the line. So:

1:   If CheckConditions(A, B, C) = DONT_PROCEED Then Exit Sub

is a valid line; the call to CheckConditions needs the parentheses to indicate what other bits of the line are its arguments. Conversely, this would produce a syntax error:

2:   If CheckConditions A, B, C = DONT_PROCEED Then Exit Sub

Because it is impossible to parse.

With a procedure call as the only statement on the line, parentheses aren't needed because it is clear that the arguments belong to the procedure call:

3:   SaveNewValues Value1, Value2, Value3

While this results in a syntax error (for sound reasons discussed below):

4:   SaveNewValues(Value1, Value2, Value3)

To avoid confusion about parentheses or no parentheses (in fact, to avoid the Parentheses Rule entirely), it is always a good idea to use the Call keyword for calls like these; that ensures that the procedure call is not the only statement on the line, thus requiring parentheses:

5:   Call SaveNewValues(Value1, Value2, Value3)

So if you get in the habit of preceding self-contained procedure calls with the Call keyword, you can forget the Parentheses Rule, because you can then always enclose your arguments in parentheses.

The matter is confused by the additional role parentheses play in VB(A) (and many other languages): they also indicate evaluation precedence for expressions. If you use parentheses in any other context but to enclose procedure call arguments, VB(A) will attempt to evaluate the expression in the parentheses to a resulting simple value.

Thus, in example 4, where parentheses are illegal for enclosing the arguments, VB(A) will instead attempt to evaluate the expression in the parentheses. Since (Value1, Value 2, Value3) is not an expression that can be evaluated, a syntax error ensues.

This also explains why calls with a variable passed ByRef act as if called ByVal if the argument is enclosed in parentheses. In the example above, where function p is called with ByRef parameter a, there is a big difference between these two calls to p:

6:  p a

And

7:  p(a)

As discussed above, 6 is the correct syntax: the call is alone on its line, so parentheses should not be used to enclose the arguments.

In 7, the argument is enclosed in parentheses anyway, prompting VB(A) to evaluate the enclosed expression to a simple value. Which of course is the very definition of passing ByVal. The parentheses ensure that instead of a pointer to a, the value of a is passed, and a is left unmodified.

This also explains why the parentheses rule doesn't always seem to hold sway. Clearest example is a MsgBox call:

8:  MsgBox "Hello World!"

And

9:  MsgBox ("Hello World!")

Are both correct, even though the parentheses rule dictates that 9 should be wrong. It is, of course, but all that happens is that VB(A) evaluates the expression in the parentheses. And the string literal evaluates to the exact same string literal, so that the actual call made is 8. In other words: calls to single-argument procedures with constant or string literal arguments have the identical result with or without parentheses. (This is why even my MsgBox calls are preceded by the Call keyword.)

Finally, this explains odd Type Mismatch errors and weird behavior when passing Object arguments. Let's say your application has a HighlightContent procedure that takes a TextBox as argument (and, you'll never guess, highlights it contents). You call this to select all text in the textbox. You can call this procedure in three syntactically correct ways:

10: HighlightContent txtName
11: HighlightContent (txtName)
12: Call HighlightContent(txtName)

Let's say your user has entered "John" in the textbox and your application calls HighlightContent. What will happen, which call will work?

10 and 12 are correct; the name John will be highlighted in the textbox. But 11 is syntactically correct, but will result in a compile or runtime error. Why? Because the parentheses are out of place. That will prompt VB(A) to attempt an evaluation of the expression in the parentheses. And the result of the evaluation of an object will most often be the value of its default property; .Text, in this case. So calling the procedure like 11 will not pass the TextBox object to the procedure, but a string value "John". Resulting in a Type Mismatch.

Alainealair answered 20/3, 2013 at 8:59 Comment(7)
+1 for a great answer, but I still don't agree that the parenthesis rule is "perfectly logical"... I can't imagine a clumsier way to handle something simple like parentheses!Bartie
What about when there are 'dots'? (feel free to correct my terminology) myCollection.add obj AND myCollection.item(obj) Aren't these both the correct ways to do this? But the parenthesis rules are different, and I don't know why.Chadwell
Thorough answer to something I've been puzzled about for a while. It still seems a little goofy. Other languages don't have any problem parsing function calls with parentheses and no "call" keyword. But now that I know the rules I won't be wasting time trying to figure out WTFITMWTSL!, Thanks for the help. B^JEnhanced
Call Debug.Print("Hello world") still raises error. Where's the logic behind that?Choroid
This answer was amazing. Though the comment section left me with even more questions. If anyone finds answers to comments kindly link them hereDonley
@Microsoft, do yourself a favor and redirect your vba/language/concepts/getting-started/using-parentheses-in-code to here.Klump
The advice to always use Call is terrible advice. This answer explains whyVinosity
T
25

From Here:

Using the VBScript Call Statement to Call a Subroutine The use of Call statement is optional when you wish to call a subroutine. The purpose of the Call statement when used with a Sub is to allow you to enclose the argument list in parentheses. However, if a subroutine does not pass any arguments, then you still should not use parentheses when calling a Sub using the Call statement.

Call MySubroutine

If a subroutine has arguments, you must use parentheses when using the Call statement. If there is more than one argument, you must separate the arguments with commas.

Call MySubroutine(intUsageFee, intTimeInHours, "DevGuru") 

Calling the Function There are two possible ways to call a function. You may either call the function directly, by name only, or you may call it by using the VBScript Call statement.

Calling a Function by Name When calling a function directly by name and when there is no assignment to a returned value, all of the following are legal syntax:

MyFunction
MyFunction()
MyFunction intUsageFee, intTimeInHours, "DevGuru"

If you want a returned value, you can assign the function to a variable. Note that if there is one or more arguments, you must use the parentheses.

returnval = MyFunction
returnval = MyFunction()
returnval = MyFunction(intUsageFee, intTimeInHours, "DevGuru") 
Terrorstricken answered 24/3, 2011 at 2:5 Comment(1)
Thanks - it looks like my problem was because my function is not returning a value, but I was still using brackets around my argument list. This seems a rather odd syntax decision...Sublapsarianism
R
7

I just found some weird behavior calling a function with / without parentheses. Google took me here.

sub test()
  dim a as double
  a = 1#
  p(a) 'this won't change a's value
  Debug.Print a '1
  p a  ' this is expected behavior
  Debug.Print a '2
  Call p(a) 'this is also valid
  Debug.Print a '3
end sub

Function p(a as Double) 'default is byref
  a = a + 1
end function

My conclusion is that you have to use either Call or omitting the parentheses when calling a function with only one parameter, otherwise the parameter isn't passed by reference (it's still get called, as I checked already).

Refract answered 30/10, 2012 at 18:0 Comment(1)
The parenthesis do indeed force an argument to be passed ByVal.Disputable
G
5

I just spent 10 minutes figuring out an "types incompatible" exception while calling a Sub which takes 1 argument via

CallMe(argument)

As it turns out, this is invalid, googling lead me here and finally

Call CallMe(argument)

or

CallMe argument

did the trick. So you must not use the brackets when calling a sub without the call-statement which only takes 1 argument.

Grimonia answered 13/4, 2012 at 8:53 Comment(1)
+_1 for the Sub nameBrom
D
3

When you use Call MySub you should use parentheses around parameters, but if you omit Call, you don't need parentheses.

Disdainful answered 24/3, 2011 at 2:6 Comment(0)
S
2

1 - By default, do not use parentheses when calling procedures or functions:

MsgBox "Hello World"

2 - If you are calling a function, and are interested in its result, then you must enclose its arguments with parentheses:

Dim s As String
Dim l As Long 
s = "Hello World"
l = Len(s)

3 - If you want to use the call keyword with a procedure, then you must enclose the arguments with parentheses (e.g. when you want to assign the result in a variable or to use the function in an expression):

Call MsgBox("Hello World")

4 - If you want to force a ByRef argument (the default) to be passed ByVal, then enclose the ByRef argument with parentheses:

Sub Test
  Dim text As String
  text = "Hello World"

  ChangeArgument((text))

  MsgBox text
End Sub

Sub ChangeArgument(ByRef s As String)
    s = "Changed"
End Sub

This displays "Hello World"

Specialist answered 24/9, 2014 at 10:16 Comment(0)
C
-1

I use another logic to differ when to use brackets or not. If you function doesn't return a value (void type in C-liked languages), you don't need the parentheses. And it is always true for subs because returning value is the main difference between sub and function. Otherwise you have to use parentheses.

Cambell answered 5/3, 2021 at 5:2 Comment(0)
I
-1

Well this was asked long ago but I just faced this problem and I found this question which I feel hasn't been fully answered yet. Hope I shed some light over this issue so that it serves for newcomers.

As I have seen previous answers mainly focus on the fact that whenever you use the "Call" statement you must enclose the arguments within parenthesis. Although this is true 1 it is definitely not the main source triggering this "strange" syntax errors.

The key point has been briefly noted by Cristopher. I'll just reference the documentation and further explain a little.

Ref Docs 2

So the main point is that the parenthesis determine whether you are interested in the return value of the function/sub/method/statement you are calling or not, that is, whether it must be returned to store it on a variable or not.

Having said that one may run into several problems

  • Calling with parenthesis a procedure that doesn't return a value 3.
Sub no_value_return(x as Integer)
   Dim dummy as Integer
   dummy = x
End Sub

'Error
no_value_return(1)

'No error
no_value_return 1
  • Calling with parenthesis a procedure that returns a value but not assigning it to a variable
Function value_return(ByVal x as Integer)
   Dim value_return as Integer
   value_return = x*2
End Function

'Error: 
value_return(1)

'No error
Dim result as Integer
result = value_return(1)

Some additional examples

'Error - No value returned since no parenthesis were specified
Dim result as Integer
result = value_return 1

'No error - Special case
Dim result as Variant
result = value_return 1
'The reason for this is that variant is the only data type that accepts 
'the special value "Empty"

'No error - You can perfectly ignore the returned value even if it exists
value_return 1

1 https://learn.microsoft.com/en-us/office/vba/language/concepts/getting-started/calling-sub-and-function-procedures

2 https://learn.microsoft.com/en-us/office/vba/language/concepts/getting-started/using-parentheses-in-code

3 Note this isn't aplicable for function procedures or built-in functions since those must always return a value

Increasing answered 4/1, 2022 at 12:39 Comment(6)
return dummy - that isn't VBA. And a Sub never returns a value, only Functions. -- result = value_return 1 - this will never work, no matter the type of result. However editor isn't magical and sometimes it will lose track and won't apply the patch what does that even mean.Curran
Oops, you are right I mistyped the declaration of the return_value procedure, sorry for that. With the patch I mean prepending an space in front of the parenthesis, maybe the wording is a little bit confusing, should I change it?.Increasing
The whole concept is wrong. The editor doesn't "fix" anything, the parentheses change the evaluation of the parameter. It is all well explained in the answer by Floris Kleijne. -- The other issues in my comment still apply. Please try to actually run all example code that you wrote.Curran
Sorry just noticed you mention the return syntax was incorrect, changing it as well.Increasing
I actually don't know if the preprending of a parenthesis is documented behavior or not, but it is something I noticed while programming. Whenever I call a sub (procedure with no return) the editor automatically prepends an space. I have assumed this is done to preserve the user notation but avoiding at the same time a syntax error.Increasing
Alright just noticed I got confused of when the editor prepends the space I am deleting that part of the comment.Increasing

© 2022 - 2024 — McMap. All rights reserved.