SUGGESTION: 'base' keyword

Started by monkey0506, Mon 09/08/2010 21:29:25

Previous topic - Next topic

monkey0506

Understandably with no formal support for polymorphism, the idea of a base keyword may not make much sense. However, as I have explained here, simple polymorphism in AGS is possible. Presently we cannot create pointers to custom struct types, so this means we cannot simply use a pointer to access base-level functions when using derived class functions (in a polymorphic sense). However, it is possible for the engine to create temporary pointers, namely the this keyword.

What I'm proposing is that for derived types the engine could also create a temporary pointer to the base class object.

I've composed a highly detailed list of pros and cons for this suggestion:

CONS:

-This suggestion furthers the usability of an unsupported functionality.
-CJ thinks it's a horrible, horrible, stupid and pointless idea*.

PROS:

-This suggestion furthers the usability of polymorphism in AGS, a very important OOP functionality.
-It is, quite frankly, awesome.
-general_knox was just about to suggest it.
-It would give rise to more "all your base" jokes.

Thanks for your consideration. :=

*EDITOR'S NOTE: Mostly sarcasm.

Knox

...I was going to propose the exact same thing*  ;D


*this line is a lie
--All that is necessary for evil to triumph is for good men to do nothing.

monkey0506

The examples at the end of the aforementioned article showcase the difficulties we are presently faced with in trying to access the base-level functions. Essentially without a base-class pointer we must create a new base-class object, load all relevant data, call the relevant function, and copy the relevant data from the new base-class object back into this derived-class object.

SSH

This would be great as it would allow more "all your base" jokes in AGS code.
12

monkey0506

Let's keep the positive reasoning pouring in here folks. :=

Monsieur OUXX

it would be the equivalent of the keyword "super"* in other languages?

* (not when it's meant to represent a method, like when you write "super()" but when it's meant to replace "this", like when you write "super.Foo()").
 

Wonkyth

"But with a ninja on your face, you live longer!"

monkey0506

#7
Quote from: Monsieur OUXX on Tue 10/08/2010 08:53:05it would be the equivalent of the keyword "super"* in other languages?

I'm not really familiar with that keyword, and I'm about to be without internet (my roommates are both going to work), but it would make the crappy example from that article:

Code: ags
 struct Stack2 extends Stack {
   // some new functions and whatnot
 };
 
 bool Push(this Stack2*, StackData data) { // note the parameter list does NOT have to be the same
   // do something
   Stack s;
   s.LoadFromStack(this.Copy()); // load this object into the temporary stack from the base Copy function
   bool success = s.Push(data, 0, false); // insert at front of stack
   this.LoadFromStack(s.Copy()); // load the temporary stack object into this object via the base LoadFromStack function
   return success;
 }


Look something more like this:

Code: ags
 struct Stack2 extends Stack {
   // some new functions and whatnot
 };
 
 bool Push(this Stack2*, StackData data) { // note the parameter list does NOT have to be the same
   // do something
   return base.Push(data, 0, false); // call the Stack::Push function on this object.
 }


So yes (I read up on it very briefly).

Pumaman

I think AGS should support OO polymorphism properly, rather than trying to hack around it with new keywords. However, obviously that's not a priority right now!

monkey0506

#9
Seeing as AGS doesn't support scope-resolution, presumably there would eventually be a base-style keyword eventually anyway, right? (EDITOR'S NOTE: Eventually eventually? I like it.)

I was just hoping that seeing as there's already a 'this' pointer it wouldn't be too much trouble to send along a base pointer as well..oh well. :'(

We tried knox..we tried.

Ryan Timothy B

Hey Monkey man. I honestly am a little lost with this base keyword suggestion and all.

I'm actually asking about the code you posted 3 posts above. I'm not familiar with all of AGS's limitations or even all of its capabilities. So I'm not sure if any of it is hypothetical coding to get your point across or if it's actually capable. (I should probably just test it myself, but I'd rather know that if it's possible, that I'm actually just not doing it right)

I want you to rub your big crazy haired head against mine--possibly cross contaminating each other with scalp organisms--and share some of that knowledge with me.

What exactly does the 'extends' do, if it's actually capable with AGS, otherwise I'm not allll that interested in knowing :P :
Code: ags
struct Stack2 extends Stack


Code: ags
this.LoadFromStack(s.Copy());

The LoadFromStack and Copy, are those real AGS functions too? Does it copy all elements of that struct index? Or are they functions that haven't been included in your example code's struct (I understand what it's doing, just not sure if it's an AGS function or not)?


Sorry for being a tard.

          ....and giving you head lice. :P

monkey0506

The extends keyword is already implemented in AGS and allows for simple inheritance. That means that you can have two types in which one is derived from the other.

For example, you could have a struct such as this to store data about a person:

Code: ags
struct Person {
  String Name;
  float Height;
  int Weight;
};


And then if you could define a new type for containing additional information about said person:

Code: ags
struct Employee extends Person {
  int EmployeeID;
  int HoursPerWeek;
  float HourlyWage;
};


Every instance of the Employee struct would also contain Name, Height, and Weight members in addition to their EmployeeID, HoursPerWeek, and HourlyWage. That is, every Employee would also be a Person, but not every Person would be an Employee.

Every property and function of the base type Person would be inherited by the derived type Employee.

This already is possible in AGS.

There is a workaround to a concept known as polymorphism thanks to the existence of extender methods in AGS. Polymorphism is the idea that you're going to have a function in a derived type which overrides a function in a base type by the same name.

So, you might have a Create function for a Person like this:

Code: ags
void Create(this Person*, String name, float height, int weight) {
  if ((String.IsNullOrEmpty(name)) || (height <= 0.0) || (weight < 0)) return;
  this.Name = name;
  this.Height = height;
  this.Weight = weight;
}


You wouldn't want to instanciate an Employee the same way as you would a Person though, so you might want the Employee function to look more like:

Code: ags
void Create(this Employee*, String name, float height, int weight, int employeeID, int hoursPerWeek, float hourlyWage) {
  if ((String.IsNullOrEmpty(name)) || (height <= 0.0) || (weight < 0) || (employeeID < 0) || (hoursPerWeek < 0) || (hourlyWage <= 0.0)) return;
  this.Name = name;
  this.Height = height;
  this.Weight = weight;
  this.EmployeeID = employeeID;
  this.HoursPerWeek = hoursPerWeek;
  this.HourlyWage = hourlyWage;
}


Ordinarily in AGS you couldn't do this because the two functions have the same name. If both functions were imported directly into the struct definition it wouldn't even compile. However, due to a sort of quirk in the implementation of extender methods, we actually can define both functions with the same name. One for the base type Person, and one for the derived type Employee. Since they both have the same name, this is a simple form of polymorphism.

However, you can clearly see that there's code duplication between the two functions. In higher-level programming languages it would be possible for the Employee.Create method to directly call the Person.Create method whilst still referencing the same object ('this'). Since AGS's implementation of polymorphism is not "by design" but more a happy sort of accident, there's no way to reference 'this' whilst calling the base-level function.

The C# language uses the keyword 'base' to reference the base-level object represented by the 'this' pointer. That is what I was suggesting we could have in AGS, even though polymorphism isn't yet supported.

The LoadFromStack and Copy methods you've asked about are custom functions, not anything that is built-in. As an example of the means required I referenced methods implemented in my Stack module (defined by the Stack type, which in the example acts as a base type).

The point being, with the current implementation you would have to script some custom method of loading all relevant data from the 'this' pointer into a base-level instance (with no actual ties to this instance), call the base-level function, and then copy the relevant data back into this instance. In other words, it's a lot more work because of the fact that we can't currently implement custom pointers. :P

Ryan Timothy B

Ahh see, and I've even used polymorphism for my nonblocking walkto script and didn't even realize what it was called.

Would the Name, Height and Weight in the Person struct still be equal to that of the extended Employee struct? 
I can't see where one would actually need to extend a struct with another. Hmm...

Calin Leafshade

#13
It's used to specialise classes (or in this case structs)

Imagine if you had a generic class called ScreenObject.

this class had things like position, and sprite and stuff like that.
is also contains all the functions required to draw the object to the screen and all the collision logic and stuff..

Ok now you need to make a class called Character.

the Character has routines like say and walk and animate but it also needs all the information from ScreenObject since it is still something that needs to be drawn to the screen but it is a *special case*

like monkey said, all Characters are ScreenObjects but not all ScreenObjects are Characters

EDIT:

I guess a more obvious example for AGSers is the GuiControl class.

GuiControl has members like x and y and functions like BringToFront and so on.
Label has all those functions plus a few more so we can say that the Label class *inherits* those members from the GuiControl class.

If this were not the case then CJ would have had to copy identical code for each of the types of GuiControl (buttons n stuff)

this concept of a class taking on lots of different forms is called polymorphism (Poly - many, Morph - forms)

Ryan Timothy B

Hmm.. I see your point now..
Alright. Perhaps I will actually use this extend feature.

monkey0506

#15
How did you use polymorphism if you didn't know about extending structs with derived types?

The term "polymorphism" only applies to the relation between a derived type and its base type, with regard to the derived-level functions overriding the base-class functions. Otherwise it's just called "overloading" the function.

Regarding extending structs, consider the built-in GUIControl type, which looks something like this:

Code: ags
managed struct GUIControl {
  readonly Button *AsButton;
  readonly InvWindow *AsInvWindow;
  readonly Label *AsLabel;
  readonly ListBox *AsListBox;
  readonly Slider *AsSlider;
  readonly TextBox *AsTextBox;
  bool Clickable;
  bool Enabled;
  int Height;
  readonly int ID;
  readonly GUI *OwningGUI;
  bool Visible;
  int Width;
  int X;
  int Y;
  import void BringToFront();
  import static GUIControl* GetAtScreenXY(int x, int y);
  import void SendToBack();
  import void SetPosition(int x, int y);
  import void SetSize(int width, int height);
};


That's a pretty good sized definition there, and just for the generic GUIControl type. Each of the specialized types (Button, InvWindow, Label, ListBox, Slider, and TextBox) include each of these members (except the "AsType" properties) and functions. Imagine if CJ had to duplicate this struct definition six more times in order to implement the specialized types.

Beyond that he would also have to manually define the five functions for each of the specialized types as well.

Finally, after duplicating all that code, he would be able to include the differences such as Button.NormalGraphic or ListBox.Items[] that make each of the specialized types different from each other.

Now, consider instead the concept of extending a struct. Everything that is defined for a GUIControl will automatically be defined for anything that extends the GUIControl type. This means CJ does not have to include those properties or functions, and can just focus on the new properties and functions that differentiate the types from each other:

Code: ags
managed struct Button extends GUIControl { // inherits all GUIControl definitions
  bool ClipImage;
  FontType Font;
  readonly int Graphic;
  int MouseOverGraphic;
  int NormalGraphic;
  int PushedGraphic;
  String Text;
  int TextColor;
  import void Animate(int view, int loop, int delay, RepeatStyle);
};


Comparably the definition for a Button is much shorter than that of a GUIControl, but that is only because the GUIControl properties and functions are automatically included thanks to inheritance.

Calin beat me to the punch, but I went more in-depth. Ah well.

You also beat me to the punch. Ah well^2. :P

P.S. Although they are somewhat advanced, you may want to check out the following:

Overloading
Inheritance
Polymorphism
Polymorphism in AGS (less technically advanced than the prior three)

Ryan Timothy B

Oh, I thought you had explained that Object.Whatever or Character.Whatever, etc would be basic polymorphism. Meh.
QuoteSince they both have the same name, this is a simple form of polymorphism.

Anyway. It's a very interesting read. I love learning new things that AGS is capable of. It always manages to improve my scripting.

I've got a few other questions from the code you just posted, but honestly, I should probably just start another thread so that I'm not flooding this suggestion thread.

SMF spam blocked by CleanTalk