Equivalent of classes in LiveCode

LiveCode is the premier environment for creating multi-platform solutions for all major operating systems - Windows, Mac OS X, Linux, the Web, Server environments and Mobile platforms. Brand new to LiveCode? Welcome!

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

Post Reply
rkriesel
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 119
Joined: Thu Apr 13, 2006 6:25 pm

Re: Equivalent of classes in LiveCode

Post by rkriesel » Wed Aug 12, 2020 3:04 am

bn wrote:
Tue Aug 11, 2020 11:52 pm

Code: Select all

before birth pX, pY, pdX, pdY, pLifeSpan, pParticleSource, pGravity
   --dispatch "birth" to the behavior of me with px, py, pdX, pdY, pLifeSpan, pParticleSource -- blocked BN
   if pGravity is empty then
      set the gravity of me to 1
   else
      set the gravity of me to pGravity
   end if
end birth

before animate
   set the dy of me to the dy of me + _gravity
   --dispatch "animate" to the behavior of me -- blocked BN
end animate
When a message is dispatched to a behavior of a control then "me" is the behavior and not the control. That is probably the root of the problem. The context is lost.
Hi, Bernd.
How about replacing "dispatch" with "call" to sustain the context?
-- Dick

bwmilby
Posts: 462
Joined: Wed Jun 07, 2017 5:37 am
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Wed Aug 12, 2020 5:08 am

mwieder wrote:
Wed Aug 12, 2020 12:19 am
It's still a mystery to me why the dispatch to the IsAlive?() function wasn't doing the right thing, while removing the dispatch handler in the birth function does.
I think that what is happening is that you are not calling the instance of the behavior that is a part of the chain, but you are calling the behavior script directly. Using `this me` was one thing that I had tried.

I thought about using before/after, but there is another problem there. That would only work for a single super class. Anything more than that, and the before/after would no longer work. Also, there is no need to use `after` - just pass and let the next parent handle it.

For the constructor, adding an optional parameter for the class would allow it to handle dispatch correctly.

Code: Select all

command constructor pClass
   local tSuperClass
   if pClass is not empty then
      if pClass is not the short name of this me then
         pass constructor
      end if
   end if
   --
   # pass up the behavior chain first
   put the behavior of this me into tSuperClass
   if tSuperClass is not empty then
      dispatch "constructor" to me with the short name of tSuperClass
   end if
   --
   # then do the most local constructor tasks
   # put the short name of this me && param(0) && the id of the target & cr after msg
end constructor
The destructor is simpler:

Code: Select all

command destructor
   local tSuperClass
   put the behavior of this me into tSuperClass
   --
   # do the local destructor tasks first
   # put the short name of this this me && param(0) && the id of the target & cr after msg
   --
   # then pass up the message chain
   if tSuperClass is not empty then
      pass destructor
   end if
end destructor
A similar method could be used for class methods (handlers). In that case, I'd probably use dot notation within each class for direct access if there are parameters that need to be dealt with and pass wasn't an option.

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4171
Joined: Sun Jan 07, 2007 9:12 pm

Re: Equivalent of classes in LiveCode

Post by bn » Wed Aug 12, 2020 6:05 pm

rkriesel wrote:
Wed Aug 12, 2020 3:04 am
Hi, Bernd.
How about replacing "dispatch" with "call" to sustain the context?
-- Dick
Unfortunately although it does change the target compared to dispatch it does not change "me".

A small stack with 2 buttons that have two behaviors each. One button uses "call" the other uses "dispatch".
see script for what is called/dispatched when simply clicking, control-clicking or shift-clicking.

addressingBehaviorsChangesTarget.livecode.zip
(1.58 KiB) Downloaded 243 times


Kind regards
Bernd

rkriesel
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 119
Joined: Thu Apr 13, 2006 6:25 pm

Re: Equivalent of classes in LiveCode

Post by rkriesel » Thu Aug 13, 2020 6:18 am

Hi, Bernd. Thanks for the stack. I'm still considering it.

Meanwhile, how about, by convention, passing the object id to every constructor and destructor, instead of relying on "me" to identify the object?

-- Dick

aetaylorBUSBnWt
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 118
Joined: Thu Sep 20, 2012 5:11 pm

Re: Equivalent of classes in LiveCode

Post by aetaylorBUSBnWt » Thu Aug 13, 2020 11:56 pm

bn wrote:
Wed Aug 12, 2020 6:05 pm
rkriesel wrote:
Wed Aug 12, 2020 3:04 am
Hi, Bernd.
How about replacing "dispatch" with "call" to sustain the context?
-- Dick
Unfortunately although it does change the target compared to dispatch it does not change "me".

A small stack with 2 buttons that have two behaviors each. One button uses "call" the other uses "dispatch".
see script for what is called/dispatched when simply clicking, control-clicking or shift-clicking.


addressingBehaviorsChangesTarget.livecode.zip



Kind regards
Bernd
OMG, wow!
C++ is trivial in comparison.
I definitely have some catching up to do to figure this language out.

Next, what does "pass" do?

The documentation appears to say that if in a handler and NOT in an IF clause, you are done executing in that handler and the handler of the same name, UP the message/behavior path is called.

IF "pass" is called IN an IF clause, control DOES return to the handler, resuming execution with first statement after the end of the IF clause.

Correct?

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4171
Joined: Sun Jan 07, 2007 9:12 pm

Re: Equivalent of classes in LiveCode

Post by bn » Fri Aug 14, 2020 12:21 pm

aetaylorBUSBnWt wrote:
Thu Aug 13, 2020 11:56 pm
Next, what does "pass" do?
The documentation appears to say that if in a handler and NOT in an IF clause, you are done executing in that handler and the handler of the same name, UP the message/behavior path is called.
IF "pass" is called IN an IF clause, control DOES return to the handler, resuming execution with first statement after the end of the IF clause.
Correct?
this looks correct to me except for
IF "pass" is called IN an IF clause, control DOES return to the handler, resuming execution with first statement after the end of the IF clause.
If you issue a pass handlername in an If clause the handler with the If-clause is terminated and the message handlername is passed up.

script of a field that should only accept numbers 0 through 9:

Code: Select all

on keyDown pKey
   if pKey is among the items of "0,1,2,3,4,5,6,7,8,9" then
      pass keyDown
   else
      beep
   end if
end keyDown
If you don't pass nothing happens (except beep) other wise the field accepts the numbers.

Kind regards
Bernd

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Fri Aug 14, 2020 4:12 pm

Or more to the point, "pass" is an exit from the routine.

Code: Select all

on keyDown pKey
   if pKey is among the items of "0,1,2,3,4,5,6,7,8,9" then
      pass keyDown
   else
      pass keyDown
   end if
   beep # this never gets called, irrespective of the state of the conditional
end keyDown

aetaylorBUSBnWt
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 118
Joined: Thu Sep 20, 2012 5:11 pm

Re: Equivalent of classes in LiveCode

Post by aetaylorBUSBnWt » Fri Aug 14, 2020 10:41 pm

Hello,

OK, so the documentation is not quite correct on "pass".

I am still discovering what an object is.

As Bernd has taught us, there is a difference in the variable value content of "behavior of me" and "me".

From the looks of this we don't really have true inheritance where you can call the methods of the "super class" and the super class method is operating on its subset of all the fields of the object instantiated by the subclass. If you talk to the "behavior of me" it is a different object.

So if I want to call the reportBehav function in the behav1 button with the variable value content of "me", I thought I would add a parameter to the function to specify what I wanted the function to operate on.

I have found that there is a difference between:

dispatch "reportBehav" to the behavior of me with me

and

dispatch "reportBehav" to the behavior of me with long id of me

What is being passed when you specify "me" vs "long id of me"?

If you pass "me" and in the function it is referred to as "pid" and you try the following: "name of pid", you get a chunk error.
(whatever that is)

If you pass "long id of me" and in the function it is referred to as "pid" and you try the following: "name of pid", you don't get an error.
You don't get the name, but that is a different problem - I think, I hope.

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4171
Joined: Sun Jan 07, 2007 9:12 pm

Re: Equivalent of classes in LiveCode

Post by bn » Sat Aug 15, 2020 12:32 am

aetaylorBUSBnWt wrote:
Fri Aug 14, 2020 10:41 pm
So if I want to call the reportBehav function in the behav1 button with the variable value content of "me", I thought I would add a parameter to the function to specify what I wanted the function to operate on.

I have found that there is a difference between:
dispatch "reportBehav" to the behavior of me with me
and
dispatch "reportBehav" to the behavior of me with long id of me

What is being passed when you specify "me" vs "long id of me"?
if you pass "me" as a parameter you get nothing (empty) because you can only pass a property of me (name, width etc)
In case of passing the "long id of me" as parameter you get exactly that at the receiving end
If you pass "me" and in the function it is referred to as "pid" and you try the following: "name of pid", you get a chunk error.
(whatever that is)
Since "me" as parameter is empty you can not extract the name of it. A chunk is for example: char 3 of word 4 of line 6 of field 1. If that can not be resolved you get a chunk error. Here you can not get the "name" of pid which is also chunking.
Probably the compiler should have caught passing "me" as parameter and should have thrown an error.
If you pass "long id of me" and in the function it is referred to as "pid" and you try the following: "name of pid", you don't get an error.
You don't get the name, but that is a different problem - I think, I hope.
When I tried that I did get the "name of pid" from the parameter "long id of me". Don't know what happened.

As shown you can not address a behavior directly because you loose context. It has to pass through the object that has the behavior/s as a parent.

Oh, and the dictionary says:
When the pass control structure is executed, any remaining
statements in the handler are skipped. Hence, the pass
control structure is usually used either at the end of a handler or
within an if control structure.
this is what is happening, "pass" ends the execution of the rest of the handler and passes the message. The dictionary has some misleading descriptions but this seems to be correct.

Since I am not familiar with Superclasses and OOP I can not comment on that part.But I liked Mark Wieder's stack as an example.

Kind regards
Bernd

bwmilby
Posts: 462
Joined: Wed Jun 07, 2017 5:37 am
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Sat Aug 15, 2020 3:24 pm

aetaylorBUSBnWt wrote:
Fri Aug 14, 2020 10:41 pm
From the looks of this we don't really have true inheritance where you can call the methods of the "super class" and the super class method is operating on its subset of all the fields of the object instantiated by the subclass. If you talk to the "behavior of me" it is a different object.
I think that we do have proper inheritance, but you can only directly access handlers up the chain if they have unique names. From the particle example, if you create `birth` and `particle.birth` in the first class button, then the subclass could directly pass to the superclass by dispatching to `particle.birth`. In the respective `birth` handler, simply call the class specific handler directly.

The reason that `the behavior of me` and `the behavior of this me` references are not useful for dispatching messages is because you are just sending the messages to the base class object (the button with the code) instead of the actual instance associated with `me`.

Also, what documentation are you referring to that hints that pass operates differently?

rkriesel
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 119
Joined: Thu Apr 13, 2006 6:25 pm

Re: Equivalent of classes in LiveCode

Post by rkriesel » Mon Aug 17, 2020 7:42 am

mwieder wrote:
Wed Aug 12, 2020 12:19 am
Here's the latest.
Hi, mwieder. Thanks for investing in this effort, and for sharing progress and current challenges.

Since constructors and destructors in general need to know what they're working on, and since the target and me don't suffice, handlers newObject and deleteObject can help by passing the object id as the first parameter.

The nine changes are four dispatch statements, four signatures, and a constant:

Code: Select all

constant kVersion = 1.02 -- added parameter pObject for every constructor and destructor -- Dick Kriesel

function newObject pClass, pName
   ...
   dispatch "constructor" to control id tNewObject with tNewObject -- 1.02
   ...
end newObject

# example class constructor
command constructor pObject -- 1.02
   ...
   dispatch "constructor" to the behavior of me with pObject -- 1.02
   ...
end constructor

command constructor pObject -- 1.02
   ...
   dispatch "constructor" to the behavior of me with pObject -- 1.02
   ...
end constructor
...
on deleteObject pObject
   ...
   dispatch "destructor" to pObject with pObject -- 1.02
   ...
end deleteObject

# example class destructor
command destructor pObject -- 1.02
  ...
  dispatch "destructor" to the behavior of me with pObject -- 1.02
  ...
end destructor
The technique bwmilby suggests involving passing the object's class can instead invoke function ClassName(pObject).

Does this discussion have a goal beyond just being interesting and fun?
-- Dick

aetaylorBUSBnWt
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 118
Joined: Thu Sep 20, 2012 5:11 pm

Re: Equivalent of classes in LiveCode

Post by aetaylorBUSBnWt » Tue Aug 18, 2020 1:42 am

Hi,

Well my goal with this discussion is to update OOPengine to the latest LiveCode concepts and then use it as a base for C++ like coding.

I have a number of non-UI needs where a C++ like class hierarchy is preferred.

I have a large C++ library (completely non-UI and operating system independent) that I have already created and prefer to use, but if that falls apart, not having to completely rethink the architecture would be helpful.

I have new needs, where once again there is significant behind the scenes code going on that eventually ends up with UI in it and I would rather not tie its design into UI code because there can easily be different UIs for the underlying functionality.

And I believe that this discussion will definitely help anybody coming from a C++ background understand LiveCode better, faster.

I would have never considered the idea of "target", "me", "this me" as all different things in the ways that LiveCode does it.
Also from the initial descriptions of "behavior inheritance", I would not have guessed that it works the way it does.

I thank you all for the education I have gotten here and will be publishing my ultimate version of OOPengine as I get farther down the path.

Andrew

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Tue Aug 18, 2020 2:42 am

Nothing more serious on my part, although melding true OOP onto LiveCode has been a backburner goal for some time now.

At any rate, here's my latest attempt, which seems to do what I expect.
With the caveat that in its current incarnation it's using getProp/setProp handlers natively rather than getters/setters.
Attachments
OOPMWLivecode.zip
(90.05 KiB) Downloaded 246 times

bwmilby
Posts: 462
Joined: Wed Jun 07, 2017 5:37 am
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Tue Aug 18, 2020 2:59 am

rkriesel wrote:
Mon Aug 17, 2020 7:42 am
Since constructors and destructors in general need to know what they're working on, and since the target and me don't suffice, handlers newObject and deleteObject can help by passing the object id as the first parameter.
Actually `me` and `this me` work correctly to identify the object and the class at any time. The problem with your proposed approach is that you lose the context in those dispatches. When you dispatch to any behavior object directly, it goes to that class object. You are not dispatching to an instance that is available in the behavior chain. The only way to enter the behavior chain is to dispatch to `me`. If you wanted to access some type of global class variable, then dispatching to the class object itself would work.

The issue is only a problem when you need to do things after the super class though. In the destructor, you do everything first and then call the parent. In LiveCode you would just pass to the parent. The constructor is where the special care is needed to execute the parent code first but still get back to the child. My method should work as long as the handler is defined at every level in the class hierarchy. If any level is skipped, then it won't work correctly.

I've been keeping all of the modifications that Mark and I made to the library in a git repository on my computer. I could go ahead and post it to GitHub if there is any interest. I used my ScriptTracker on it so it is possible to follow the changes to the code.

bwmilby
Posts: 462
Joined: Wed Jun 07, 2017 5:37 am
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Tue Aug 18, 2020 4:29 am

mwieder wrote:
Tue Aug 18, 2020 2:42 am
At any rate, here's my latest attempt, which seems to do what I expect.
I'm not sure that your SuperClass command is going to work like you expect. My thought reading the code is that the instance variables will be tied to the class object itself instead of the behavior instance associated to the instantiated object.

You are not using your example constructor/destructor code in the particle example. I don't think they would work as expected. Same reason as above.

The reason the destructor deleted the parent object is because the original version used nested groups instead of chained behaviors. So for proper cleanup it would need to delete those groups. When using behaviors, this is no longer applicable. If we wanted to consider a form of multiple inheritance using groups, then it may need to be added.

I'm curious why you changed from creating a group for the object to using a button.

I wonder if `setClassForControl` should check the behavior first and if it isn't empty then use a group to add the class.

As I ponder this further, I'm beginning to feel that a form of dot notation would be required for direct access of superclass methods. A library function could be used to return an array of the class hierarchy which could be used to dispatch a command/function in the required order to parents. Apart from the constructor, how often do we need to start at the top first? It is much more efficient to use pass since you don't need to traverse the whole path each time.

Post Reply