Here is an example of using OOP in M2000. This code can be copied in a module A (in M2000 console we write Edit A and then editor open, so we just copy this and press Esc to exit editor). We run the code calling A (just write A and press enter).
M2000 Interpreter can be use objects of type Group. This type is the user object. A group is a prototype kind of object. A group is a value type, but we can define pointers to groups also. A pointer to group is one of two kinds, a real pointer which hold alive a group, and a weak reference type. In any of these two kinds we can change the pointer to point to another group, or to an empty group (using A->0& a long value indicate the empty group to thin arrow operator).
A group may have a name, like Bob or John, and we say we have a named group. This type is like a static object in other languages, and life cycle end when the environment where we create it end or exit from execution. So named groups exist if the creator is in a running state. We can make global groups also, which are exist the same as the local ones but they can used from modules/functions which we can call (we can define modules and functions in modules and functions). Scope in M2000 has two levels and a half. One is the global scope, then another one is the local scope, and there is a half one, the members scope. So in a group's member Function ToString$ the .name$ is This.name$ and This is the object (instance and object is the same in m2000).
This example use a Big module which have a local CheckIt module. We can keep the inner module and we can copy it in a module B, then we can execute it again (use a Flush statement to empty the stack or delete the Push statement which feed the stack).
When Big module called, there is no CheckIt module, until the definition executed, which M2000 do the simple action to copy it in a list of modules/functions, without check anything. Also notice that a call to Big bound the current stack of values, so parent of Big can pass in and out values through stack. The M2000 Interpreter make a process object and just copy the pointer of parent stack to the new one. This happen again when we call the CheckIt module. So if we say we have the A module running, and the two others, we have three process objects at a running state (process objects used for threads also, but here we didn't use threads).
So now we follow the flow of execution, and interpreter execute the Class statement. This is a macro which replace the code to a function (a global one) with same name as the class name. We can see the code using Print &person() or Edit person() (we can open editor when the program run, the editor is part of language). The difference in this function is the call to constructor. This is a special call to prevent the deletion of local definitions. We need this when we use a statement like This=Person(... ), because we want all new definitions which merged to This to stay there. So as a summary for class functions, we have a Group statement which make group with all the code inside class, and some logic to execute the constructor ( a module of same name as the group name), and the last statement is the return value, the group as is.
When a named group passed to stack or returned from a function, or assigned to an array item, or an inventory item (a type of list), or in tuple (a kind of array), we place a copy of it, as a float group (an unnamed group). So for these types of use, a group has one and only one internal pointer that can't be shared. We can use names to be references to other named groups, but not to unnamed groups. Such a reference can't get a second one, so a workaround is to use a temporary name, so the next time this name not exist and created again. This can be done in a block for temporary definitions, like the For object_name { }. With this manner we have always groups with only one internal not shared pointer. A float group with a not shared pointer always dies when the container (stack, array, inventory) remove it. In these containers when we assign a new float group, the old one deleted. If we want a group to hold other groups, we can use container type members, and we just put them.
The things get harder if we want to use pointers to groups. A named group always dies at the end of the creator, and a pointer to a named group is always a weak reference, so if we pass the pointer outside of creator, and the creator terminate the pointer will be invalid, but not immediate but the time we use it. A pointer to group is a variable which point to group, and can be copied to other variables (a new one or an already pointer), and in containers. The difference from a named group is that in a variable we can access members using a dot but a thik arrow (=>), and the life of object depends from a pointer counter. If the counter is zero then the object deleted (no more pointers point to object). We can use members of a group as group pointers. We can make a group to point to itself. If it is a named group this isn't a real pointer so we don't have to worry for the life of the object. But if we do this to float group, then the object never die, except of the use a dedicated deconstructor which we have to call it to reset the pointers to the 0& (empty group). So this is the difficult part when we use multiple pointers to groups.
There is another variant of group, the superclass. When a group created has a null superclass pointer (we can't handle it). When a merge happen the merged group get the superclass pointer of group at the right expression. Because a group may have many groups inside (and each group inside may have other groups). inner groups may have a superclass also, but a different one. A superclass is a named group with no visible members, but has a pointer to the superclass and a flag which indicate that this is a superclass. So when we create a group from superclass we get a group with all members that are not unique to superclass (unique: is a label which we can use before some members of superclass). From group's member functions and modules we can open superclass using For SuperClass { } structure and we can call functions/modules and other members but not the private members. These members can only used from superclass functions and modules. The intent to use a superclass is to make groups to have common properties. Another way to have common properties is by using pointers to groups inside groups, and to use containers, because containers like stack, array, inventory and buffers are reference type, so we can construct objects using the same pointer to array (an identifier without parenthesis used as pointer to a container, and we can make container's items to point to other containers too). So superclass isn't useful but exist as an easy way to demonstrate the use of a referenced base object.
Here in this example we make composition of objects, which we get a pure object, so we don't have a base object. Also M2000 has no use of interfaces, because there are no types for groups, so we can add classes one after the other, in the root group (the group we make from a class) or the child groups (which we can make inside a group).
Here is a different approach, we make for each player a pointer to a person group. A group named Person inside Player group return a value as a copy of the A group. Because A is a member of the parent we have to explicit link with a local variable, which maybe have the same name.
See also that we include functions for Player to call functions. For functions can pass the stack using ![] so if ABC() get a, b we don't get anything in the Person member ABC and in the call we use A=>ABC(![]). The ! place a stack to current stack, and [] is a function which return pointer to a stack, which was the previous stack (so immediate after the execution of [] we get an empty current stack). This mechanism make two things copy the pointer of current stack, and then change the current stack with an empty stack. So no copy or move of items in stack happen and we pass the parameters without any check to ABC in A group (which pointed by A)
M2000 Interpreter can be use objects of type Group. This type is the user object. A group is a prototype kind of object. A group is a value type, but we can define pointers to groups also. A pointer to group is one of two kinds, a real pointer which hold alive a group, and a weak reference type. In any of these two kinds we can change the pointer to point to another group, or to an empty group (using A->0& a long value indicate the empty group to thin arrow operator).
A group may have a name, like Bob or John, and we say we have a named group. This type is like a static object in other languages, and life cycle end when the environment where we create it end or exit from execution. So named groups exist if the creator is in a running state. We can make global groups also, which are exist the same as the local ones but they can used from modules/functions which we can call (we can define modules and functions in modules and functions). Scope in M2000 has two levels and a half. One is the global scope, then another one is the local scope, and there is a half one, the members scope. So in a group's member Function ToString$ the .name$ is This.name$ and This is the object (instance and object is the same in m2000).
This example use a Big module which have a local CheckIt module. We can keep the inner module and we can copy it in a module B, then we can execute it again (use a Flush statement to empty the stack or delete the Push statement which feed the stack).
When Big module called, there is no CheckIt module, until the definition executed, which M2000 do the simple action to copy it in a list of modules/functions, without check anything. Also notice that a call to Big bound the current stack of values, so parent of Big can pass in and out values through stack. The M2000 Interpreter make a process object and just copy the pointer of parent stack to the new one. This happen again when we call the CheckIt module. So if we say we have the A module running, and the two others, we have three process objects at a running state (process objects used for threads also, but here we didn't use threads).
So now we follow the flow of execution, and interpreter execute the Class statement. This is a macro which replace the code to a function (a global one) with same name as the class name. We can see the code using Print &person() or Edit person() (we can open editor when the program run, the editor is part of language). The difference in this function is the call to constructor. This is a special call to prevent the deletion of local definitions. We need this when we use a statement like This=Person(... ), because we want all new definitions which merged to This to stay there. So as a summary for class functions, we have a Group statement which make group with all the code inside class, and some logic to execute the constructor ( a module of same name as the group name), and the last statement is the return value, the group as is.
When a named group passed to stack or returned from a function, or assigned to an array item, or an inventory item (a type of list), or in tuple (a kind of array), we place a copy of it, as a float group (an unnamed group). So for these types of use, a group has one and only one internal pointer that can't be shared. We can use names to be references to other named groups, but not to unnamed groups. Such a reference can't get a second one, so a workaround is to use a temporary name, so the next time this name not exist and created again. This can be done in a block for temporary definitions, like the For object_name { }. With this manner we have always groups with only one internal not shared pointer. A float group with a not shared pointer always dies when the container (stack, array, inventory) remove it. In these containers when we assign a new float group, the old one deleted. If we want a group to hold other groups, we can use container type members, and we just put them.
The things get harder if we want to use pointers to groups. A named group always dies at the end of the creator, and a pointer to a named group is always a weak reference, so if we pass the pointer outside of creator, and the creator terminate the pointer will be invalid, but not immediate but the time we use it. A pointer to group is a variable which point to group, and can be copied to other variables (a new one or an already pointer), and in containers. The difference from a named group is that in a variable we can access members using a dot but a thik arrow (=>), and the life of object depends from a pointer counter. If the counter is zero then the object deleted (no more pointers point to object). We can use members of a group as group pointers. We can make a group to point to itself. If it is a named group this isn't a real pointer so we don't have to worry for the life of the object. But if we do this to float group, then the object never die, except of the use a dedicated deconstructor which we have to call it to reset the pointers to the 0& (empty group). So this is the difficult part when we use multiple pointers to groups.
There is another variant of group, the superclass. When a group created has a null superclass pointer (we can't handle it). When a merge happen the merged group get the superclass pointer of group at the right expression. Because a group may have many groups inside (and each group inside may have other groups). inner groups may have a superclass also, but a different one. A superclass is a named group with no visible members, but has a pointer to the superclass and a flag which indicate that this is a superclass. So when we create a group from superclass we get a group with all members that are not unique to superclass (unique: is a label which we can use before some members of superclass). From group's member functions and modules we can open superclass using For SuperClass { } structure and we can call functions/modules and other members but not the private members. These members can only used from superclass functions and modules. The intent to use a superclass is to make groups to have common properties. Another way to have common properties is by using pointers to groups inside groups, and to use containers, because containers like stack, array, inventory and buffers are reference type, so we can construct objects using the same pointer to array (an identifier without parenthesis used as pointer to a container, and we can make container's items to point to other containers too). So superclass isn't useful but exist as an easy way to demonstrate the use of a referenced base object.
Here in this example we make composition of objects, which we get a pure object, so we don't have a base object. Also M2000 has no use of interfaces, because there are no types for groups, so we can add classes one after the other, in the root group (the group we make from a class) or the child groups (which we can make inside a group).
Module Big { \\ classes are functions which return a concrete object (not a pointer to object) \\ by default a class defined in module/function is global, but deleted after exit of module/function Module CheckIt { Class person { Private: aged=1000 name$="Not defined yet" Public: Function ToString$ { =.name$+" age"+str$(.aged) } Function IsOld$ { =if$(.aged>65->"Yes", "No") } \\ constructor \\ after Class: all members are exist once, here ate the person() function Class: Module Person (.name$, .aged) {} } Class player { \\ by default members are public \\ A final function can't be changed from this=person() Function Final IsOld$ { =if$(.aged>35->"Yes", "No") } \\ constructor Class: Module player (name$="Not Defined Yet", age=1000) { this=person(name$, age) \\ apply inheritence, by merging person() to this } } \\ variables like Bob and John can get an object, and each create a named group \\ these groups erased at the end of this module Bob=player("Bob Marley", 36) John=player("John Harris", 66) Print Bob.ToString$() Print "is to old:"+Bob.IsOld$() Print John.ToString$() Print "is to old:"+John.IsOld$() CopyOfBob=Bob \\ to alter a private member we have to explicit redefined it Group CopyOfBob { Private: aged=38 } Print CopyOfBob.ToString$() Print "is to old:"+CopyOfBob.IsOld$() \\ we can merge CopyOfBob to Bob Bob=CopyOfBob Print Bob.ToString$() Print "is to old:"+Bob.IsOld$() Dim Players(10)=Player() Players(0):=Bob, John For i=0 to 1 Print Players(i).ToString$() Next \\ we can export the array, place a pointer to stack Push Players() } \\ when we call a module we pass current stack as module's current stack CheckIt \\ now everything in CheckIt are deleted, except the array in current stack \\ there are no class functions, but array has all it needs to use Read P() For i=0 to 9 Print P(i).ToString$() Print "is to old:"+P(i).IsOld$() Next \\ we can use pointers to objects \\ when a pointer created from a no named group, we get a real pointer A->p(0) Print A=>ToString$(), A=>IsOld$() \\ a pointer can point to another object A->p(1) Print A=>ToString$(), A=>IsOld$() \\ we can delete all groups in A() Dim A() \\ a pointer hold the object Print A=>ToString$(), A=>IsOld$() \\ we can make a named group (bound to this module), as a copy of A (a pointer to a group) John=Group(A) Print John.ToString$(), John.IsOld$() A->0& ' we delete the group because no other reference exist for it. A point to an empty group A->John ' this is a pointer as a weak reference to john Print A=>ToString$(), A=>IsOld$() A->0& ' we delete the weak reference and we place a pointer to an empty group Print type$(A)="Group" Push John } Big \\ now stack has a group (not a pointer to group, but a group) Dim A(10) \\ we can put it in A(3) Read A(3) Print A(3).IsOld$()
Here is a different approach, we make for each player a pointer to a person group. A group named Person inside Player group return a value as a copy of the A group. Because A is a member of the parent we have to explicit link with a local variable, which maybe have the same name.
See also that we include functions for Player to call functions. For functions can pass the stack using ![] so if ABC() get a, b we don't get anything in the Person member ABC and in the call we use A=>ABC(![]). The ! place a stack to current stack, and [] is a function which return pointer to a stack, which was the previous stack (so immediate after the execution of [] we get an empty current stack). This mechanism make two things copy the pointer of current stack, and then change the current stack with an empty stack. So no copy or move of items in stack happen and we pass the parameters without any check to ABC in A group (which pointed by A)
Module Big { Module CheckIt { Class person { Private: aged=1000 name$="Not defined yet" Public: Function ToString$ { =.name$+" age"+str$(.aged) } Function IsOld$ { =if$(.aged>65->"Yes", "No") } Class: Module Person (.name$, .aged) {} } Class player { Private: Group A Public: Group Player { Function IsOld$ { Link parent A to A =A=>IsOld$() } value { Link parent A to A =Group(A) } } Function ToString$ { =.A=>ToString$() } Function IsOld$ { =.A=>IsOld$() } Class: Module player (name$="Not Defined Yet", age=1000) { Alfa=person(name$, age) \\ apply inheritence, by merging person() to this Group Alfa { Function Final IsOld$ { =if$(.aged>35->"Yes", "No") } } .A<=Pointer((Alfa)) } } Bob=player("Bob Marley", 36) John=player("John Harris", 66) Print Bob.ToString$() Print "is to old:"+Bob.IsOld$() Print John.ToString$() Print "is to old:"+John.IsOld$() Dim Players(10)=Player() Players(0):=Bob, John For i=0 to 1 Print Players(i).ToString$() Next Push Players() } CheckIt Read P() For i=0 to 9 Print P(i).ToString$() Print "is to old:"+P(i).IsOld$() Next A->p(0) Print A=>ToString$(), A=>IsOld$() A->p(1) Print A=>ToString$(), A=>IsOld$() Dim A() Print A=>ToString$(), A=>IsOld$() John=Group(A) Print John.ToString$(), John.IsOld$() A->0& A->John Print A=>ToString$(), A=>IsOld$() A->0& Print type$(A)="Group" Push John } Big \\ now stack has a group (not a pointer to group, but a group) Dim A(10) \\ we can put it in A(3) Read A(3) Print A(3).IsOld$() k=A(3).Player PrintK.IsOld$(), "ok" For A(3).Player { Print .IsOld$(), "ok" } PrintA(3).Player.IsOld$(), "ok"' call IsOld$ inside Player
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
You can feel free to write any suggestion, or idea on the subject.