Δευτέρα, 18 Απριλίου 2016

Κλάσεις από κλάσεις στη Μ2000.

Μπορούμε στη Μ2000 να φτιάξουμε ένα αντικείμενο και να του δώσουμε επιπλέον ιδιότητες ενός άλλου αντικειμένου.
1. Βασική γνώση εδώ είναι ότι ένα αντικείμενο έχει φυσική παρουσία, ενώ μια κλάση είναι συνάρτηση που δίνει ένα αντικείμενο. Γλώσσες όπως η Java έχουν ειδικό λεκτικό το extents που δημιουργεί κλάση με βάση άλλη κλάση. Στη Μ2000 η λογική είναι πιο απλή (επειδή δεν υπάρχουν έλεγχοι τύπων, και θα γίνει αντιληπτό στον αναγνώστη γιατί δεν μπορούν να υπάρχουν τύποι),
2. Βασική γνώση επίσης είναι ότι ο μηχανισμός επέκτασης ενός αντικειμένου  είναι η συγχώνευση. Δηλαδή μπορούμε να συγχωνεύσουμε ένα αντικείμενο σε ένα άλλο. Δείτε όμως ότι πάντα μιλάμε για αντιγραφή με συγχώνευση  Στην κυριολεξία αν θέλουμε το Α να συγχωνευθεί στο Β, τότε θα πάρουμε ένα Β που θα έχει ότι το Α και ότι είχε το Β διαφορετικό, Το Α θα παραμένει ως έχει.
3. Βασική γνώση επίσης είναι ότι η αντιγραφή με συγχώνευση γίνεται μόνο σε ανοικτό αντικείμενο. Κλειστά αντικείμενα στη Μ2000 είναι τα ανώνυμα αντικείμενα, αυτά που επιστρέφουν από συναρτήσεις, και αυτά που βρίσκονται σε πίνακες. Ένα ανώνυμο αντικείμενο μπορεί να βρίσκεται στο σωρό τιμών. Ξέρουμε ότι έχουμε ανοικτό αντικείμενο όταν αυτό υπάρχει σε μια μεταβλητή.

Το παράδειγμα είναι στα αγγλικά:
Η Class (Κλάση) είναι μια συνάρτηση που επιστρέφει κλειστό αντικείμενο (ανώνυμο). Πώς λειτουργεί;
Θα φτιάξει ένα αντικείμενο Some με μια ιδιωτική ιδιότητα count, και με μια μέθοδο increment και μια συνάρτηση toString$
Ας τρέξουμε το πρόγραμμα (έστω είναι σε ένα τμήμα Α, module A)
Καθώς ξεκινάει την εκτέλεση ο διερμηνευτής (δ.) βλέπει τον ορισμό και τον καταχωρεί τοπικά ως συνάρτηση Some(). Δεν ασχολείται με το αν είναι έγκυρος ή όχι, ούτε φτιάχνει κάποιο αντικείμενο.
Μετά o δ. βρίσκει την s, βλέπει ότι δεν υπάρχει τοπική s και δημιουργεί μία άδεια (Empty), και εκτελεί την παράσταση μετά το = για να καταχωρήσει τιμή. Τότε τρέχει η συνάρτηση Some() και δίνει το κλειστό αντικείμενο. Με το που βλέπει το αντικείμενο ο δ. φτιάχνει για το s ότι λέει το κλειστό αντικείμενο, και δίνει τιμές ότι έχει αυτό και το πετάει.
Μένει τότε το ανοικτό αντικείμενο, δηλαδή θα μείνει μια κρυφή μεταβλητή s.count, ένα τμήμα s.increment, μια συνάρτηση s.toString$() και η s που κρατάει τη λίστα μελών της ομάδας, γιατί στην πράξη ομάδα στοιχείων έχουμε φτιάξει. Το ερώτημα που προκύπτει είναι, ότι αφού η s.toString$() είναι μια κανονική, ξεχωριστή συνάρτηση, πώς γίνεται να διαβάζει την s.count.
Οι συναρτήσεις στη Μ2000 βλέπουν γενικές και τοπικές μεταβλητές συν μια ακόμα, αυτή του αντικειμένου που τις δημιούργησε (αν τις δημιούργησε αντικείμενο). Έτσι η s.toString$() είναι μια κανονική συνάρτηση, που βλέπει όμως τις ιδιωτικές και δημόσιες μεταβλητές/συναρτήσεις/τμήματα του αντικειμένου που "ανήκει". Ομοίως και η s,increment βλέπει την s.count.

Αν στο τέλος απαριθμήσουμε τον αριθμό μεταβλητών θα πούμε με ακρίβεια ότι έχουμε τρεις, την s, την s.count (που είναι κρυφή) και την v$. Αν απαριθμήσουμε τις συναρτήσεις και τα τμήματα (με εξαίρεση αυτό που βρίσκεται ο κώδικας), θα πούμε με βεβαιότητα ότι έχουμε δυο συναρτήσεις, την Some() και την s.toString$() και ένα τμήμα το s.increment. Σύνολο στις λίστες 5 οντότητες.
Αν είχαμε φτιάξει άλλα δύο αντικείμενα με την Some() τότε θα είχαμε επιπλέον 4 μεταβλητές και 4 συναρτήσεις-τμήματα.

Class Some {
   Private: count
   Public:
    Module increment {
      .count++
   }
   Function toString$ {
       =Str$(.count, "00000")
   }
}


s = Some()
s.increment
s.increment
s.increment
v$ =s.toString$()
Print v$


Αφού είδαμε όλα αυτά, πάμε να δούμε πώς θα κάνουμε κάτι εξαιρετικό! Πώς θα κάνουμε επέκταση ενός αντικειμένου, αλλάζοντας την ιδιωτική μεταβλητή και αλλάζοντας ένα τμήμα του.
Θα χρησιμοποιούσε εδώ απ΄ευθείας ορισμό ανοικτού αντικειμένου. Η Group φτιάχνει απ΄ευθείας ανοικτό αντικείμενο. Που σημαίνει ότι όταν φτάσουμε στην επόμενη εντολή Group θα υπάρχει η Some ως αντικείμενο, η Some.count, το τμήμα Some.increment, και η συνάρτηση Some.toString$()

Αμέσως μετά φτιάχνουμε ένα ανοικτό αντικείμενο, το ExtentSome με μια ιδιωτική την ExtentSome.count, και ένα τμήμα το ExtentSome.increment. Εδώ δίνουμε αρχική τιμή στη μεταβλητή και επιπλέον δίνουμε και στο τμήμα μια άλλη λειτουργία, έναν άλλο τύπο στην ExtentSome.count

Δείτε τι γίνεται στην αμέσως επόμενη γραμμή μετά τον ορισμό της ομάδας (group) ΕxtentSome. Φτιάχνω μια S από το κλειστό αντικείμενο που επιστρέφει η Some. Η Some είναι αντικείμενο και να χρησιμοποιηθεί στα δεξιά μιας αντικατάστασης θα δώσει ένα κλειστό αντικείμενο. Στην κυριολεξία μαζεύει ότι έχει η λίστα της και τα αντιγράφει σε ένα αντικείμενο που λέγεται κλειστό (είναι η κατάστασή του τέτοια δηλαδή). Αυτά που έχει θα πάνε στην S όπως ακριβώς έκανε και η κλάση στο προηγούμενο παράδειγμα. Κάθε αντικείμενο group (ομάδα) στη Μ2000 δίνει ένα αντίγραφο όποτε θέλουμε. Θα δημιουργηθούν τα s και s.count, και τα τμήματα/συναρτήσεις s.increment και s.toString$()    (όταν λέμε για ανοικτό αντικείμενο μιλάμε για δημιουργία μεταβλητών στους καταλόγους του τμήματος, άρα για οντότητες που μπορούμε να χειριστούμε ανεξάρτητα)

Δείτε τι γίνεται αμέσως μετά. Ενώ τη πρώτη φορά η s ήταν άδεια (δεν υπήρχε και δημιοθργήθηκε), τη δεύτερη είναι ήδη ομάδα άρα το ExentSome θα αντιγραφεί στην s με συγχώνευση. Που σημαίνει ότι δεν θα δημιουργηθεί νέα s.count (θα παραμείνει ιδιωτική), ούτε νέo τμήμα  s.increment (μπορούμε να το λέμε και μέθοδο, αλλά η λέξη μέθοδος ως εντολή κάνει κάτι άλλο στη Μ2000, οπότε η αναφορά στη μέθοδο γίνεται μόνο λεκτικά όχι με εντολή).

Μπορούμε να παραλείψουμε τη γραμμή s=ExtentSome() και θα δουλέψει το πρόγραμμα, με αυτά που ;έδωσε η Some στην s.
Στην ουσία η s κληρονόμησε την Some, και μετά την ExtentSomt. Τα αρχικά αντικείμενα μπορούν να αλλάξουν, αλλά δεν σχετίζονται πια με την s. Σε άλλες γλώσσες γίνεται μεταξύ τους τα αντικείμενα ενός τύπου να έχουν κοινή μεταβλητή, εδώ δεν γίνεται. Κοινές μεταβλητές δίνουν τα αντικείμενα στο πρώτο επίπεδο συναρτήσεων και τμημάτων (στη Μ2000 μπορούν οι συναρτήσεις και τα τμήματα  να έχουν φωλιασμένες συναρτήσεις και τμήματα). Όπως επίσης κοινές μεταβλητές κάνουμε ως γενικές (μια γενική σε ένα τμήμα θα υπάρχει όσο το τμήμα υπάρχει και θα σκιάζει όποια τυχόν γενική υπήρχε στο πατρικό τμήμα).

Group Some {
   Private: count
   Public:
    Module increment {
      .count++
   }
   Function toString$ {
       =Str$(.count, "00000")
   }
}
Group ExtentSome {
      Private: count=1000
      Public:
          Module increment {
            .count*=10000
       }
}
s = Some
s = ExtentSome
s.increment
s.increment
s.increment
v$ =s.toString$()
Print v$



Όμως η δημιουργικότητά μας δεν σταματάει εδώ. Στο προηγούμενο παράδειγμα στο τέλος μετράμε τι έχει στις λίστες του ο διερμηνευτής  (έχει μια λίστα για πίνακες και μεταβλητές και μια για συναρτήσεις και τμήματα).
Τέσσερα κομμάτια για το Some συν τρία για το ExtentSome, συν Τέσσερα για το s και μια η v$ σύνολο 12. Στο προηγούμενο παράδειγμα είχαμε 5, και εδώ αλλάξαμε μόνο δυο οντότητες, άρα τα επιπλέον 5 ήταν απαραίτητα για την αλλαγή.
Εδώ χρησιμοποιούμε λάμδα συναρτήσεις H λάμδα Some έχει και μια αδελφή συνάρτηση την Some(). Στις λίστες του διερμηνευτή μόνο αυτά τα δύο αναφέρονται. Η λάμδα ExtentSome έχει και αυτή την ExtentSome μεταβλητή και την ExtentSome() συνάρτηση, παράλληλα εσωτερικά έχει και ένα αντίγραφο της Some (γιατί την έχουμε μετά την λέξη Lambda και πριν το "->" και αυτός είναι ο χώρος που δείχνουμε τι θα συλλάβει το αντικείμενο λάμδα. Δεν περνάμε αναφορά στο Some αλλά αντίγραφο του Some, το οποίο για την λάμδα είναι μη τοπικό, δηλαδή θα υπάρχει για αυτό όσες φορές και αν καλέσουμε την συνάρτηση ExtentSome. Αν μάλιστα βγάλουμε αντίγραφο του ExtendSome θα έχει και αυτό την δική του Some.

Για την Some. όταν τρέχει δημιουργεί τοπικά μια κλάση και την δίνει ως επιστροφή, ως κλειστό αντικείμενο (άρα δεν γράφονται στο κατάλογο του διερμηνευτή τα στοιχεία του. αλλά σε κατάλογο στο ίδιο το αντικείμενο χωρίς όνομα, να γιατί λέγεται κλειστό, δεν ξέρει τι όνομα να δώσει, ενώ π.χ. στο s παρακάτω το count είναι s.count, στο κλειστό απλά γνωρίζει ότι αν ανοίξει θα φτιάξει για δοσμένο όνομα Χ το Χ.count)

Πραγματικά αν μετρήσουμε εδώ θα βγάλουμε 2 +2+4+1 =9 οντότητες στις λίστες άρα 4 λιγότερες και τώρα μια λάμδα η extented δίνει επακριβώς ένα αντικείμενο εκτεταμένο, ή παραλλαγμένο, ή υποκλάσης της κλάσης Some (αν θέλουμε συμβατικά να το λέμε, για τη Μ2000  αυτά τα αντικείμενα είναι ομάδες groups)

Some = Lambda -> {
      Class InnerSome {
         Private: count
         public:
          Module increment {
                  .count++
               }
         Function toString$ {
             =str$(.count, "00000")
               }
         }
         =InnerSome()
}


ExtentSome=Lambda Some -> {
      Class Extend {
      Private: count=1000
      Public:
          Module increment {
            .count*=10000
         }
      }
      B=Some() \\ first make a some
      B=Extend() \\ and extend it
      =B
}



s = ExtentSome()
s.increment
s.increment
s.increment
v$ =s.toString$()
Print v$



Μια παραλλαγή του παραπάνω προγράμματος είναι αυτή εδώ όπου αν προσθέσουμε τις οντότητες είναι τώρα μια λιγότερη αφού τη πρώτη λάμδα την κάναμε κλάση. Και αντί στην ExtentSome να κρατάμε με σύλληψη την λάμδα κάνουμε κάτι άλλο, δημιουργούμε τo ανοικτό αντικείμενο Β, το οποίο δημιουργείται μόνο για την ExtentSome, και κλείνει, γίνεται κλειστό και μπαίνει στο αντικείμενο λάμδα ExtendSome (το αντικείμενο κρατάει την συνάρτηση και ότι έχει συλλάβει από το περιβάλλον, και έχουμε δηλώσει να το κάνει,η σύλληψη είναι αντιγραφή).

Class Some {
   Private: count
   Public:
    Module increment {
      .count++
   }
   Function toString$ {
       =Str$(.count, "00000")
   }
}
ExtentSome=Lambda B=Some() -> {
      Class Extend {
      Private: count=1000
      Public:
          Module increment {
            .count*=10000
         }
      }
      \\ merge Extent() to B
      B=Extend()
      =B
}


s = ExtentSome()
s.increment
s.increment
s.increment
v$ =s.toString$()
Print v$



Και για Bonus (αφού διαβάσατε μέχρι εδώ)...
Ένα Group μπορεί να πάρει και λάμδα συναρτήσεις στην λίστα του. Εδώ φτιάχνουμε την Μ. Επειδή δημιουργείται και συνάρτηση Μ() θα έχει επίσης και ένα πεδίο που θα λέει "ανήκω στο Alfa".  Θέλω όμως να συλλάβω την k και να την χρησιμοποιώ, αλλά είναι ιδιωτική. Οπότε φτιάχνω ένα τμήμα που ξεκινάει την Μ. Στην ουσία της δίνει νέο ορισμό (δείτε το "<=" αν το ξεχάσουμε θα φτιάξει ο διερμηνευτής τοπική μεταβλητή Μ, η οποία θα χαθεί με το πέρας εκτέλεσης της StartM. Επειδή η Alfa.k είναι ιδιωτική την αντιγράφουμε σε μια άλλη, εδώ δείτε ότι μετά το = υπάρχει μια τελεία και το k (lambda k=.k). Τώρα χειριζόμαστε το αντίγραφο της Αlfa.k στην k της Μ. Οι συναρτήσεις λάμδα δουλεύουν όπως και οι μεταβλητές στην αντικατάσταση, πετάνε ότι έχουν και παίρνουν νέο ορισμό. Πράγματι καλούμε δεύτερη φορά την Alfa.SrartM και κάνουμε νέα σύλληψη (και αυτή είναι μια σκοπιμότητα των λάμδα).


Group Alfa {
Private:
      k=100
Public:
      klm=5
      M=lambda->0
      Module StartM {
            .M<=lambda k=.k -> {
                  k++
                  =k*.klm
            }
      }
      Function M2 {
            .k--
            =.k
      }    
}
Alfa.StartM
For I=1 to 10
      Print Alfa.M(), Alfa.M2()
Next i
\\ new capture
Alfa.StartM
For I=1 to 10
      Print Alfa.M(), Alfa.M2()
Next i


Επειδή έχουμε στον ορισμό της Μ την .klm σημαίνει ότι αν δώσουμε αντίγραφο  και τρέξει η Μ χωρίς να είναι σε αντικείμενο ή αν είναι χωρίς αυτό να έχει μια klm τότε θα βγει λάθος. Μπορούμε να περάσουμε με αναφορά την συνάρτηση. Οι συναρτήσεις που ανήκουν σε πρώτο επίπεδο σε μια ομάδα (όχι στις φωλιασμένες) περνούν και το περιβάλλον της ομάδας μαζί τους, δηλαδή μπορούν να καλέσουν συναρτήσεις και τμήματα της ομάδας και να αλλάξουν τιμές σε αυτήν.
Εδώ θα δουλέψει γιατί το Alfa.M είναι λάμδα αντικείμενο και έχει και τη συνάρτηση ALFA.Μ()  που περνάει με αναφορά στο TestMe
Βάλτε το κώδικα αυτό ως συμπλήρωμα του προηγούμενου!

Module TestMe {
      Read &MM()
      Print MM()
}


TestMe &Alfa.M()