Πέμπτη, 12 Οκτωβρίου 2017

Using a kind of Superclass for prototyped objects in M2000

In M2000 programming language we use prototyped objects, named groups. Groups are groups of any kind of variable, array, modules, functions, and other groups. There is a class function to produce groups, but we can make own, and this example has a class function as a lambda function. Lambda functions are a special kind of object too, because are not pointer type object (as exposed to user), and has a list of closures plus a function definition, all of them as "value". So if we assign a lambda to a name (a lambda one, or a new one) then we get a copy. If some closures are pointer typed objects then pointers copy too. In M2000 arrays can be pointer typed objects or not. A Dim A(10) make a unique object, but A=A() make A a pointer type array to A(), but A=(1,2,3) change pointer A to point to three items "auto array".
Here in the example bellow, we have a lambda function in an inventory. Inventories are list of keys and values. keys may be numbers or strings (internal are strings), and we can put keys without values (then values are keys). Each inventory has three names. So inventory Superclass, has name Superclass (pointer to inventory), and two for reading values Superclass() and Superclass$(). When a value is a lambda we have to use () if we want to get function output from lambda, or without () if we want a copy of lambda (lambda functions are first citizens in M2000).
So Superclasss("account")(Superclass) is a calling to internal lambda, which we provide a pointer to Superclass. We need it because we want to keep it inside the group that returns from this function, for later use. When Module Inner ends, any name defined there erased. So name Superclass erased, but because there are groups who points to inventory, inventory behind Superclass remain alive.

We need a superclass only for things we want common, and hold a state also. Because M2000 objects like group are prototyped objects, they are typeless, with no class of anything, so how we can "Extend" any of them.
To extend a group (a named group) we can add anything using block Group name {} or by merging a copy from another group using "=". So if B is a group then:
A=B
1) A is a copy of B A if A is new name
2) is a merge to A from a copy of B
In an Array, or Stack, or Inventory, a return value as group, through old group, and not merge or copy. A return value is a copy of group, nameless, so only pointer copied. Pointers for any kind are hidden. We can use them, but we don't read them as numbers.
So A(0)=B if B is a group means
A(0) get a copy o B, and anything before just through it (destroyed).
If a group Alfa has variable (a public one) X then A.X is variable like any variable in module. If Alfa prepared in module then Alfa.X=10 is a proper assign for new value. If Alfa read from stack, and module is a group module then Alfa.X<=10 is the proper assign for value (else we define a local Alfa.x only, and perhaps we can work, but real Alfa.X can't changed, and maybe this can be a problem). We can pass any variable of group (if is public) by reference using & before name. So &Alfa.X is the weak reference of variable.
How we can pass by reference group members? Because a named group is an object without data and data exist in separate variables with name of group, a dot and then name of member. When we get a closed group, then a list from group object used to pack all data in one place, inside group. So a group as an array item, has data inside, hidden from system. A named group has no hidden data to system (but may have private data, for programmer).

When we use For Object {}, with a closed object (in an array or in inventory) we open a group (copied to a named group) and at the end the group closed and copy this pointer back again. Because Blocks are executed without any thread stopped them, is guaranteed that all the work can be done without a second read happen to closed group.

So this is an average example:



Form 100, 60
\\ In M2000 we use prototyped based objects as groups, and normal predefined objects as Inventories
\\ Inventories, Arrays (without parenthesis) and Stacks are objects wotking with pointers, so we can reference to them from more than one site
\\ These objects stay alived if there are pointers to point to them.
\\ Pointers for those objects can't be null, and can change to point something of the same kind.
\\ There is a garbage collector for these objects, and involved when we put something with pointer to something with pointer (to prevent memory leaks)
\\ groups are bond to a name, or can be holded in a inventory, Array and Stack as nameless groups.
\\ A group can't be holded in two places, no pointer exist for groups. So a group can't point or reference to self.
\\ Inteprerter works only with named groups, and if needed can give a name in a nameless group (and then expose data/methods to system)
\\ we can make named reference to a named group only. Only a fresh variable can be made a refernece
\\ A reference is alive if the referenced group is alive. A reference can't change to other group.
\\We can't use a group variable to reference to a group.  We can use arrays, or other "group holders" to hold groups in groups.
\\
\\ We can use weak references if we know that referenced names exist before we use this type of reference
\\
\\ Simple reference through puting weak reference to stack. &k is a string type holding the weak reference - the actual name of k.
K=1
Push &k
\\ so here k1 link to k, we can't link it to something else.
Read &k1
k1++
Print K=k1
\\ in one line we can link k to k2
Link k to k2
Print k2=k1
w$=weak$(k2)
\\ see dot after $ symbol
w$.++
Print eval(w$.)=3 , eval(&k1) \\ because &k1 is a weak reference
\\ we can make k$ as string  - and k exist as number
K$="Hello"
Kw$=Weak$(K$)
\\ This print weak reference, not value of k$
Print Eval$(&k$), kw$, &k$
\\ This print "Hello"
Print Eval$(&k$.), Eval$(Kw$.)

\\ This is an example of using a Superclass, which isn't a class, but work as the common class,
\\ with some fields, one for making counting objects, one as an array, and some others as common values.

Module Inner (FirstIndex) {
      Inventory Superclass
      Append Superclass, "classvariable" :=100, "flag":=False, 100:=("Yes","No"), 50:="Superclass"
      Append Superclass, "account":=lambda N=FirstIndex (self)-> {
      \\ self is just a parameter
      \\ interpreter convert to statement: Read self
            Group m {
            Private:
                  inventory superclass
            Public:
                  \\ index is read only property
                  Property index {value}=N
                  Group index {
                  Private:
                        id=rnd
                  Public:
                        Group Something {
                              Value {
                                    link parent id to id
                                    =id
                              }
                        }
                        Module PrintId {
                              Print .id
                        }
                  }
                  Function copy_new {
                              =.superclass("account")(.superclass)
                  }
                  Module ChangeToTrue {
                        Return .superclass, "flag":=True
                  }
                  Module PrintData {
                      Print .superclass$(50),"index=";.index, Array$(.superclass(100), random(0,1))
                      Print .superclass("classvariable"), .superclass("flag")
                  }
                  Set {
                        Read This
                        group m {
                               group index {
                                     private:
                                     id=rnd
                               }
                         }
                         \\ if we don't use let then we get infinite loop
                         \\ because this call set
                         \\ using let we do two things: Push m : Read This
                         \\ which do a merge only
                         let this=m
                  }
                  Class:
                  \\ a class part can't copied in =m (see last statement)
                        Module SetSuperClass (.superclass) { }
    
            }
            \\ just copy a pointer to m.superclass
            m.SetSuperClass self
            N++
            \\ if m return a value then we have to use Group(m)
            \\ but here is not used (if we place a Group(m) isn't a fault)
            =m
      }
    
      Print "Items in inventory Superclass:"; Len(Superclass)
      M1=SuperClass("account")(SuperClass)
      Print M1.index
      M2=SuperClass("account")(SuperClass)
      Print M2.index
      M3=M1.copy_new()
      Print M3.index
      M3.PrintData
      Return Superclass, "classvariable":=500
      M3$=weak$(M3)
      \\ M3$ is a weak reference (we can make weak references for items in arrays)
      \\ they resolved only in execution
      \\ to read values we have to use Eval() or Eval$() - these too resolve weak references
      M3$.PrintData
      M3_copy=M3
      Print eval(M3$.index)=M3_copy.index,  ">>>>>>";eval(M3$.index.something)
      M1.ChangeToTrue
      M2.PrintData
      \\ we can push an array of M1 through M3 - note M3_copy  not needed here
      Push (M1, M2, M3)
}
\\ So now we call Inner Module
Inner 100
Print "Out of Inner module"
\\ Now Superclass is not exposed
Read A()
\\ for loop is not the same as for object {}
For i=0 to Len(A())-1 {
      For A(i) {
            .PrintData
            Print "original=";.index=A(i).index and .index.something=A(i).index.something
            .index.Printid
      }
}
Print "so now check how we know if a merge happen"
\\ we can get a copy of A(0)
M0=A(0)
Print M0.index.something, A(0).index.something
Print M0.index, A(0).index
Print "original=";M0.index=A(0).index and M0.index.something=A(0).index.something
\\ now we merge A(1) to M0 so we get a not original copy.
M0=A(1)
Print M0.index, A(1).index
Print M0.index.something, A(1).index.something
Print "original=";M0.index=A(1).index and M0.index.something=A(1).index.something
M1=A(0).copy_new()
M1.PrintData
Print M1.index=103 \\ true
Print "Current value of id=";M1.index.something
\\ this is a way to change a private variable, in an inner group in M1
\\ first we  open a for this block for temporary definitions
For This {
      \\ MM is a temporary definition, because created in a block For object {}
      Link M1 to MM
      \\ we can make MM to reference M1
      \\ we can add to MM a new module to Index inner group
      Group MM {
            Group Index {
                  module change_id (.id) {}
            }
      }
      \\ so now we can execute it
      MM.index.change_id 10000
}
\\ now MM be erased, but M1 get the new value for private index.id
Print format$("Changed id={0}", M1.index.something)

\\ we can do the same for a group as an array item
Print A(2).index.something
\\ We can't link as closed group, we have to open first using For {}
For A(2) {
      \\ MM is a temporary definition, because created in a block For object {}
      \\ there is a *M1* for A(2), but we can't use the name of it
      \\ So we can use This as the name of A(2) open group (named group)
      \\ this group closed back at the end of For object {}.
      \\ using copy in - copy out mechanism (also copy pointers too)
      link This to MM
      Group MM {
            Group Index {
                  module change_id (.id) {}
            }
      }
      \\ so now we can execute it
     MM.index.change_id 10001
}
Print A(2).index.something
\ \ The only way to use real pointer to Group is by using Swap, and Swap two groups in an array
Swap A(0),A(2)
Print "A(0)"
A(0).PrintData
Print "A(2)"
A(2).PrintData
Dim A(5)
A(3)=A(0).copy_new()
\\ we can use any group to define a new group
A(4)=A(1).copy_new()
A(3).PrintData \\ index 104
A(4).PrintData \\ index 105