Donnerstag, 19. Januar 2017

Programmkonstrukt - Grundlagen für eine Programmiersprache

Sehr geehrte Damen und Herren,

folgender Text ist unausgereift und hier nur als Skizze zu sehen.

eigentlich ist es ja so: Mit jeder neuen Technologie stellt sich die Frage, ob man diese brauch. Auch ist es immer wieder fraglich, ob es vorteilhafter wird, ob es einfacher wird, oder nur komplexer. Es stellt sich die Frage, ob die Technologie eine gute Idee war.
Prinzipiell ist jede Technologie gut. Jedoch gibt es auch die Möglichkeit eine unnötige Technologie zu besitzen oder Zeit in diese zu investieren. Man stelle sich vor, man hätte eine Waschmaschine, in die man nur ein Teil jeweils rein machen kann, die trotzdem eine Stunde pro Teil brauch. Oder ein stylischen Tretroller (Scooter), der ganze 10 kg wiegt.

Und im Prinzip verhält es sich mit der Programmierung gleich. Es gibt komplizierte Programmierkonstrukte, die mehr Chaos als Ordnung in die Programmierung bringen. Es sieht so aus, als ob nicht jedes Programmierkonstrukt geeignet ist. Es sieht so aus, als ob es ein optimale Technik geben könnte. Ich weiß, dass manche ja behaupten, jede Programmiersprache habe seine Berechtigung. Klar, jeder Mensch und jede Menschensprache auch. Nur gibt es auch Mörder und Kaudawelsch.

Wollen wir wirklich mörderisches Chaos? Wollen wir wirklich Programmiersprachen als Kaudawelsch? Sicherlich sehr künstlerisch, und sicherlich auch sehr cool. Nur leider nicht zielstrebig um folgende Punkte zu lösen:

* Schnelles Auffinden von Code-Stellen.
* Leichtes Einarbeiten in neuen Code.
* Schnelles Implementieren von neuen Features.
* Zukünftige und andauernde Verwendung dieses Programmierkonstrukts.


Lösung

Deshalb gibt es hier die Lösung.
Ziel ist es eine Programmierkonstrukt zu bekommen, welches die Punkte, wie oben beschrieben erfüllt:
Ziele:
* Schnelles Auffinden von Code-Stellen.
* Leichtes Einarbeiten in neuen Code.
* Schnelles Implementieren von neuen Features.
* Zukünftiges und andauerndes Verwenden des Programmierkonstrukts.
* Möglichst einfaches und fehlerfreies implementieren.

Das Programmierkonstrukt brauch eine hierarchische Ordnung. Es sollte folgende schon vorhandene und bewährte Hierarchien übernehmen.
Eigenschaften:
* Prozesse und Threads
* Memory Pages und Shared Memory
* Daten in JSON and Binary-Part in JSON

Im Prinzip ist ein Rechner eine Turing-Maschine. D.h. er besteht nur aus Einem Band, einem Schreib/Lese-Kopf und einem Alphabet mit Regeln.
Das Band sind Memory Pages und Shared Memory. Der Schreib-/Lese-Kopf sind die Prozesse und Threads. Das Alphabet sind die Daten und Programme. Und die Regeln sind die Rechenoperationen der CPU.

Im Prinzip lässt sich alles auf ein Netzwerk von Daten und Programminstruktionen reduzieren. In diesem Netzwerk verläuft man nun einen Weg. Um genauer zu sein, ist der Weg immer einen Round-Trip, sonst muss man die Maschine immer wieder neu-starten. Was dann auch einem Round-Trip entspricht.
Es bietet sich nun an einen Round-Trip immer im kürzesten Weg zu machen, denn wir wollen ja eine schnelle Maschine. Und nicht die Waschmaschine mit einem Teil.
D.h. der Programmierer ist immer dabei bei gegebenen Daten und Instruktionen den kürzesten Round-Trip zu formulieren, so dass am Ende das gewünschte Endprodukt entstanden ist, oder der gewünschte Prozess durchgeführt wurde.

Das Programmkonstrukt soll den Programmiere nun dabei unterstützen diese Round-Trips zu erschaffen und dabei die oben genannten "Ziele" zu berücksichtigen.
Man sieht sofort, es ist nicht möglich durch chaotische Programmkonstrukte einen kürzesten Round-Trip zu finden.
Ein Beispiel für einen gelungenen Round-Trip wäre:
Round-Trip A
1. warte auf User-Input
2. Lese User-Input
3. If (data x == 1) starte Round-Trip B
4. Drucke Information auf Bildschirm
5. Beginne wieder mit 1.

Round-Trip B
1. Drucke "Wie ist Dein name".
2. Warte auf User-Input
3. Drucke "Hallo " + Data y + " !"
4. Beginne mit Round-Trip A.1.

Wie man an dem Beispiel sieht sind mehrere Round-Trips auch miteinander Verknüpft. Jedoch wissen Informatiker, dass dabei nicht ein wildes umherspringen zwischen den Round-Trips passieren sollte, wie es z.B. mit Goto-Anweisungen früher der Fall war. Sonder, dass jeder Round-Trip auch einen klaren Pfad haben sollte, der einem "kurzen" Weg entsprechen sollte. "kurz" hier in dem Sinne von nützlich. Es ist z.B. nicht nützlich in B.2.5. einen Schritt "Warte auf User-Input" zu setzen und die Eingabe dann zu verwerfen. Denn, das wäre dann die Waschmaschine mit nur einem Teil.
Unter der Betrachtung von Daten und Instruktionen als Punkte-Menge, wäre das eine Abzweigung ohne zurück zu kommen. Demnach kein Round-Trip, demnach hier nicht gewünscht.

Aus der Lösung eines Travelling-Salesman-Problem (TSP) wissen wir, dass ein kürzester Round-Trip durch ein Devide-And-Conquer-Verfahren gelöst werden kann. D.h. der Raum wird in Teilräume unterteilt und jeder Teilraum löst wieder ein TSP.
Die Punkte im TSP ist das Turing-Maschine Alphabet mit Daten und Instruktionen. Jedes Datum ein Punkt und jede Instruktion ein Punkt. Auf diesen Knoten bilden wir nun die Struktur.
Aus dem TSP-Solver wissen wir, dass es eine hierarchische Baumstruktur seien sollte.

Vorhandene Struktur

Daten:
Unterste Ebene: Binärdaten
zweite Ebene: Primitive Data-Types (float, int, double, long, char)
dritte Ebene: JSON oder binary JSON. Am besten beides.
JSON ist dann eine hierarchische Ordnung der Daten und damit das Ziel erreicht.

Instruktionen:
Unterste Ebene: Maschinencode
zweite Ebene: Assembler-Befehle
dritte Ebene: Konstrukte höherer Programmiersprachen
vierte Ebene: OOP
fünfte Ebene: Spring-Framework?
Bei OOP sind wir in der hierarchischen Ordnung für Daten angekommen.

Jetzt benötigen wir noch eine hierarchische Ordnung/Aufteilung dieser Daten/Instruktionen.
Das ist zum einen die OOP, die zugehörige Daten mit Instruktionen verbindet und zum anderen, unser gesuchtes Programmkonstrukt, welches Diese OOP's dann wieder in Bereiche einteilt.

Neues Konstrukt


Eigenschaften des neuen Konstrukts:
* Jeder Bereich sollte maximal und minimal einen Round-Trip enthalten.
* Die Round-Trips, welche ineinander gehen, liegen in der hierarchie nebeneinander.
* Der oberste Knoten in dem Konstrukt sollte Versionierung und Varianten enthalten. Versionierung um eine Zeitskala abzubilden und Varianten um die Frage "für wen" abzubilden. Also kundenspezifische Varianten.
* Die oberen Nodes im Baum sollten auf beliebige Tiefe laufen können um mehrdimmensionale Eigenschaften abzubilden. Z.b. erste Ebene ist der Kunde, zweite Ebene ist der Ort/die Maschine, und dritte Eben ist die Version.
* Aspect-Orientierte-Programmierung sollte durch die Hierarchie ermöglicht werden.
D.h. von dem ursprünglichen Zweig kann jeweils immer eine andere Variante abgezweigt werden, die nur eine Erweiterung des Code's vom Original enthält.
* Prozess-hierarchien wie sie bei Multiprozessor-Technologien erscheinen, sind durch die Hierarchie im Programmierkonstrukt ebenfalls abgedeckt. D.h. das Programmkonstrukt brauch noch festgelegte Struktur, bevor es in die oberste, freie Struktur der Varianten gehen kann.

Festgelegte Struktur, bevor der freien Struktur.

Daten und Instruktionen sind gut in OOP gekapselt.
Threads sind gut in Prozesse gekapselt.
Memory ist gut in Pages und shared Memory eingeteilt.
Festplatten sind gut in Sektionen aufgeteilt.
Damit sind die Turing-Maschinen Features unterteilt.

Bleibt nur noch die Hierarchie für alle Komponenten zusammen.
Gewünscht ist:
* Mehrere Prozesse sollen miteinander Kommunizieren.
* Mehrere Daten und Instruktionen sollen miteinander Verbunden werden.
* Mehrere Zugriffsmöglichkeiten auf Memory und Festplattte sollen Verbunden werden.

Aktuelle Lösung:
* Prozesse kommunizieren über Inter-Process-Communication.
* Daten und Instruktionen sind in Bibliotheken miteinander verbunden.
* Zugriffsmöglichkeiten sind auf Memory und Festplatte über Datenbanken verbunden.

Gesucht ist also ein Konstrukte, welches:
* Inter-Process-Communication macht. Auf hierarchische Art-und-Weise.
* Bibliotheken und Versionierungssystem enthält.
* Und eine verteilte Datenbankanbindung nach dem CouchDB Prinzip enthält.

CouchDB:
Primitive Instructions einer Datenbank sind: query, edit, add und delete.
Eigenschaften sind: append-only updates, indizes as b-trees.

Pro CouchDB:
* Document-Based
* "Views are the method of aggregating and reporting on the documents in a database"[¹]
* "as many different view representations of the same data as you like"¹

Contra CouchDB:
* "When CouchDB documents are updated, all data and associated indexes are flushed to disk"[1]

MongoDB:
Neben CouchDB gibt es noch eine nach meiner Meinung geeignetere Datenbank.
Diese ist MongoDB. Sie ist insbesondere besser geeignet, da sie Consistency garantiert und nicht wie CouchDB zwischen verschiedenen Clients evtl. veraltete Daten besitzt. Die schnelle Erreichbarkeit ist eine andere Frage und kann einfacherweise später auf Consistency drauf gesetzt werden.

Pro MongoDB:
* Document-based,
* Consistency.
* typical find() method for queries.

Contra MongoDB:
* nicht so gute Availability.

Beide Datenbanken haben einen entscheidenden Nachteil. Ihre Basis sind nicht Elemente, sondern Documente. Es können so nur ganze Documente einen Index enthalten. Für das Union-Find Problem auf Datenbanken wäre ein Mengen-Orientierte Datenbank noch praktischer.

==> Entscheidung für MongoDB

Bleibt noch das Paketsystem.
Und bleibt noch die Inter-Process-Communication.

==> Das Paketsystem is mnu für JavaScript updater.

==> Die Inter-Process-Communication ist dann mit Events auf der Basis von Node.js

Contra Node.js:
* keine Typisierung im JavaScript - die Differenzierung der Daten geht verloren.
 (z.B. wie kann man einen 64Int in JavaScript darstellen? - nur mit einer Hilfsbibliothek, was zu Performance-Einbusen führt)
* Es ist ein Interpreter kein Compiler. Manche Fehler werden erst nicht zur Compilezeit aufgedeckt. Alles muss intensiver getestet werden, gerade, da Typ-Converting-Fehler auftreten können. (z.B. ein String in einen Integer).
* Eine Variable kann die eine Sekunde Integer und die andere Sekunde ein String sein. Daher keine klare Trennung von Daten.
* Keine besonderen Features guter Programmiersprachen, wie
 - exception-handling
 - Generics/Templates
 - kein AOP (aspect-oriented-programming)

Pro Node.js:
* Sehr Funktionale-Programmiersprache, da viele sogenannte "Lambdas" verwendet werden. Trotzdem Prozedural.
* Performanz ist zum Glück bei vielen Anwendungen nicht notwendig.

==> Aufgrund der vielen Contras, entscheide ich mich doch für C++ mit Poco als Inter-Process-Communication (IPC) über TCP/IP und HTML/JSON

Freie Struktur

 Die hier als "freie Struktur" bezeichnete Programmstruktur beinhaltet die Aufgabe alle besonderen und zusätzlichen Strukturen zu einem gemeinsamen Konzept zu vereinen. D.h. die Programmstruktur bildet einen Baum mit beliebiger Anzahl von Kindern über den oben vordefinierten Strukturen.

Angewandt heißt dies: Es benötigt ein Framework, welches MongoDB mit zusätzlicher verbesserter Availability, C++ in Kombination mit IPC über Poco und eine Versionierung mit AOP kombiniert.
Dabei muss Poco noch um Event-Driven Engineering erweitert werden. D.h. anstelle von Request-Response Schemas sollten Events in Poco eingeführt werden. Dies entspricht letztlich eher der Realität.

Auf dieser Struktur von C++ Poco und MongoDB kommt nun eine weitere Schicht Versionierung. Bei der Versionierung geht es darum für verschiedene Kunden verschiedene Versionen zu pflegen und dennoch eine gemeinsame Linie zu führen pro Produkt.

(Außerdem muss für die TCP Connection noch ein stabiles Messaging implementiert werden, in dem die Pakete mit und ohne Reliability verschickt werden können.)



Referenzen


[1] https://wiki.apache.org/couchdb/Technical%20Overview






Keine Kommentare:

Kommentar veröffentlichen