Dynamic dispatch... and why Xojo needs it.

If I have a class called ‘Base’ with a shared method called, ‘FindByID’… and a subclass of Base called, ‘Team’. I should be able to write something like:

Var myTeam As Team = Team.FindByID(1)

Ideally, this should return an object of type, ‘Team’… But Xojo runs this as Base.FindByID(1) which doesn’t know what a ‘Team’ is.

Even within instance methods, the Base class needs to be given constants such as the table name so that it can retrieve the data and then the data must be passed back down to the subclass for deserialization. This creates unnecessary code duplication that is amplified by the size of your project.

ARGen is a great solution to a problem that shouldn’t exist. It does all the copy/paste work and then performs the mundane substitutions that are required to customize each object. Which saves a lot of work. My point is that it shouldn’t have to.

We either need the base class to be aware of the subclass that called it, or we need the method to run from the perspective of the subclass so that it has access to the class name and property declarations that it would need to instantiate a new object of that subclass.

Until one of these things happen, we will be forced to implement subclass-specific methods for common operations on every subclass.

It should be noted that the above code works as expected in modern, object-oriented programming languages such as Python. What is it that Python does that Xojo doesn’t? I’m not sure but the result is much cleaner code that is faster to write and easier to understand.

My questions are:

  1. What is the scientific term for this functionality? (Not knowing what to call it has made it difficult to answer the next question.)

  2. Has this been brought up before? If so, what was the response from Xojo or is there an open feedback ticket that I can vote for?

To shed some light for those who don’t use ActiveRecord:

The issue is that “self has no meaning in a Shared Method”. Basically, the Base class wouldn’t be able to tell whether you called FindByID on Team or Player because it’s a Shared Method. I found that the information isn’t even available in the stack trace, the stack always shows the Base class Shard Method.

In ActiveRecord, the FindByID method must be a Shared Method so that one doesn’t have to instantiate the table to use the function.

I agree, a shared method in a subclass should override the shared method in its super, and it doesn’t. As far I can tell, there is no way to get it to do it either.

In some circumstances, you can work around it. One technique is to pass either a new object or TypeInfo on the object to the shared method. If successful, the method will return that object filled in with the correct data, or create a new object using TypeInfo. If not, it will return nil.

In either case, you will have to typecast it on the calling side, so something like this:

var myTeam as Team = Team(Team.FindById(id, new Team))
'or
var myTeam as Team = Team(Team.FindById(id, GetTypeInfo(Team)))

Not pretty, but it can be coded to work.

as far as I know the resolution of the specific shared method is done at compile time because it can be
and the name of the class that was used to access the shared method isnt necessary once the specific method has been looked up in the dispatch table of whatever class its found in
hence all the calls, unless they are overridden in a subclass, appear to be from the super class because thats indeed where the method exists

i tested this i have no problem
i use

var n as NormalClass = NormalClass.SharedMethod

have a BaseClass with

[code]Public Shared Function SharedMethod() as NormalClass
Var n As New NormalClass

Return n

End Function
[/code]

and NormalClass with Super to BaseClass

Call SharedMethod on a subclass of NormalClass. You can not determine whether it was NormalClass or SubClass that called SharedMethod, and the Xojo language doesn’t offer a feature that is required to do so.

Just to be clear, the shared method doesn’t exist in the subclass, in my example. It calls the method that exists in the Base class… but does so AS IF IT WAS in the subclass.

[quote=496397:@Norman Palardy]as far as I know the resolution of the specific shared method is done at compile time because it can be
and the name of the class that was used to access the shared method isnt necessary once the specific method has been looked up in the dispatch table of whatever class its found in
hence all the calls, unless they are overridden in a subclass, appear to be from the super class because thats indeed where the method exists[/quote]

Thank you for this insight!

You say that it isn’t necessary but we seem to have found a reason why it is. The questions remain, what is this called and what can be done about it if it is inherent to the Xojo system not something that can be solved with better code?

i think your problem is that you will search in a list that not exists in the base class.
this method could also return a team object with “Base”.FindByID(1)
can you show what FindByID is doing inside?

i never need this, if the normal class have this method than its called, if normal and base class have both this method and i need
to call the base method then i just call it base.method.
i see a method abstract it put something in and get something in return.

using a interface with FindByID could also be a solution.

i hade a exhausting day, its possible that i not get the problem overall.

[quote=496409:@Markus Rauch]i think your problem is that you will search in a list that not exists in the base class.
this method could also return a team object with “Base”.FindByID(1)
can you show what FindByID is doing inside?[/quote]

The idea here is that Base handles all interaction with the database. Base.FindByID(id As Integer) would create the SQL statement and retrieve the data that the subclass requires… but it can’t, because it doesn’t know which subclass asked for it. By calling it as a Shared Method of the Base class, we don’t require an instantiation (hence how Shared Methods work) but it still needs to know the table name and fields to retrieve.

Part of a work-around solution is to make Base.FindByID(tableName As String, id As Integer) and create a Shared Method with Team called, FindByID which calls it’s super and passes along the required parameters that it wouldn’t otherwise have. It works but look at all the methods that I have to create in my Team class…

Public Sub Delete()
  Super.Delete("Team", id)
End Sub


Public Sub Read()
  Super.Read("Team", id)
End Sub


Public Sub Update()
  Super.Save("Team", id, SerializedData)
End Sub


Public Sub Create()
  Super.Save("Team", SerializedData)
End Sub


Public Property id as Integer


Public Property name as String


Public Shared Function FindByID() as Team
  Super.FindByID("Team", 1)
End Function

And that only solves part of the problem since Base has no way to return an object of ‘Team’. Instead, it has to return the raw response from the database and the Team class has to know how to Deserialize it.

The point is that Xojo shouldn’t inflict this sort of pain upon us. The class should be nothing more than the declaration of it’s properties unless specific methods must be overridden.

[quote=496408:@Kristin Green]Thank you for this insight!

You say that it isn’t necessary but we seem to have found a reason why it is. The questions remain, what is this called and what can be done about it if it is inherent to the Xojo system not something that can be solved with better code?[/quote]

Its not necessary to call the correct method

This would require a massive change in how Xojo has worked for 20 years & the compiler
I doubt they’d entertain such an overhaul

understand, and how about this
var t as Team = Base.FindBy(Team.Create(1))
var t as New Team(1)
Base.FindBy(t) as byref
where FindBy use a object?
so the Team Constructor memory the Table name and memory a request id as example in base class.
i think i would also end up in experimenting what is best.

or a mix

Public Function Find() as Team Return ClassWithSharedMehod.Find(Me) End Function

suppose you have a base class - not necessarily Base in this case
what you could do is like this

    Class Class1 
         Shared Sub Foo( ti as Introspection.TypeInfo = nil )
               if ti is nil then
                     // this happens when you call Class1.foo with no param
               else
                     // this happens if one of the subs called this 
               end if
         end Sub
   end class


   Class CustomClass1
         Shared Sub Foo( ti as Introspection.TypeInfo = nil )
               if ti is nil then
                     // this happens when you call CustomClass1.foo with no param
                     Super.Foo( getTypeInfo( CustomClass1) )
               else
                     // this happens if one of the subs called this 
                     Super.Foo( ti ) 
               end if
         end Sub
    End Class        


   Class CustomCustomClass1
         Shared Sub Foo( ti as Introspection.TypeInfo = nil )
               if ti is nil then
                     Super.Foo( getTypeInfo( CustomCustomClass1) )
               else
                     // this happens if a sub called this 
                     Super.Foo( ti ) 
               end if
         end Sub

it still requires a few overloads of superclass methods but now the base class has a typeinfo and from that it can access properties & the constructor methods etc

its not perfect but its whats possible today

Yep. I’m using a work around that doesn’t require that the entire class TypeInfo be passed but I see where you’re going with this.

Both methods still require that I create methods at the sub-class level.

To be clear, I’m not looking for a code work-around. I’m asking what this functionality is called and whether or not it’s something that Xojo might support in the future. I don’t mind creating the feature request and launching it into the either… I just wanted to know if someone else already had and if Xojo had an official response to it.

[quote=496415:@Norman Palardy]This would require a massive change in how Xojo has worked for 20 years & the compiler
I doubt they’d entertain such an overhaul[/quote]

As big as API 2.0?

:wink:

bigger
API 2 is pretty light weight in many respects
About like you renaming a bunch of properties & events on classes you created
Very little actual compiler work or even deep framework work really required

This is much more low level and have everything to do with how methods are resolved & eventually dispatched so that the lowest base level has knowledge of which subclass was used to gain initial access to the method

and, at the end of the day what would “base” get as information to know how to use it, do the right thing etc ?
this is why I passed a typeinfo - it gave the base class access to everything that subclass has - properties, attributes, methods etc etc etc

[quote=496422:@Kristin Green]I’m asking what this functionality is called[/code]
If it has a specific name beyond name, or symbol, resolution I dont know it

[quote=496422:@Kristin Green]and whether or not it’s something that Xojo might support in the future. I don’t mind creating the feature request and launching it into the either
[/quote]
create the request and they’ll assess it

Shared methods are akin to static methods in other languages like C#. You cannot override them there either. The override is adjusting the functionality of the instance of that class. Shared/static methods don’t belong to an instance of the class, only a similar namespace.

Maybe I’m missing something, but shouldn’t each subclass override the shared method and do it’s unique stuff? Trying to have a method in the base class that handles the details of the subclass seems backwards to me. Sure, have the database access baked into the base class, but each subclass should set its own unique properties from the rowset.

And, yes, shared methods can be overridden. I tested it.

I would argue you are not actually overriding anything. They are just two methods with the same signature where one happens to be a super of the other. Super in this context means nothing other than a pointer to the shared methods in the super class which you could just as easily call by using the class name vs Super. As a shared method it has no instantiated values or access to instance methods so…

Class Base
Shared Sub MsgBox
MsgBox(“Base”)
End Sub
End Class

Class SubClass
Inherits Base
Shared Sub MsgBox

// These two are 100% equivalent
Super.MsgBox()
Base.MsgBox()

End Sub
End Class

Now if it wasn’t a shared method then the subclass could do all kinds of fun things like access Supers properties/methods or even manipulate some of its own.

The only value I see in this topology is an easier way to return a SubClass vs a Base class ala “Return New SubClass”. But what advantage does that have over just “Dim x As New SubClass”? Unless you want to set private/protected variables of the SubClass which you should do in the constructor and not in a shared method I think.

A normal method of a subclass could update the protected variables of the super class. Not true in a shared method. Yet another strike against this being an override in any useful context.