Function overloading
BlitzMax Forums/BlitzMax Programming/Function overloading
| ||
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. |
| ||
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 |
| ||
yeah |
| ||
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. |
| ||
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.. |
| ||
The original code is missing () on the functions anyway What code? both examples (EdzUp's and mine) are valid as pseudo code. |
| ||
yup |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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 |
| ||
C# has a good system for Get and Set Methodspublic 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. |
| ||
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) |
| ||
Which is pretty much how brucey's wxmax mod is designed, extended types for overloading methods, like OnInit() etc. |
| ||
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 |
| ||
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. |
| ||
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. |
| ||
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() |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
We should explictly cast anyway tho.... |
| ||
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. |
| ||
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. |
| ||
How about when calling functions you can tell the compiler which one to use by calling it like this Oh you mean explicitly casting ;) |
| ||
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. |
| ||
There is none especially with reflection that would allow invoke(functionname + returnType, data) |
| ||
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 TypeSimple and clean... |
| ||
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. |
| ||
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. |
| ||
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 |
| ||
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 :/ |
| ||
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 |
| ||
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. |
| ||
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. |
| ||
Damn, this would be handy. |
| ||
Here's a good way that's super efficientType MyType Field name:String End Type .. type.name = "bob" print type.name :) |
| ||
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() |
| ||
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 |