Function overloading

BlitzMax Forums/BlitzMax Programming/Function overloading

*(Posted 2008) [#1]
will we ever see function overloading in BlitzMax, it would be very useful IMHO. For those that dont know it allows you to have the same function return loads of different types. An example would be:
Function PlayerInformation:Int()
   return 3
End Function

Function PlayerInformation:String()
   return "Playername"
End Function


Both functions have the same name just different parameters, this would be an overloaded function and would be massively helpful in coding some systems in max. Another approach would be a extra variable type var as an example:

Function ReturnInformation:Var( operator:Int )
   Select Operator
   case 0
      return 1
   case 1
      return "PlayerName"
   end Select
end function


All are do-able and would be majorly helpful IMHO.


plash(Posted 2008) [#2]
Doesn't function overloading typically allow parameter changes too? like so:
Function PlayerInformation:Int(a:Int, b:Int)
   return a + b
End Function

Function PlayerInformation:String(name:String)
   return "Hello " + name + "!"
End Function



*(Posted 2008) [#3]
yeah


Torrente(Posted 2008) [#4]
I didn't know Function Overloading allowed different return types for functions with the same number (and type) of arguments. To be honest, I don't really even like that idea.

What if you just called PlayerInformation(), without setting a variable equal to the value returned -- which one would it call?

I would like to see the function overloading that I was familiar with -- different parameters for the same function. Also, Plash's example would work better in my opinion, because the different parameters would dictate the return type.


Czar Flavius(Posted 2008) [#5]
I didn't know Function Overloading allowed different return types for functions with the same number (and type) of arguments.


I didn't think you could do that either. The original code is missing () on the functions anyway, so I think there's been a typo somewhere..


plash(Posted 2008) [#6]
The original code is missing () on the functions anyway


What code? both examples (EdzUp's and mine) are valid as pseudo code.


*(Posted 2008) [#7]
yup


Who was John Galt?(Posted 2008) [#8]
Well C doesn't allow this type of overloading, does it? It's possible but not a great idea IMHO. Overloading for different calling parameters would be both useful and easy to implement.


*(Posted 2008) [#9]
I agree it would be nice if it was in there as it would mean we didnt need to write loads of functions to just return values and would make code easier to read.


Dreamora(Posted 2008) [#10]
no it wouldn't be easier to read as it would become impossible to tell from the function name what it does when it takes totally different things for the same purpose. Thats a thing that C thinks of as beeing nice but its actually about the only major OO language allowing type unsafe overloading.


Perturbatio(Posted 2008) [#11]
Overloading return types I'm not sure I would need, but overloading parameters is a definite thumbs up for me.

Along with read and write methods for fields like object pascal has.

something like:

Type myType
	
	Field name:String Read getName write setName
	
	Method getName()
		return name
	End Method
	
	Method setName(value:String)
		If value.contains("trainers") Then
			RuntimeError("Sorry, no trainers allowed")
		Else
			Name = value
		EndIf
	End Method
	
End Type




plash(Posted 2008) [#12]
C# has a good system for Get and Set Methods
public class PropertyHolder
{
    private int someProperty = 0;

    public int SomeProperty
    {
        get
       {
            return someProperty;
        }
        set
       {
            someProperty = value;
        }
    }
}

Using that system you can also just use the set/get bits of it to signify write or read only variables.


Dreamora(Posted 2008) [#13]
you get-set as well as access restriction possibilities are about the most requested OO extensions for BM ( beside multiple inheritance / implementation *like java interface* and overloading. BM only supports undefine which happens when you declare the same field in an extended class which will "mask" the parents field and replace it with the new one)


plash(Posted 2008) [#14]
Which is pretty much how brucey's wxmax mod is designed, extended types for overloading methods, like OnInit() etc.


Russell(Posted 2008) [#15]
If both functions have the same parameters (none in the case of the examples above), how does the compiler know which one to use? I think at least one parameter would be required, and the type/number of parameter(s) would determine which function would be used. Right?

Russell


Czar Flavius(Posted 2008) [#16]
Overloading lets you have functions with the same name, but different numbers of parameters, or parameters of different types. It can be a convienience, but doesn't allow you to do anything you couldn't anyway. The compiler can work out which function is which from the types of the variables sent as parameters, and compiles. There's no 'magic' post-compiling.

Function PlayerInformation:Int()
   return 3
End Function

Function PlayerInformation:String()
   return "Playername"
End Function


Possible this could work if the function is being stored into a variable of known type, but as stated above how can the compiler know for sure which function to run. It doesn't seem a very tidy solution. The work around is to simply name the functions differently... PlayerInformationString and PlayerInformationInt or a shorter, more convienient name.

Functions which can return different types (int or string for example) would require functions to work differently during program execution, and this is hard to implement in a compiled language. It is possible in Javascript and PHP, interperated languages.

A work around could be... returning numbers as strings ("1") for some functions. Or returning a custom Type containing as fields the possible types, with some indicator of the desired type. Not very elegant. but should work.


*(Posted 2008) [#17]
I think it would be useful when you have a system that has loads of different data to return, multiple functions dont seem as neat as function overloading would.

The programmer would be the one to decide what was being returned and read at the other end.


nawi(Posted 2008) [#18]
It could work by defining the return type of the function when you use it, in this instance:

Print PlayerInformation:String()
Local a:Int = PlayerInformation:Int()



plash(Posted 2008) [#19]
That doesn't work with other objects, you can't get an integer from a function that returns a non standard Object (TList, TMap, TMyType etc..)

EDIT: Using what you posted would not allow different return values.


Dreamora(Posted 2008) [#20]
you could just return :object and send integers as strings back and the like.
that definitely is the least problem.

but if you have such a function like that one especially your code is a mess ... if you can have "anything" returned from it, how is the correct work of your program guaranteed? Hope definitely isn't an acceptable part of software development sorry.


Gabriel(Posted 2008) [#21]
Don't know if anyone has mentioned this, but the reason we don't have overloading is because BlitzMax is still too beginner friendly. In C# for example, you can't start passing ints as floats and vice versa, but in BlitzMax you can. C# requires all type changes are explicit casts, but BlitzMax allows implicit casting all over the place. ( Much to my annoyance, actually. )

If you had something like this :

Type MyType
   Function Create(Param1:Float,Param2:Float)
   End Function

   Function Create(Param1:Int,Param2:Int)
   End Function

   Function Create(Param1:Double, Param2:Double)
   End Function

End Type

Now if you call MyType.Create(1,1) then BlitzMax has no clue which function you just called, because 1 is valid as an integer, float and double. You can specify the type of a constant value, but you're not required to.

So essentially, BlitzMax is doing it all backwards. When you call that function, it looks at the function prototype and then tries to cast your parameters to match the function prototype. But with overloading, it can't look at the function prototype because there are several.

So it would require a complete rewrite of the way BlitzMax handles variable casting, and it would require ditching the noob-friendly attitude of implicitly casting everything.

In short, much as I would like to, I don't see Mark doing that. Heavens, there are enough people whining about BlitzMax trying to become a more professional language as it is.


H&K(Posted 2008) [#22]
We should explictly cast anyway tho....


Gabriel(Posted 2008) [#23]
We should explicitly cast, yes, to avoid unexplained errors where implicit casts are changing the result, but until BlitzMax enforces explicit casting, it won't change the situation with regard to overloading.


plash(Posted 2008) [#24]
How about when calling functions you can tell the compiler which one to use by calling it like this:
MyType.Create(12:Float, 0.12) 'first parameter is not a float so you can tell it to convert to a float, second is a float so the compiler should know already which one this refers to
MyType.Create(12:Int, 2.4:int) 'first parameter is an integer so the :int part is not required, second parameter gets rounded


EDIT: I just realized what I posted probably didn't make too much sense. Basically you can just tell the compiler which one to call by just putting :ObjectType (:String/:Int/:Float/:Object etc) after each variable.


H&K(Posted 2008) [#25]
How about when calling functions you can tell the compiler which one to use by calling it like this
Oh you mean explicitly casting ;)


Czar Flavius(Posted 2008) [#26]
It could work by defining the return type of the function when you use it, in this instance:

Print PlayerInformation:String()
Local a:Int = PlayerInformation:Int()




For all intents and purposes, how is that different to :
Print PlayerInformationString()
Local a:Int = PlayerInformationInt()


...where the functions have same base name, but different by something that lets you know what type they are.


Dreamora(Posted 2008) [#27]
There is none especially with reflection that would allow

invoke(functionname + returnType, data)


Scaremonger(Posted 2008) [#28]
It's always bothered me why GET and a SET are coded seperately.

Type myType

Field name:String Read getName write setName

Method getName()
return name
End Method

Method setName(value:String)
If value.contains("trainers") Then
RuntimeError("Sorry, no trainers allowed")
Else
Name = value
EndIf
End Method

End Type


Sometimes it is required I agree, but I usually use a bit of code that I call a GETSET... Like this:
Type myType
Field _name:String

	Method name:String(value:String=Null)
	Local result:String = _name
		If value Then _name = value
	Return result
	End Method
	
End Type
Simple and clean...


Czar Flavius(Posted 2008) [#29]
Not sure why you have If indented. It's a nice idea, but you will always have an unused string somewhere whenever you use that method.


plash(Posted 2008) [#30]
you will always have an unused string somewhere whenever you use that method

Given the example he has shown, yes mytype._name is never used but it probably would be in a real world application.


Dreamora(Posted 2008) [#31]
you could turn it into the following if you are worried because of the unused string:

Type myType
Field _name:String

	Method name:String(value:String=Null)
		Local result:String = _name
		If value 
			_name = value
			return null
		endif
		Return result
	End Method
	
End Type


this would return null whenever you use it as setter


plash(Posted 2008) [#32]
or this:
Type myType

	Method name:String(value:String = Null)
	  Global getval:String
		If value
			getval = value
		  Return Null
		EndIf
		
	   Return getval
	End Method
	
End Type

Local mt:myType = New myType
	mt.name "test"
	Print mt.name()

The only problem without using a built in solution is you can't do name = "blahblahblah" etc, you always have to use it as a method/function :/


Kistjes(Posted 2008) [#33]
Whats wrong with:
Type MyType
	Field _name:String

	Method name:String(value:String = Null)
		If value Then _name = value
		Return _name
	End Method

End Type




Scaremonger(Posted 2008) [#34]
Kistjes: Your version is good as long as you never want to set and save the old value in one hit.

For example:

local oldvalue = myType.name( "newvalue" )
print oldvalue


It is very handy when you want to save a value temporarily and put it back the way it was later. Colour is a good example.

Also, the "unused" string is only unused if you never get the value. In which case it's pointless using this method which is by definition a GetSet.


Dreamora(Posted 2008) [#35]
Plash: Nice attempt. But it will not solve the issue with the not needed string reference that has to be collected if that was the idea of it (but it removes the need for the field in the type. Nice trick :) )

At least for the case of strings (not tested with object) it seems like NOT the string reference is given out but a clone of it. (varptr getval is different to varptr of the returned string which I stored in a different string object )

Scaremonger:
Well you won't want it to return the old one.
Whats the point of get / set if it does not what one expect:

Set: Sets the value
Get: Gets the current value

But as mentioned above, returning anything when you set means a lot of extra work for the GC when you use them more often as the object will always be returned and if you don't assign it, it means useless extra work for the GC to clean. With large strings a considerable issue.


plash(Posted 2008) [#36]
Damn, this would be handy.


Czar Flavius(Posted 2008) [#37]
Here's a good way that's super efficient
Type  MyType
	Field name:String
End Type
..

type.name = "bob"
print type.name

:)


plash(Posted 2008) [#38]
Do you not understand how get/set was intended to be used? or are you just being sarcastic?

The purpose of the get/set bits of a field is used for playing around with the value thats coming in or doing something else along with setting or getting the value.

example:
Type myType

	Field otherfield:Float
		
		Method dostuff(fab:String, par:String)
			Print fab + " is being " + par
			'... do some other stuff
		End Method
		
		Method name:String(value:String = Null)
		  Global getval:String
			
			'set
			If value
				otherfield = 0.0002
				dostuff("name", "set!")
				getval = value + otherfield
			   Return Null
			EndIf
			
			'get
			dostuff("name", "requested!")
			otherfield = RndFloat()
		   Return getval
		End Method
		
End Type

Local mt:myType = New myType
	mt.name "test"
	Print mt.name()



Russell(Posted 2008) [#39]
True, if you just change the value of a field directly like Czar Flavius says, no "action" is taken after the change because the functions or methods within the type are called after the change. In this case, it is better to have a set/get method/function that does the change, updates whatever it needs to and can also do bounds checking on the parameter as an extra precaution.

That being said, there are cases where Czar's example is valid and basically safe. ;)

Russell