Εμφάνιση αναρτήσεων με ετικέτα Οδηγίες Χρήστη. Εμφάνιση όλων των αναρτήσεων
Εμφάνιση αναρτήσεων με ετικέτα Οδηγίες Χρήστη. Εμφάνιση όλων των αναρτήσεων

Σάββατο 25 Νοεμβρίου 2017

Dynamic & Lexical Scope in M2000, and lifetime of entities

Dynamic Scope
This example run in a module
(print 3 and 1)

\\ x isn't global
\\ but x is visible from subs in this module
x=1
f()
Print x
sub g()
      Print x
      x=2
end sub
sub f()
      local x=3
      g()
end sub


Lexical Scope
This example run in a module and return 1 and 2. We need to make Global x and Global g(), and also to change a global value we need to use <= and not a  simple =
(print 1 and 2)

Global x=1
Function Global g {
     Print x
     \\ for globals we have to use <=
     x<=2
}
Function f {
      \\ x=3 make a local x, or if x exist as local then change value to 3
      \\ but local x make always a new variable
      \\ so Local x,x,x make three x and last one is visible.
      Local x=3
      Call g()
}
Call f()
Print x


Lexical Scope using only one global x
(print 1 and 2)

Global x=1
Function f {
      Function g {
           Print x
           \\ for globals we have to use <=
           x<=2
      }
      x=3
      Call g()
}
Call f()
Print x

Dynamic Scope using functions
(print 3 and 1)
We use Call Local which call functions with parent name. So each time scope is the same. Here x=3 in Function F can't make a local x, we have to use Local x=3.
x=1
Function g {
     Print x
     x=2
}
Function f {
      Local x=3
      Call local g()
}
Call Local f()
Print x

Dynamic Scope using Global to shadow global


\\ Dynamic scope
Global b=5
Function Global foo {
      a=b+5
      =a
}
Function bar {
      Global b=2
      =foo()
}

Print foo()
Print bar()


Print 10 and 7

Lexical (Static) Scope  (just not using global b in bar)


\\ Static Scope
Global b=5
Function Global foo {
      a=b+5
      =a
}
Function bar {
      b=2
      =foo()
}

Print foo()
Print bar()

print 10 and 10




Lifetime of an entity

Entities are named values/containers (we say variables, but include constants) and named code
A name scope begins where first time interpret assign value, or perform a declaration which automatic assign zero/empty_string/empty_array values. This is not true for subs, because subs names searched once from the bottom, when we call them, and in an another call interpreter use a hash table to get entry point for them. The same happen for labels, numeric or named, for Goto and Gosub (simple routines, exactly like Basic's), interpreter use a hash table to find them. These hash tables cleared after the use of an Inline statement. Lables/Subs can called from any direction. Block {} of code are exit using goto.


Function something {
      \\ test simple goto (else 200 is same like else goto 200)
      \\ and simple routines using gosub
      x=1
      {
            gosub alfa
            {
                  gosub alfa
                  {
                        gosub alfa
                       if x>1 else 200
                  }
                  Print "ok2"
            }
      200 Print "ok3"
      210 x++
      220 if x<5 then loop
      }
      Print "ok4"
      exit
      alfa:
      Print "ok"
      Return
}
Call Something()






A clear command inside a module/function clear variables from that module/function and newer. A clear command in manual mode clear every variable, global and static, and perform garbage collection. Using command Flush Garbage we can perform garbage collection without clearing variables.
Named entities have lifetime of parent (entity which defines entities). Global entities have same lifetime as normal.

Threads (have no name but a handler) normally have also lifetime same as parent module/function, except for those threads that starts in a thread, so they have no parent.

Com external objects, or internal Com like GUI elements (Forms, Controls), and properties for them (early bindings to objects), have also life time  same of parent. Pointers for them are "smart" which means that one pointer only exist for one object, and every time we need a pointer as a copy of pointer, we get just  a reference to first name which hold the actual pointer
Groups (standard objects of M2000), are value types and can be named, so have lifetime of parent, or can be copied in containers, and then can be leave until a new group replace them or when containers get out of scope.
Arrays, Inventories (inventory, an association list with hash table), Stacks are three type of containers, and handled by pointers. Arrays are two kinds, those arrays that can be copied, and those that can be passed by pointer. In each case, using stack to pass an array always passed a pointer to array, end when we pop the array we get a pointer to a copy of array or a copy of pointer to array depends of where we read, if we read pointer to a name with parenthesis "()" then we get a copy (shallow copies, for containers in containers, but copies for groups in containers), else if we read a simple name (without ( or  % or $ as last symbol) we get pointer to array. There is no null pointer for containers. There are empty containers, or a starting value of a double, and then this can be change to a pointer to a container, and can get any type of container, but no a double value (is a Missing Object error)

Event objects are value types, so we get always a copy, and these leave like any named entity. Events hold code for functions, and sometimes we want some functions to run in a group context because are functions belonging to groups, or in module context, changing function to act as module, replacing the name appropriate and automatic (Using a Lazy$() function, see example below), we say that these functions utilize another context (parent group or parent module). If we get a copy of event and later call event (means raise event), calling all functions inside (multicast), and if some of them utilize another context, maybe we get an error if any of these functions is out of context.

Lambda functions may have closures as local for them, and any call of them (recursive call using Lambda() or lambda$() as name of function). Lambda functions are value types, we can copy them. Containers as closures copied by pointer (so they passed by reference). A lambda function has own context as a black box. Each lambda function has a name without parenthesis to use it as an entity and a name with parenthesis to call the included function. If we pass by reference a lamda function, as function reference then we miss the object, and all variables inside lambda object. If we pass by reference the lambda object then we get all, closures and the included function. If we pass by value we get a copy of all (except those closures that are pointers, so we get a copy of that pointers)

Lifetime for a Bag for static variables. Each call from a parent to a child may hold a bag of static variables. These variables can be cleared if parent bag cleared. Functions that are called by Events objects that are used by forms, feed global bag,so we have to clear global bag manual from M2000 terminal.
Static variables created per calling object (first object is the terminal's execution object, and all other are children, or children of children). So static variables can be erased if a clear command executed at parent, except for terminal, which has no parent, and clear then clear all static variables.

Static modules/functions are those in level 0, and are visible in terminal mode. They can erased using New, erase modules/functions list, or Start (warm reset for M2000 runtime environment), or using remove for removing the newest of them. They can change definition using Set Load (like load from terminal) or using load from terminal. Also we can change definition by editing it as it run, but edited definition can be used next time, or when we call a sub or using goto to a label (which first time we use in the first code, after edit command)

A Sub is named code in module or function, and uses parent context, all parent context is visible in sub, but also can make temporary variables and modules/functions. Subs have no static variables, they use parent static variables.
A For object {} block define a block for temporary definitions. A for This {} block can be used any where for temporary definitions.


Scope
Static A after local A is an error, we can't mix static and local with same name. Local A after static A has same effect but no error happen. If we perform A++ then local A change, not static A, and always we read static A first.


A Local A shadow Global A, for lifetime of local one. A second Local A shadow old Local A, for life time of second one.

Any local variable is visible in module/Function/Sub/For object block, where defined
Any Global variable is visible from where we define it., but we can shadow it with a local with same name.

If we use global const x=123456 then we have to use inside a child module "const x" without value to make a reference as local x to prevent a new local by using X=10

Check Visibility by code

We can check if a variable is visible using Valid(). We can check if a module/function exist using module$(). We can't examine if a sub exist.
local modules/functions are not visible by child modules and parent modules. This hold for variables.
We can decorate a module by replacing a inner module definition at calling, and for that call only, using a parent module. Module Decoration is a way to handle code inside module at calling. functions and subs can't decorated.
We can load libraries, as local/global modules/functions. Loading as global may we change existing globals, if we use Set Load nameoledib. To get global without altering global with same names, we have to explicity define modules as globals in library. So we load library as local and get modules as globals, shadowing any global with same name.

A group in a container has all data encapsulated (closed for altering). When we use it, interpreter open it using a hidden name, and at the end all data closed again. We can define private and public members for groups. Also we can define class members (members that is no actual members of group, but act as members). A local member (variable) in a closed group always intialised at the opening, and never save value, but can alter value in lifetime of opening. A local member in a named group (not in container) never be referenced, among other members when we pass group by reference (and this is a way to check if a group is a reference or not). A reference of group is a new group which have all members passed by reference, on another group. (it is not a pointer to group). In a reference of group we can add members, but this addition wasn't performed in first group)/

A SuperClass is a group with an internal pointer to a closed group, with no members. When we asign a superclass or a group with superclass in a name then performed a copy of all members of superclass (except those members that we defined as unique to super class) plus a setting to group internal superclass pointer to that superclass. A group may have many sub groups and each sub group may have own super class. Superclasses can't hold pointer to any superclass. When we use For SuperClass {} to access superclass, interpreter open superclass, but always allow one time opening,and if second time happen before first closed then an error occur. Superclass used for holding common members. If no pointer to superclass exist then superclass removed.

A Class definition is a function which return a group. A class is always global, except those class definitions inside classes or groups which are member to group.

Group A {
      X=10
}
Module alfa (&B) {
      Group B {
            Y=30
      }
      Module delta (&C) {
            Group C {
                  Module XY {
                        Print .X*.Y
                  }
            }
            C.X/=2
            Push C
      }
      delta &B
}
alfa &A
A.X+=100
Print A.X
Read AA
AA.XY
Rem : list  \\ unhide List to see list of variables)



Example for changing code using editor as code runs.
edit a
(open editor for module A)
inline "edit "+module$
goto 100

(press esc to exit)
When run this, using A as command in M2000 terminal, you get editor with context above. So delete all and leave first line empty then write from second line 100 Print "Ok"
\\ one line left
100 Print "Ok"

(Press Exit) and now you get Ok because interpreter search from second line for label 100, and found it. If no label 100 exist then goto used as exit. A goto to a not exist label is an exit (same for a Gosub to a non exist label, but Gosub to a non exist sub is an error).



\\ Example of using user event in a Form
\\ use clear before enter this module to clear all conainers for static variables
clear
Declare alfa form event aa
X=1
Function that {
      Stack
      Static Y=100
      ' show all messages which they don't use arguments by reference
      ' using Lazy$() we push this function at the same lexical scope as
      ' parent, which is the same for X, so X is visible
      X++
      Y++
      Print X, Y

}
Function that1 {
      Static X=10000
      Stack
      X++
      Print X
      if X>10020 then {
            Drop : Read &Form1
            Print Type$(Form1)
            Method Form1, "CloseNow"
      }
      ' get same message
      ' but X isn't visible
      refresh
}
Event aa New Lazy$(&that()), &that1()
method alfa,"show", 1
Declare alfa nothing

Πέμπτη 25 Μαΐου 2017

Αναθεώρηση 4 (Έκδοση 8.8) και οδηγίες για τον διορθωτή

Ίσως η τελευταία αναθεώρηση για αυτή τη σεζόν (από Νοέμβριο πάλι), λόγω άλλων εργασιών. Πιθανά bugs θα δώσουν νέα αναθεώρηση αλλά δεν θα γίνει κάποια επέκταση στο κώδικα.

Έγινε έλεγχος για όλα τα προγράμματα - δοκιμών- και η συγκεκριμένη αναθεώρηση θεωρείται η πιο καλή και από άποψη ταχύτητας.

Στον διορθωτή μπήκαν μερικοί αυτοιματισμοί:
  • Με ctrl+enter μπαίνουν αυτόματα τα άγκυστρα { } και ο δρομέας πάει στην γραμμή ανάμεσα με υπολογισμένη την αριστερή εσοχή.
  • Όταν πατάμε το πλήκτρο  παρένθεση "(" μπαίνει αυτόματα η συνέχεια ")" και ο δρομέας βρίσκεται ανάμεσα.
  • Όταν πατάμε το διπλό εισαγωγικό " μπαίνει αυτόματα η συνέχεια " και ο δρομέα βρίσκεταΙ ανάμεσα.
Για υπενθύμιση μόνο (υπήρχαν πριν):

Τα ctrl F2/ctrl F3 (και shift F2/ shift F3) ανοίγουν το διάλογο για την αναζήτηση πάνω και κάτω.

Συνεχόμενα F2/F3 πάμε στο επόμενο κατά τη διεύθυνση. Αν μαρκάρουμε μια λέξη με απλό κλικ και πατήσουμε ένα από τα F2/F3 τότε αυτή τη λέξη αναζητούμε.

Μπορούμε να αλλάξουμε κεφαλαία σε πεζά σε κάποια γράμματα σε  μια λέξη (εδώ οι τόνοι έχουν σημασία), και με το F4 θα γίνουν οι αλλαγές σε όλο το τμήμα/συνάρτηση (στο τμήμα που βλέπουμε το όνομα πάνω αριστερά).

Αλλαγές πολλαπλές (με πολλαπλό undo) κάνουμε με το F5 αφού έχουμε επιλέξει με απλό κλικ (αριστερό) μια λέξη.

Το ctrl+Z είναι το Undo, το ctrl+Y το Redo.

Το Tab μετατρέπεται αυτόματα σε διαστήματα (6 διαστήματα)
Με Tab σε πολλαπλές γραμμές μετακινούμε όλες τις γραμμές δεξιά βάζοντας περισσότερη εσοχή
Με ctrl+Tab βγάζουμε εσοχές (δουλεύει και για πολλαπλές γραμμές). Αν πατάμε συνέχεια τον συνδυασμό αυτό σε μια ομάδα γραμμών στο τέλος θα βγούν όλες οι εσοχές, ακόμα και αν είναι μικρότερες από 6 διαστήματα

Στην αντιγραφή στο πρόχειρο γίνεται εξαγωγή σe HTML και οι εσοχές μετατρέπονται σε non breaking space

Μπορούμε να βγούμε από το διορθωτή ακυρώνοντας όποιες αλλαγές έχουμε κάνει με shift-F12

Ο διορθωτής δεν έχει πρακτικό όριο χαρακτήρων στην γραμμή. Δεν υπάρχει οριζόντια μπάρα ολίσθησης (υπάρχει κρυφή κάθετη, που φαίνεται όταν είναι από πάνω το ποντίκι). Η μετακίνηση οριζόντια γίνεται αυτόματα. Μπορούμε με τα Home και End να μετακινηθούμε άμεσα στην αρχή ή το τέλος. Το Home εναλλάσεται, μια φορά στην αρχή της γραμμής και μια φορά στην αρχή χαρακτήρων πάνω στην γραμμή (μετά τα αρχικά διαστήματα δηλαδή)

Όταν γράφουμε στον διορθωτή αυτόματα μεταφέρει την γραμμή που γράφουμε στο κέντρο. Μπορούμε να ολισθήσουμε πάνω κάτω το κείμενο χωρίς μετακίνηση του δρομέα, οπότε με ένα πάτημα πλήκτρου (και με βελάκια γίνεται) επανέρχεται η γραμμή διόρθωσης στο κέντρο. Η ολίσθηση αυτή γίνεται ή με την μπάρα ολίσθησης ή με κράτημα πατημένου του αριστερού πλήκτρου του ποντικιού και μετακίνηση (αντίστροφη από την μπάρα ολίσθησης, σαν να σπρώχνουμε το κείμενο, δηλαδή να πατήσουμε και σπρωξουμε προς τα πάνω θα δούμε τις γραμμές να ανεβαίνουν).

Τα πλήκτα PageUp/PageDown και το ροδελάκι του ποντικιού (και αυτό κάνει PageUp/PageDown) αλλάζουν ανά μισή σελίδα (πάντα δείχνει ο διορθωτής μια γραμμή από τη μισή σελίδα που φεύγει) και μετακινούν τον δρομέα.

Τα ctrl-x, ctrl-c, ctrl-v λειτουργούν κανονικά. Το ins δεν χρησιμοποιείται εδώ.

Τα PageUp/PageDown/Home/End χρησιμοποιούνται με το Shift για να μαρκάρουμε κώδικα
Όπως και τα βελάκια με Shift μαρκάρουν κώδικα. Ο μαρκαρισμένος κώδικας είναι λευκός με διαφορετικό φόντο.

Ο χρωματισμός κώδικα γίνεται στον διορθωτή, άμεσα. Η Μ2000 δεν αποθηκεύει τη χρωματική πληροφορία στα τμήματα και τις συναρτήσεις, επειδή ο χρωματιστής είναι πολύ γρήγορος! Χρωματίζει ότι πρέπει να δείξει, και κρατάει τοπικά την πληροφορία. Ορισμένες φορές μπορεί να εκτιμήσει τον χρωματισμός διαβάζοντας προηγούμενες γραμμές! Δουλεύει με αναδρομή, οπότε χρωματίζει κατάλληλα και τις εκφράσεις.

Χρήσιμα πλήκτρα στην διόρθωση:

  • Ctrl+F1 (υπάρχει και στο αναδυόμενο μενού - σε Linux το Ctrl+F1 δεν δουλεύει), ανοίγει τη βοήθεια για την επιλεγμένη λέξη. Η βοήθεια εκτός από αυτό που δείχνει, μπορεί να χρησιμοποιηθεί για να γράψουμε (μέσα στο κείμενο που εμφανίζει) μια λέξη και με κλικ πάνω σε αυτήν κάνει αυτόματα αναζήτηση. Επίσης μπορούμε να αντιγράφουμε κώδικα από τη βοήθεια στο διορθωτή. 
  • F1 αλλαγή από αναδίπλωση σε μη αναδίπλωση παραγράφου (γίνεται πολύ γρήγορα και δεν χάνεται η θέση του δρομέα)
  • F12 ανοίγει την βοήθεια η οποία τώρα δείχνει μια λίστα τμημάτων (όσα είναι γενικά, ή ήδη φορτωμένα) και με κλικ σε αυτά βλέπουμε το κώδικα (όχι χρωματισμένο). Η βοήθεια έχει στην άκρη δεξιά κρυμμένη την μπάρα ολίσθησης, αλλά δουλεύει και με ολίσθηση με  κράτημα του πλήκτου πατημένο και τράβηγμα!
  • F6, F7, F8 είναι οι σελιδοδείκτες και ορίζονται/επιλέγονται/διαγράφονται με ένα πλήκτρο. Όταν πατήσουμε το F6 μια φορά και αυτό δεν έχει κάτι στην "μνήμη" τότε ορίζει τη θέση του δρομέα (βάσει παραγράφου, δηλαδή αν βάλουμε άλλες πριν δεν χάνει την συγκεκριμένη παράγραφο). 'Οταν πατήσουμε πάλι το F6 από θέση εκτός της θέσης της μνήμης μας πάει εκεί που δείχνει. Αν πατήσουμε πάλι το F6 στη θέση της μνήμης που έχει τότε την καταργεί. Σε κάθε περίπτωση βγαίνουν μηνύματα για το τι κάνουμε και επίσης όταν ο δρομέας είναι στη θέση κάποιου σελιδοδείκτη τότε αυτό φαίνεται στην επικεφαλίδα του διορθωτή.
  • F9 μέτρηση λέξεων (δεν μετράει αριθμούς και σύμβολα)
  • F10 Εμφάνιση μη εκτυπώσιμων χαρακτήρων (κενών/σύμβολο παραγράφου). Βοηθάει να δούμε αν έχουμε αφήσει κενά στη γραμμή -(ο διορθωτής δεν πειράζει τα κενά)
  • F11 αποχρωματισμός+ αλλαγή εμφάνισης επικεφαλίδας (4 καταστάσεις οι οποίες έρχονται η μία μετά την άλλη σε κάθε πίεση του F11)
  • Esc βγαίνουμε από το διορθωτή κανονικά!
Με την εντολή Φόρμα ορίζουμε το μέγεθος των χαρακτήρων της οθόνης βάσει διαστάσεων:
Πχ. η παρακάτω εντολή θα φτιάξει μέγεθος χαρακτήρων για 40 χαρακτήρες (μη αναλογικής γραφής) με 25 γραμμές και αυτόματα θα έχει φτιάξει το διάστιχο ώστε να καλύψει όλο το παράθυρο της κονσόλας.(το παράθυρο μπορεί να μετακινηθεί και να αλλάξει θέση, αλλά δεν θα το δούμε εδώ αυτό).


Φόρμα 40, 25
Form 40, 25
για τμήμα:
Σ αλφα
edit a
για συνάρηση:
Σ αλφα()
edit a()

σκέτο το Σ ή το Edit ανοίγει το διορθωτή με τις καταγραφές των άμεσων εντολών και μπορούμε εκεί να τις διορθώσουμε ή να προσθέσουμε. Όταν θα βγούμε με Esc τότε με τα βελάκια άνω και κάτω επιλέγουμε μια εντολή (μια παράγραφο που μπορεί να έχει εντολές η μία μετά την άλλη με διαχωρισμό με άνω και κάτω τελεία)

Παρασκευή 10 Φεβρουαρίου 2017

Tutorial for Groups in M2000

To run this code, please copy it in module in M2000 environment, (M2000.exe): Start the environment, type Edit A, copy this program from browser, press Esc to return to CLI (command line interpreter), and type A and press Enter.

Group Abc { X=10, Y, Z$="Yes"}
For This {
      \\ This is a block for temporary definitions
      Group Temp {X=12, W=13}
      \\ we make a copy as "float" group
      \\ float groups are groups in stack and arrays and the result of any function which return group
      \\ non float group, static, is a group with a name, and position in module.
      \\ Group Temp erased after the exit of this block
      Push Temp
}
\\ now reading a group from stack to named (static) group means Join
Read Abc


\\ We see that we get new value for Abc.X, and a new variable Abc.W
For Abc {
      Print .X, .Y, .W, .Z$ \\
}
List   \\ we see all 5 variables group and four group variables


\\ Why is 5? Because in a named group each element exist as it is not in a group.
\\ We can't delete a already defined variable (until module exit) and this happen for groups.
\\ So groups can take more variables and other (Arrays, Functions etc), temporary or not.
\\ We can pass a variable from group by reference without passing the group.
Module CheckRef {
      Read &M
      M++
      Print M \\14
}
\\ by referenece one variable from group Abc
CheckRef &Abc.W
Print Abc.W \\14


\\ We can Add function to group
Group Abc {
      Function CheckIt {
            \ one dot mean This.
            This.X++ \\ change some state
            .Y+=3
            =.X*.W+.Y
      }
}
Print Abc.CheckIt() \\ 185
Print Abc.CheckIt() \\ 202
Print Abc.CheckIt() \\ 219


\\ We can pass a function of Group in a module, and we can change state inside it,
Module PassFunc {
      Read &F()
      Print F() \\ 236
}
PassFunc &Abc.CheckIt()


Module PassAll {
      Read &AbcRef
      \\ now AbcRef is a group
      For AbcRef {
            Print .X, .Y, .W, .Z$
      }
      List  \\ there are 5 more variables
}
PassAll &Abc
\\ Some times is better to pass a weak reference only
\\ this can be done using Weak$() or & operator
\\ but important is what we have in Read


Module WeakPass {
      Read A$
      For A$ {
            Print .X, .Y, .W, .Z$
      }
      \\ we can use A$ to call modules or change variables
      A$.X+=10
      Print Eval(A$.X) \\ we must read using Eval(), or Eval$() for strings
      Print Eval(A$.CheckIt()) \\ 393      
      List  \\ there are NO 5 more variables, only a new string variable (weak reference)
}
WeakPass &Abc
For This {
      \\ temporary use
      Local M, I=Group.Count(Abc)
      For M=1 to I : Print member$(Abc, M), member.type$(Abc, M) : Next M
      \\ we use New so we shadow any variable with same name
      Read New From Abc, X, Y$, Z$, W
      \\ these are references. If not match type (array or variable) we get error
      \\ Here we make string the reference to Y
      Print X, Y$, Z$, W
      Z$="it is a reference to Abc.Z$"
      Y$="1500"
}
List
Print Abc.Z$, Abc.Y \\  "it is a reference to Abc.Z$"   1500


\\ until here____________________________________________

A small program:

\\ We can make reference from number to string variable
A=10
Link A to A$
\\ feed A$ from A
For A=A to A+10 {
      Print A$;
}
Print
\\ feed A from A$
A$="1212.1292"
Print A*100


Advanced Program
M2000 has no interfaces for Groups (Groups are like javascript objects, but not exactly). So here is a program to implement "interface". The task not only needed for give interface to group for particular task, but we need to take feedback, so we must find a way to add functions to groups and from what we get, we need to take something back.
Our feedback is kept in counter in each item (circle or rect). The interface is kept in another group named Geometry. We use a function type$() to define "soft" type. So function interface choose the right group to export. This group then joined to original group

Clear  \\ variables
Flush  \\ Stack for values


Class Circle { Radius, counter
      Function Type$ {="Circle"}
}
Class Rect { Height, Width, counter
      Function Type$ {="Rect" }
}
Group Geometry {
Private:
      Group Geometry_Rect {
            Function Perim { =.Height*2+.width*2}
            Function Area { =.Height*.width}
      }
      Group Geometry_Circle {
            Function Perim { =.Radius*2*pi}
            Function Area { =.Radius**2*pi}
      }
Public:
      Function Interface {
            Read What
            If What.Type$()="Circle" then {
                  =.Geometry_Circle
            } else {
                  =.Geometry_Rect
            }
      }
}
\\ main
Base 1
Dim A(10)=Circle(), B(10)=Rect(), Mix(10)
A(1).Radius=5 \\ we can put .Radious=5 inside For A(1) { }
For A(1) {
      measure(&this, &Geometry.Interface())
}
For B(1) {
      .Width=3 : .Height=4
      measure(&this , &Geometry.Interface())
}
Print A(1).counter, B(1).counter
Mix(1)=A(1)
Mix(2)=B(1)
For Mix(1) {
      measure(&this, &Geometry.Interface())
}
For Mix(2) {
      measure(&this , &Geometry.Interface())
}
Sub measure(&G, &Geometry()) \\ group refer and function (from group) refer
    G=Geometry(G) \\ temporary union with group Geometry
    Print format$("Type     {0:12}", G.Type$())
    Print format$("Area     {0:3:-10}mm",G.Area())
    Print format$("Perimeter{0:3:-10}mm",G.Perim())
    G.counter++ \\ this is feedback
End Sub

___________________________________
And here is another variant of  previous program. Now we don't use a Group Function to return group, but a function. From Sub measure we see everything from module, so we see Function Geometry. More simple now:

Clear  \\ variables - help on developing this
Flush  \\ Empty Stack for values


Class Circle { Radius, counter
      Function Type$ {="Circle"}
}
Class Rect { Height, Width, counter
      Function Type$ {="Rect" }
}
Function Geometry {
            Read What
            If What.Type$()="Circle" then {
                  Group Geometry_Circle {
                        Function Perim { =.Radius*2*pi}
                        Function Area { =.Radius**2*pi}
                  }
                  =.Geometry_Circle
            } else {
                  Group Geometry_Rect {
                        Function Perim { =.Height*2+.width*2}
                        Function Area { =.Height*.width}
                  }
                  =.Geometry_Rect
            }
}
\\ main
Base 1
Dim A(10)=Circle(), B(10)=Rect(), Mix(10)
A(1).Radius=5 \\ we can put .Radious=5 inside For A(1) { }
For A(1) {
      measure(&this)
}
For B(1) {
      .Width=3 : .Height=4
      measure(&this)
}
Print A(1).counter, B(1).counter
Mix(1)=A(1)
Mix(2)=B(1)
For Mix(1) {
      measure(&this)
}
For Mix(2) {
      measure(&this)
}
Sub measure(&G) \\ group refer and function (from group) refer
    \\ sub see anything in module, so can see Geometry()
    G=Geometry(G) \\ temporary union with group Geometry
    Print format$("Type     {0:12}", G.Type$())
    Print format$("Area     {0:3:-10}mm",G.Area())
    Print format$("Perimeter{0:3:-10}mm",G.Perim())
    G.counter++ \\ this is feedback
End Sub



Previous program, with a change. We can pass function G.type$() by reference of each group to Geometry() and not all group, to choose the functions we want to bind to G.
When a function of Group passed by reference we have a new function (a copy of Group's function, with a proper weak reference to Group, so any identifier inside function with a dot precede it, connect by weak reference to actual members of group. All functions and modules in a group have a weak reference to group. When a Group has no name (as those in an array), no weak reference exist. When we open a Group from array item, then interpreter make a new group with a hidden name and make all members using this name, and pass references to this group to modules and functions, and the identifier This refer to this hidden name.
So passing function only, we don't make a copy of group, just a copy of one function.

How reference for Group works

Here we pass a reference of This to sub Measure(), so  interpreter make all members, as identifiers and pointers to actual members of This (we say This but this is an alias, of a copy in of Mix(i)). But G Group has not a pointer to This, has a list of members, as G.identifier_of_member. Calling Geometry() we pass G.type$() which points to This.type$() so we get the weak reference of This. So any bind to G from Geometry, listed only to G and not toThis. So we have two lists, as two objects, with two set of members, but for G some members points to This members, and some aren't references but are new members to G.

From 8.2 we can make the Read Parameter1,...  as  Function Name (Parameter1,...) {function code  }. Interpreter get the parameter list and insert a Read command as first line.
Another change is the For.. to... {} for Mix() array.
Command Modules ? display modules as they are in Modules/Functions List (if not hidden, or private)




Clear  \\ variables - help on developing this
Flush  \\ Empty Stack for values

Class Circle { Radius, counter
      Function Type$ {="Circle"}
}
Class Rect { Height, Width, counter
      Function Type$ {="Rect" }
}
Function Geometry (&What$()) {
            If What$()="Circle" then {
                  Group Geometry_Circle {
                        Function Perim { =.Radius*2*pi}
                        Function Area { =.Radius**2*pi}
                  }
                  =.Geometry_Circle
            } else {
                  Group Geometry_Rect {
                        Function Perim { =.Height*2+.width*2}
                        Function Area { =.Height*.width}
                  }
                  =.Geometry_Rect
            }
}
\\ main
Base 1
Dim A(10)=Circle(), B(10)=Rect(), Mix(10)
A(1).Radius=5 \\ we can put .Radious=5 inside For A(1) { }
For A(1) {
      measure(&this)
}
For B(1) {
      .Width=3 : .Height=4
      measure(&this)
}
Print A(1).counter, B(1).counter
Mix(1)=A(1)
Mix(2)=B(1)
For i=1 to 2 {
      For Mix(i) {
            measure(&this)
      }
}
MM=Mix(1)
Modules ?
Sub measure(&G) \\ group refer and function (from group) refer
    \\ sub see anything in module, so can see Geometry()
    G=Geometry(&G.type$()) \\ temporary union with group Geometry
    Print format$("Type     {0:12}", G.Type$())
    Print format$("Area     {0:3:-10}mm",G.Area())
    Print format$("Perimeter{0:3:-10}mm",G.Perim())
    G.counter++ \\ this is feedback
    
Modules ?
End Sub




Very Advanced

Here we have an Event inside Group.


Group aa {
Private:
      y=123
Public:
      \\ event need a Read parameter list
      \\ so any function added to Event must have same signature
      Event b { Read x, &counter }
      Function aa (x, &counter) {
            Print x+.y
             .y++
            Print ".y=";.y
            counter++
      }
      Module callme {
            Call Event .b, number
      }
}
Class cbb {
Private:
      y=456
Public:
      function bb (x, &cnt) {
            print x**2+.y
            cnt++
      }
Class:
      Module cbb (.y) { }
}
bb=cbb(456)

Event aa.b New aa.aa(), bb.bb()
mycounter=0
aa.callme 10, &mycounter
aa.callme 10, &mycounter
Print mycounter
Module testme (c) {
      Print Module$ \\ name of module
      Stack  \\ display stack (for values not process stack), now have 5 as top value
      \\ a module get parent stack so 5 passed with stack to c.callme module
      c.callme
}
\\ aa by copy
mycounter=0
testme aa, 5, &mycounter
Module testme2 (&c) {
      Print Module$
      c.callme
}
Print  "Calls number=";mycounter
\\  aa by reference
mycounter=0
Event aa.b Drop aa.aa()
testme2 &aa, 10, &mycounter
Print mycounter
Event aa.b Hold
testme2 &aa, 10, &mycounter
Function SomethingElse (x, &cnt) {
      Print ">>>>>>>>>>>",x, cnt
}
Print mycounter
Event aa.b Clear \\ after Clear we need to Release Event
Event aa.b New aa.aa(), aa.aa(), bb.bb(), bb.bb(), SomethingElse()
Event aa.b Drop aa.aa()
Event aa.b Release
mycounter=0
testme2 &aa, 10, &mycounter
Print mycounter