In M2000 a group is the base for OOP.
What is a Group:
- A not reference type variable
- An open list of members
- Some members can be private and some public
- Member can be any type of variables including groups, modules, functions, and special members
- Special Member are the Operator, Value, Set, Properties
- A Value member is a function to return a value
- A Set member is a function used when we assign an expression to group
- Value and Set can have parameter list.
- Operators (like + - * /, <> >= <= =, and ++ --)
- Properties are groups with Set/Value or Set or Value members, and we can add more if we want. Properties are always Public.
- If not value supported, a group export a copy of it (a nameless group).
- If value supported a named group can return copy using Group(name)
- When no Set supported Assigning a group B to group A a merge happen, so A has a B inside but maybe has more members.
- When a Set supported then assigning a group with a value, means calling the Set function to do something for that.
- Each Group (and inner groups) can have a SuperClass, a pointer to a nameless group for class variables, and for used as a base blue print, in copies. Each group (and each inner group) can have access to superclass variables.
- A superclass can't have a superclass.
- Inheritance gained by merging groups of different kinds.
- There are no interfaces for groups. We have to merge a group with a role of interface.
- Encapsulation provided by private variables/objects/modules/functions
- Polymorphism in M2000 is something simple, because there are no interfaces, so by using group merge function, or adding/replacing members, we can make objects with some members with same names but different functionality.
- From 9.3 version there are pointers to groups.
A group may have a name, or not.
- A group without name has all members (public and private) in private space. To operate a nameless group, interpreter has to give it a name before, and place members to appropriate lists. Nameless groups have only position in a container, and we get access to them by using index or key or position. Assigning a nameless group somewhere in a container interpreter empty first the place and then place the group.
- A group with name has all members in appropriate lists (the global list for variables/arrays, and the global list for modules/functions). When we pass a group by reference all members passed by reference too, but actual group passed by copy, so in a reference group we can add anything for temporary use. There is a second type of reference by using weak reference which not reproduce all members as references, and each time we use a member the weak reference added to name of member. So with that we get actual group by reference, but no member addition allowed.
We can use weak reference for named groups, and nameless groups in arrays providing array name and index(es).
This is an example using a user function to return a weak reference for inventory. Weak reference is used where we have a generic module, which can work with a reference from a named group, a group in array or a group in inventory. Inventories are reference type, so passing it by value is a copy of pointer. But here we pass the name of inventory including the key.
Function Weak$() work for arrays preparing the index(es) evaluating the expressions before export the string.
In version 9.0 revision 9 repaired code for calling superclass A from a calling in superclass B. We can't have two superclasses references in one group, but because a group may have groups inside, each group may have a reference to superclass. A group is not a reference type in M2000, except for superclass.
Inventory
alfa
Group
Beta {
x=10
}
Module
Generic (what$)
{
For
what$ {
Print
.x
}
}
Delta=Beta
Delta.x+=100
Append
alfa,
"hello":=Beta,
400:=Delta
\\
a way to get a weak reference from an inventory inlcuding key.
Def
WeakInventory$(a$, n$,
w$) =
left$(a$,len(a$)-len(n$))
+ n$
+ "(" +
Quote$(Trim$(w$))
+
")"
N$=WeakInventory$(&alfa,
"alfa","hello")
Print
N$
Print eval(N$.x)
Generic
N$
N$=WeakInventory$(&alfa,
"alfa",Str$(400))
Print
N$
Print eval(N$.x)
Generic
N$
Dim
a(10)
a(2)=Beta
N$=weak$(a(1+1))
Print
N$
Print eval(N$.x)
Generic
N$
So now we have an idea about groups, so go on to next example. We make a function Generator to return a lambda function. This lambda function hold A, a SuperClass. This Superclass has a unique member, counter. Has also public members an id, stuff$. So we make Super as a lambda function (so exist this name Super()). We pass an name to Super and we get a group, with id filled appropriate.
Function
Generator {
SuperClass
A
{
Unique:
counter
Public:
id=0
stuff$=""
Function
Many {
For
SuperClass
{
=.counter
}
}
}
=
Lambda A
(what$)->{
tmp=A
\\
adding a module
Group
tmp {
Module
Add2counter
{
For
SuperClass
{
.counter++
}
}
}
tmp.Add2counter
M=A
\\ make a group using SuperClass
For
M {
.id=.Many()
.stuff$=what$
}
=M
}
}
Super=Generator()
A=Super("Babis")
B=Super("George")
Print
A.stuff$,
B.stuff$
Print
A.many()
In the next example we make something more advanced. We want some more members, and a way to read members from specific functions, Super() and Super$()
Also see how we use optional passing variables in functions.
Function
Generator {
\\
this is a temporary global variable.
Global
typename$="sometype1"
\\
we can read optional
Read
? typename$
\\
a SuperClass is a special Group
\\
but at creation time, copy definition in a class function
\\
so to read typename$ must be global
SuperClass
A
{
Unique:
counter,
magic=random(100000000),
type$=typename$
Public:
Function
Super {
For
SuperClass
{
=eval("."+letter$)
}
}
Function
Super$ {
For
SuperClass
{
=eval$("."+letter$.)
' need a dot after last $, to get a weak
reference.
\\
try without
dot. =eval$("."+letter$)
}
}
}
=
Lambda A
(what$)->{
M=A
Group
M {
id,
stuff$
Class:
\\
if we copy M, then anything as CLASS: can't
copied.
Module
Add2counter
{
For
SuperClass
{
.counter++
}
}
}
M.Add2counter
M.id=M.Super("counter")
M.stuff$=what$
=M
}
}
\\
we can make global the "Super" lambda
\\ Super has a
closure, the Superclass.
Global
Super=Generator("myType")
\\ we can use =Generator() to get
default value for typename
A=Super("Babis")
B=Super("George")
Print
A.stuff$,
B.stuff$
Print
"A",A.id,
A.Super("magic"),
A.Super$("type$")
C=Super("John")
For
C {
Print
"C",
.id,
.Super("magic"),
.Super$("type$")
}
Print
"Total objects:",
A.Super("counter"),
B.Super("counter"),
C.Super("counter")
Rem
1: List \\
list variables
Rem
2: Modules
? \\ list modules/functions in memory
More advanced. Now function Generator return Group (not lambda as previous example). Group now has a value member.
Global typename$ ="hello there" Function Generator { \\ this is a temporary global variable.
Global typename$ ' shadow any global typename$ Set Read typename$ = "sometype1" ' alter any global or make a new \\ a SuperClass is a special Group
\\ but at creation time, copy definition in a class function
\\ so to read typename$ must be global
SuperClass A { Unique: counter, magic=random(100000000), type$=typename$ Public: Function Super { For SuperClass { =eval("."+letter$) } } Function Super$ { For SuperClass { =eval$("."+letter$.) ' need a dot after last $, to get a weak reference. \\ try without dot. =eval$("."+letter$)
} } } M=A \\ make a M as an A \\ so now we extend M
Group M { \\ interpreter intialize members automatic
id, stuff$ \\ we set a return Value with a parameter
Value (what$) { M=This For SuperClass { .counter++ M.id<=.counter } M.stuff$<=what$ \\ because M has a return value with a parameter
\\ we have to use Group() which find from name the object and return it
=Group(M) } } \\ see remark before
=Group(M)}\\ Generator return a group only.
Global Super=Generator("myType2") ' check it without parameterA=Super("Babis")B=Super("George")
Print A.stuff$, B.stuff$Print "A",A.id, A.Super("magic"), A.Super$("type$")C=Super("John")For C { Print "C", .id, .Super("magic"), .Super$("type$")}Print "Total objects:", A.Super("counter"), B.Super("counter"), C.Super("counter")Rem 1: List \\ list variablesRem 2: Modules ? \\ list modules/functions in memoryPrint typename$="hello there"
This is a more advanced example. Code to increment counter is part of Value member in any Group, so we didn't use an
Super as group, as in previous example.
Function Generator {
\\ In 9.2> version we can Read as global variable
\\ which we define as Global (Global A make a new A if an old global A exit)
' Global typename$="sometype1"
\\ We can use Set Read which make a global and use as before newer versions.
\\ Set command send line to CLI, using current stack.
\\ in that line we can't use local variables, only global, but we can make new globals
\\ or alter old globals
\\ we can read optional using set read ? ... or just using = "firstvalue" as this:
Set Read typename$ ="sometype1"
\\ a SuperClass is a special Group
\\ but at creation time, copy definition in a class function
\\ so to read typename$ must be global
SuperClass A {
Unique:
counter,
magic=random(100000000),
type$=typename$
Public:
Function Super {
For SuperClass {
=eval("."+letter$)
}
}
Function Super$ {
For SuperClass {
=eval$("."+letter$.) ' need a dot after last $, to get a weak reference.
\\ try without dot. =eval$("."+letter$)
}
}
}
= Lambda A (what$)->{
M=A
Group M {
id, stuff$
Class:
\\ if we copy M, then anything as CLASS: can't copied.
Module Add2counter {
For SuperClass {
.counter++
}
}
}
M.Add2counter
M.id=M.Super("counter")
M.stuff$=what$
=M
}
}
\\ we can make global the "Super" lambda
\\ Super has a closure, the Superclass.
Global Super=Generator() \\ we can use =Generator() to get default value for typename
A=Super("Babis")
B=Super("George")
Print A.stuff$, B.stuff$
Print "A",A.id, A.Super("magic"), A.Super$("type$")
C=Super("John")
For C {
Print "C", .id, .Super("magic"), .Super$("type$")
}
Print "Total objects:", A.Super("counter"), B.Super("counter"), C.Super("counter")
Rem 1: List \\ list variables
Rem 2: Modules ? \\ list modules/functions in memory
So here we see something else. How to use a virtual module (method). There is no virtual keyword for M2000, but see the code below. For remind, any module take parent stack for values. When we call something we pass arguments in stack for values. When we call member printdata we call private member super.printdata (the prefix super is as chosen, can be anything, or then name can be anything). M2000 except for user Events never check signatures for parameters. If there are no appropriate Read commands, then we get error, either if we require something to read, or we provide something in wrong type.
So calling member .printdata we pass a number in stack for values, and that stack passed to .super.printdata. (X) is a sugar code for Read X (a line inserted automatic by interpreter, we can see that when we watch the "colored" code running in form Control, opened using Test command)
Later we simulate a Class function (M2000 has classes as group factories), with a lambda function. We need lambda to make a closure for SuperClass Alfa.
Superclass alfa is a special group. Has no members, and has a pointer to a nameless group, and that is the superclasss pointer which used by "Derived" classes
Superclass
alfa
{
name$="unkown"
Private:
module
super.printdata (X){
Print
"name:";
.name$
Print
"code:";
X
}
Public:
\\
this is a virtual module.
module
printdata
{
.super.printdata
}
}
\\
simulate Class function
Global
N=Lambda
alfa (noemptyname$)
-> {
if
noemptyname$=""
then error
"no name
given"
a=alfa
a.name$=noemptyname$
=group(a)
}
Beta
=N("Babis")
Beta.printdata
999
Group
Beta
{
town$="Preveza"
module
printdata {
Print
"town:";.town$
.super.printdata
}
}
Beta.printdata
1000
Delta=N("John")
Delta.printdata
1001
Another example, with a group with two superclasses, one for top group and one for inner group. So we can change inner group superclass. And thats all for mastering Groups in M2000.
\\ double super
class
Superclass
A {
unique:
counter
}
Superclass
B1 {
unique:
counter
}
Superclass
B2 {
unique:
counter
}
\\ We can make a
group Alfa with a member, another group Beta
\\ Group Beta can't
see parent group, but can see own member groups
\\ Group Alfa
can see everything in nested groups, in any level,
\\ but can't
see inside modules/functions/operator/value/set
Group
Alfa {
Group
Beta { }
}
Alfa=A
Alfa.Beta=B1
\\
we make 3 groups for marshaling counters
\\ each group get a
superclass
Marshal1=A
Marshal2=B1
Marshal3=B2
\\
Now we want to add functionallity
\\ Inc module to add 1 to
counter
\\ a Value function to return counter
\\ Without
Value a group return a copy
\\ If a group has a value then we
can get copy using Group(nameofgroup)
\\ just delete Group
Marshal1 and remove Rem when we make Marshal1 using a class
function
Group
Marshal1 {
Module
Inc {
For
SuperClass
{.counter++}
}
Value
{
For
SuperClass {=.counter}
}
}
Class
AnyMarshal {
Module
Inc {
For
SuperClass
{.counter++}
}
Value
{
For
SuperClass {=.counter}
}
}
\\
here we merge groups
Rem
: Marshal1=AnyMarshal()
Marshal2=AnyMarshal()
Marshal3=AnyMarshal()
\\ So now we see counters (three zero)
Print
Marshal1,
Marshal2,
Marshal3 \\
0, 0, 0
\\ Now we prepare Alfa and Alfa.Beta groups
Group
Alfa {
Group
Beta {
Function
SuperClass.Counter
{
For
SuperClass
{
=.counter
}
}
}
Module
PrintData {
For
SuperClass {
Print
.counter,
This.Beta.SuperClass.Counter()
}
}
}
\\
some marshaling to
counters
Marshal1.inc
Marshal2.inc
Marshal2.inc
Marshal3.inc
\\
lets print results
Print
Marshal1,
Marshal2,
Marshal3 \\
1 2 1
\\ Calling
Alfa.PrintData
Alfa.PrintData \\
1 2
\\ Merging a group in a group make a
change to superclass pointer inside group
Alfa.Beta=B2
\\ change supeclass
Alfa.PrintData \\
1 1
For
i=1 to
10 : Marshal3.inc
: Next
i
Alfa.PrintData \\
1 11
Alfa.Beta=B1
\\ change supeclass
Alfa.PrintData \\
1 2
Epsilon=Alfa
Print
Valid(@alfa
as epsilon),
Valid(@alfa.beta
as epsilon.beta)
\\ -1 -1
Epsilon.PrintData
\\ 1 2
Alfa.Beta=B2
\\ change supeclass
Alfa.PrintData \\
1 11
Epsilon.PrintData
\\ 1 2
\\ validation being for top group superclass and
all members if are same
\\ but not for inner superclasses. This
maybe change in later revisions of language.
Print
Valid(@alfa
as epsilon),
Valid(@alfa.beta
as epsilon.beta)
\\ -1 0