Function Overloading

written by :stylin:

What is It?

Function overloading is as close as you can come to generic programming 
without having templates. In functional (or modular) programming, the 
emphasis is on value, while in generic programming, the emphasis is on type.
Similar functions are called based on the type of the argument passed. 
Function overloading is a side-step into generic programming, allowing a 
function identifier to be associated with a variety of functions that work 
with a variety of different types - and making it all transparent to the 
client (you).

Simply put, function overloading involves defining functions that have the 
same name, but different signatures. A function's signature is a 
combination of all the information needed to correctly reference the 
function, and includes the function's parameter list and return type. These 
are what we redefine, or overload. Let's start off with a small example. 
Say we need functions that output the string respresentation of a number. 
We simply write:

   #lang "fblite"
   Option Explicit      '' force explicit declaration of variables
   Option ByVal         '' default passing convention as by value

   '' to declare functions with similar functionality but that accept different argument types,
   '' we 'simply' create new function names :(
   Declare Function print_byte( As Byte )      '' outputs a stringified byte
   Declare Function print_short( As Short )    '' outputs a stringified short

   Dim As Byte b = 102
   Dim As Short s = 10240

   print_byte( b )
   print_short( s )

   Sleep : End 0

   '' function definitions squished for brevity - don't do this outside a space-constrained  tutorial ;}
   Function print_byte( n As Byte ) : Print Str( n ) : Return 0 : End Function
   Function print_short( n As Short ) : Print Str( n ) : Return 0 : End Function

What Does It Do For Me?

The problem here is that not only do we have two different function 
signatures, but we have two different function identifiers as well; we - 
not the compiler - have to remember both in order to call the right 
function. As you may be able to imagine, this can be pretty confusing if 
you decide you want to support INTEGERs, SINGLEs and DOUBLEs as well. Plus, 
for completeness, you may want to have functions that accept both the 
signed and unsigned versions of each of these. Clearly, you're going to 
have some kind of naming-scheme setup to make this easier on yourself. And, 
of course you'll want to support your own TYPEs as well, and - oh wait, we 
forgot about pointers. OK, now you'll need to double the list of function 
names you not only need to come up with, but also try and remember when 
you're actually writing code that uses these functions. Since, after all, 
you do have implicit conversions available to/forced upon you, and the 
compiler will happily let you slip a DOUBLE in to your print_integer 
function - woops! Bug-city, here we come! Surely there must be a better 
way?

There is, and don't call me Shirley. I mentioned before that the compiler 
uses two primary components to establish a function signature: the 
parameter list and the return type. I also mentioned that through 
overloading, we can define multiple functions with different signatures, 
and still keep the same function name for all of them. You may be thinking 
this is our way out of our dilema, convoluted name space and all. Well, 
you're right - check this out:

   #lang "fblite"
   Option Explicit      '' force explicit declaration of variables
   Option ByVal         '' default passing convention as by value

   '' to overload function print_numeric that we can redefine to accept different argument
   '' types while keeping the name intact, we use the OVERLOAD keyword on our intial function:
   Declare Function print_numeric Overload( As Byte )      '' outputs a stringified byte
   Declare Function print_numeric( As Short )              '' outputs a stringified short
   Declare Function print_numeric( As Integer )            '' outputs a stringified integer
   Declare Function print_numeric( As LongInt )            '' outputs a stringified longint

   '' define some variables
   Dim As Byte b = 102
   Dim As Short s = 10240
   Dim As Integer i = 1024000000
   Dim As LongInt li = 1024000000000000000

   '' enter the wonderful world of function overloading :)
   print_numeric( b )
   print_numeric( s )
   print_numeric( i )
   print_numeric( li )

   Sleep : End 0

   '' define our function overloads
   Function print_numeric( n As Byte ) : Print Str( n ) : Return 0 : End Function
   Function print_numeric( n As Short ) : Print Str( n ) : Return 0 : End Function
   Function print_numeric( n As Integer ) : Print Str( n ) : Return 0 : End Function
   Function print_numeric( n As LongInt ) : Print Str( n ) : Return 0 : End Function

What does It Mean?

One thing that should stand out right away is how incredibly easy it is to 
do this. That might seem strange considering the freedom, flexibility and 
type-safety if offers you, but then again most higher-level constructs are 
like that. In a nutshell, using methods like this will not only make your 
life a whole lot easier, but you'll be spending less time debugging, and 
that's a good thing no matter what kind of code you write.

It means flexibility. Function overloading offers the ability to add more 
features ( print_numeric( f as fraction) ) while still keeping your current 
code intact. Your code doesn't break because you want to support printing 
the numeric representation of a handkerchief, or armor, or whatever else. 
You may now be thinking that the above code is not so trivial anymore, and 
that what seems really simple - because it is - is really the foundation of 
writing better code. You'd be right.

It means maintainability: So you've got your 80 functions of 
print_some_long_name_you_need_to_look_up_everytime_you_want_to_use_it 
written and debugged. Everything's great in your little torturous, 
self-loathing world. What happens when something needs to change? Even if 
only 1 of those functions needs to change, BAM! A maintenence nightmare. 
You're going to have to search the entire code-base to be completely sure 
you haven't missed a function here or there; sad way to spend a Saturday 
night, my friend.

It means safety: You may notice that I utilize two OPTIONs in these 
examples: Option Explicit and Option ByVal. I'm big on safety, and I'm even 
bigger on having the compiler watch my back for me. I use these because it 
is safer to, and I'll take all the safety I can get. Function overloading 
also affords you safety - safety against evil (read: accidental) implicit 
conversions. Consider if we were actually returning a value from these 
functions that was dependent on the argument we passed to it. As above, if 
a double were allowed to get truncated without our knowledge, that spells 
many pills of excedrin trying to make that debugging headache go away. It's 
all about the type-safety, something which cause many to scoff at Cpp.

Wrapping Up

I hope you have learned at least the basics of function overloading (since 
that's all I covered). And I hope you start thinking about the themes I've 
brought up, if you haven't before. Next time I'll discuss overloading 
functions with different numbers of parameters, different return types, as 
well as the joys and pitfalls of both. Stay tuned.

Last reviewed by sancho3 on February 08, 2018