Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call keyword - deprecated or not

Tags:

vba

enter image description here

I have seen over the time people mentioning that the Call Statement is deprecated, but I can't seem to find anything official to support this claim.

On the documentation page, there was a pretty recent update to the page (12/03/2018), so can't really say is outdated information.

I've been using Call in my code, most of the time simply because I find this:

Call function(arg1, arg2, arg3) to be more readable than function arg1, arg2, arg3

Now for the question, can anyone provide some insight into why I shouldn't use the Call statement anymore? If you do say is deprecated, please do provide a link to a resource.

If this question is against the site rules, please do let me know, I`ll happily delete it myself, though would be nice to get answer.

like image 798
FAB Avatar asked Jun 08 '19 08:06

FAB


3 Answers

Consistency is King

Whether you're calling a Sub or a Function makes no difference whatsoever. What matters is consistency, and that's what Call-lovers claim using the keyword brings. Consistency with whether or not parentheses are required around the argument list.

So instead of this simple-as-it-gets implicit call statement:

MsgBox "I'm a function but you're discarding my return value, so no parentheses."

We get things like this:

MsgBox ("I'm a function but you're discarding my return value, and this call looks weird.")

And I've yet to see the Call actually being used with any kind of actual consistency:

Call MsgBox("I'm a function but you're discarding my return value, so I have a Call keyword.")
Call MyProcedure(42)
Call ActiveSheet.Range("A1:A10").Clear
Call Beep
Call CallByName(obj, "SomeMethod", VbCalType.VbMethod)

Used consistently, Call quickly becomes obnoxious, clearly redundant, and slows down reading, because the eyes can't help but stop on the keyword, and then the brain goes "oh hey watch out, we're calling something here". I suppose at one point you just stop seeing it, and just feel like something's off if it's missing.

The overwhelming majority of every single executable statement is going to be a call to something, somewhere, at some abstraction level - using Call consistently makes the language even more heavily bulky than it already is.

Unless Call isn't really about consistency, more about being able to easily see my own user procedure calls... which at this point is just grasping at straws to legitimize an archaic construct that serves no purpose whatsoever: there is no legitimate use for the Call keyword.

This is the only "legit" use case:

Public Sub Test()
DoSomething: DoSomethingElse
'vs.
'Call DoSomething: Call DoSomethingElse
End Sub

Private Sub DoSomething() '<~ dead code. can you see why?
End Sub

Private Sub DoSomethingElse()
End Sub

The Call keyword disambiguates a LineLabel: from a ParameterlessProcedureCall followed by an : instruction separator. Only problem is, : instruction separators are great for golfing and cramming as much executable code as possible on a single line of code ...they are terrible for readability. Besides everybody agrees that a NEWLINE should follow a ; in any semicolon-terminated language, even though it makes perfect sense for the compiler to ignore the line jump.

We write code for people to read and maintain, not just for compilers to build and run.


Deprecated? Says who?

Says me and my ducky.

I'm 100% certain I've read in the Official docs at one point in my life, that the keyword was obsolete. Heck, it's even specified as being redundant. Languages evolve, VBA is no exception - even with mind-blowingly incredible levels backward-compatibility and after two decades without any kind of significant changes, the language - or rather, its practices are still fluid, even if its maintainers have retired vowed to just let it be.

Call isn't the only deprecated token in VBA, but for questionable subjective reasons not unsimilar to what people use to justify clinging to the formidable mistake that Systems Hungarian notation was in this century, its roots run deep.

Where are the defenders of the Rem comment marker? Why is While...Wend still a thing when Do While...Loop supersedes it? Is anyone raising errors with the Error statement rather than through Err.Raise? Outputting error messages with Error$ rather than through Err.Description? Declaring variable types with the ever-so-legible $&%^!# type hints? Who writes On Local Error? Why use Global for things that are really Public?

And if explicit Call isn't obsolete and "makes the code more readable", then why aren't the same people using explicit Let statements for value assignments for the exact same explicitness/clarity reasons?

I think it's well past time to rewrite what the best practices are, and leave Call in the past, along with Let, Hungarian Notation, and walls-of-declarations at the top of procedures. Every single programming community did this already - only the VBA community is still holding on to the "best practices" from 30 years ago, for brand new code, not just legacy stuff written in another era. I suspect it's very much possible that VBA's "dread score" has a lot to do with that, even without taking the bare-bones IDE into account.

I'd love to be able to pull a reference from fellow Microsoft MVP old-timers Rob Bovey and Stephen Bullen, and say "here, see, page 172 it says the call keyword is obsolete" (this answer seems to mention a "two inch thick book on VBA basically says don't use it unless you want to use the Find feature of the VBE to easily find calls in large projects"), so this might be it, but in any case at the time these gurus essentially defined what the best practices were, "refactoring" was a foreign word, "unit testing" was an extreme programming crazy idea - the entire programming landscape has enormously evolved in the past 20 years, but VBA stood still. Time to end this, and move on forward.

Speaking of moving forward...

"It makes it easier to migrate to VB.NET"

Allegedly, using Call statements make it "easier" to port the code to .NET, because VB.NET will want the parentheses everywhere. Respectfully, that's bullocks. First because what you'll want to port your VBA code to isn't VB.NET but much more likely TypeScript, if any kind of porting is ever actually going to happen. If you're manually copying VBA code and "translating it" to get it to compile in the target language, you'll quickly find that not having to deal with parentheses is a very marginal perk, since literally everything else needs to be rewritten, ...including dropping the Call tokens.

Writing small, specialized procedures that do very few things if ever more than a single one, leveraging abstraction levels and classes/objects, reducing coupling between modules, improving cohesion within modules, having your code's behavior covered and documented with thorough unit tests, that will help you port your VBA code faster, and ensure the ported code works identically. Call and parentheses are just an excuse to keep a bad habit going.

like image 179
Mathieu Guindon Avatar answered Oct 22 '22 07:10

Mathieu Guindon


I try to avoid the Call (and thus, it is depreciated for me) for the following reason - in #VBA I consider passing a variable in parenthesis as a way to overrun the standard ByVal/ByRef specification of the parameters. What do I mean? Consider this example:

Public Sub TestMe()

    Dim var1 As Long: var1 = 1
    Dim var2 As Long: var2 = 1

    IncrementByVal (var1)
    IncrementByRef (var2)

    Debug.Print var1, var2

End Sub

Public Function IncrementByVal(ByVal a As Variant) As Variant
    a = a + 100
    IncrementByVal = a
End Function

Public Function IncrementByRef(ByRef a As Variant) As Variant
    a = a + 100
    IncrementByRef= a
End Function

As you probably see, both var1 and var2 return 1, while the var2 should be 101, as far as it is ByRef. The Call-word kind-of improves this "feature" in VBA, but it becomes too complicated to remember when the parenthesis are overriding the ByRef and when not, when reading code. Thus, having 3 cases is quite a lot:

Public Sub TestMe()

    Dim var1 As Long: var1 = 1
    Dim var2 As Long: var2 = 1
    Dim var3 As Long: var3 = 1
    Dim var4 As Long: var4 = 1
    Dim var5 As Long: var5 = 1
    Dim var6 As Long: var6 = 1

    IncrementByVal (var1)           '1
    IncrementByRef (var2)           '1
    IncrementByVal var3             '1
    IncrementByRef var4             '101
    Call IncrementByVal(var5)       '1
    Call IncrementByRef(var6)       '101

    Debug.Print var1, var2
    Debug.Print var3, var4
    Debug.Print var5, var6

End Sub

Public Function IncrementByVal(ByVal a As Variant) As Variant
    a = a + 100
    IncrementByVal = a
End Function

Public Function IncrementByRef(ByRef a As Variant) As Variant
    a = a + 100
    IncrementByRef = a
End Function
like image 21
Vityata Avatar answered Oct 22 '22 07:10

Vityata


I frequently use Call when I'm refactoring code or cutting new code I'm not yet sure of. To explain, using Call requires brackets around the arguments and so does returning a value from a function. I might want to return a value from a function, or I might want to pass an argument by reference (ByRef)

Sub Test()
    Dim v

    '* Call requires brackets
    Call NotSureIfThisWillEndUpAsASubOrAFunction(1, 2)

    '* return a value from a Function then need brackets
    v = NotSureIfThisWillEndUpAsASubOrAFunction(1, 2)

    '* if always a sub then no brackets required
    WillAlwaysBeASub 4, 5

    '* but is this really so ugly, why deprecate this?
    Call WillAlwaysBeASub(4, 5)

End Sub

Function NotSureIfThisWillEndUpAsASubOrAFunction(a, b)

End Function

Sub WillAlwaysBeASub(c, d)

End Sub

EDIT: I think using brackets all the time (which means using Call as a keyword for Subs) means less time hopping around the code taking brackets out and then putting them back in upon change of mind.

like image 36
S Meaden Avatar answered Oct 22 '22 08:10

S Meaden