Grafische Programmierung

Augenkrebs-Warnung!

Das Programmieren erfolgt seit vielen Jahren fast nie auf Papier. Dennoch verwenden die populärsten Programmiersprachen ausschließlich die Textform. Diese funktioniert auch in der Regel. (Bestes Beispiel: Rust) Nur selten gibt es Probleme mit der Textform. Aber da ich ein Perfektionist bin, möchte ich einen radikalen Schritt wagen. Es ist der gleiche, der auch in vielen anderen Gebieten gemacht wurde, beispielsweise in Textverarbeitung, grafischer Modellierung, Audio-Komposition und Dateiverwaltung: Der Schritt von einer Text-basierten Bedienung zu einer grafischen Bedienung. Das hat aber auch den großen Vorteil, für Neueinsteiger viel einfacher erlernbar zu sein! (Vergleich: Scratch)

Optisch soll diese grafische Programmierung auf den verschiedenen Plattformen gleich aussehen. Die Bedienung erfolgt über Maus, Berührung und Tastatur. Das meiste Umdenken fordert der Unterschied, dass das Programm nicht nur aus den Informationen besteht, die man auch auf einen Blick sieht.

Beispielsweise werden Objektidentifikatoren (Pfadangaben) gekürzt angezeigt. Statt dem Verwenden von Variablennamen wird sich per interaktiver Auswahl auf ein Objekt bezogen. Ein freier Platz, beispielsweise ein Funktionsparameter, wird angeklickt und dann kann entweder (falls doch einer vergeben wurde) ein Name eingegeben oder einfach auf den einzufügenden Speicherplatz oder Wert geklickt werden. Variablennamen müssen nicht mehr bestimmten Gesetzen folgen. Sie dienen ausschließlich der Identifizierung für den Programmierer. Intern werden Speicherplätze über IDs identifiziert und referenziert.

Um den Überblick zu verbessern, habe ich einen Farbkodex festgelegt:

Natürlich ist die Farbgebung nicht gottgegeben. Ich habe sie mir nach Intuition ausgedacht. Sollte jemand einen besseren Vorschlag haben, bin ich dafür offen.

Beispielprogramm

So in etwa sieht ein kleines Programm in der Entwicklungsumgebung aus:

Ausgabe

Hello, World!
1
2

[...]
10

Erklärung

Alle Programme befinden sich in Modulen. Ein Modul ist immer eine Klasse, die Merkmale implementieren kann. Das hier gezeigte Modul Test implementiert das Merkmal :app, das dem Modul ermöglicht, als Benutzer-Anwendung aufgerufen zu werden. Das Merkmal ist alleine schon dadurch erfüllt, die Merkmals-Methode :start zu implementieren. Dass es sich bei :start um eine Merkmals-Methode handelt, lässt sich schnell daran erkennen, dass der Name gelb ist. Und es lässt sich daran erkennen, dass dem Namen in der Funktions-Definition ein : vorangestellt ist. Die volle Bezeichnung wäre beispielsweise system:app:start und diese wird dann angezeigt, sobald entweder der Mauszeiger auf den Funktionskopf zeigt oder wenn mit der Tastatur der Fokus auf ihn verschoben wird.

Der :start-Funktion zugehörig sind zwei Anweisungen:

Weitere Darstellungsweisen

Jedem, der bis hierhin gelesen hat, sollte klar sein, dass die oben gezeigte strukturelle Darstellung eine große Ähnlichkeit mit bestehenden Sprachen aufweist. Dies ist bewusst so gewählt, um den Umstieg zu erleichtern. Funktionen lassen sich aber auch statt als Liste an Anweisungen (Zeitverlauf) als Netzwerk anzeigen (Informationsverlauf). Vereinfacht betrachtet werden die Aufrufe jeweils umgedreht. Bei der obigen Notation wird zuerst das Ziel (die Konsolenausgabe) und dann die Quelle ("Hello, World!") angegeben. Variablen werden dann zu Rohren bzw. Knoten, falls ein Wert mehrmals verwendet wird. Aber mit dem Vorteil des einfacheren Informationsflusses kommt auch ein Nachteil: Die zeitliche Abhängigkeit muss nun irgendwie anders geregelt werden. Klassische Prozeduren lassen sich nur mit besonderen Zeitmarkierungen (Ereignisse) gleich effizient realisieren. Der intern erzeugte Graph ist der selbe. Und er ließe sich auf vielfältige Art darstellen. Als erstes möchte ich eine Rasterorientierte Darstellung, orientiert an LogiSim und Factorio. Programme sollten dann hauptsächlich so entwickelt werden (macht auch mehr Spaß), aber man kann es sich am Ende aussuchen. Der wichtigste Schritt ist eben, ein Programm nicht als Zeichenkette, sondern als Graphen zu betrachten, speichern und zu verarbeiten. Es soll keine Redundanz geben und so abstrakt wie möglich geschrieben werden. Nicht jeder kann das so gut. Wie man sein eigenes Programm entwickelt, muss man selbst entscheiden. Nur globale, wiederverwendbare Module müssen sehr redundanzarm und universell sein. Die Vielfalt bei Spielen soll erhalten bleiben, alles andere soll einheitlich sein.

Asynchrone Prozesse

Funktionsparameter werden nicht kopiert, sondern nur geborgt. Um sicherzustellen, dass keine zeitlichen Konflikte auftreten, kann ein Speicherplatz immer nur von einer Funktion zur Zeit verwaltet werden. Gelesen werden darf parallel. Auch dieses Konzept ist mit dem von Rust nahezu identisch. Es werden alle Prozess-Schritte dadurch geordnet, von welchen Daten sie abhängen. Sie werden aber nicht nur dann ausgeführt, wenn alle Parameter vorliegen, sondern immer schon so weit, wie mit den bereits vorliegenden Parametern bereits möglich ist. Auch klassische prozeduale Strukturelemente wie die Reihenfolge von Aufrufen, Schleifen und konditionaler Ausführung werden durch diese Abhängigkeits-Verwaltung realisiert. Zwei Funktionen können auch direkt nebeneinander gekoppelt werden, indem jeweils ein Ausgang mit jeweils einem Eingang verbunden wird. In klassischen Sprachen wäre dies vollkommen unmöglich. Dies geht aber nur, wenn klar ist, dass die Werte immer in der gewählten Reihenfolge vorliegen können, was Einfrieren verhindern soll.

Vorgegebene Objekte

Objekte

Alle Dinge sind Objekte. Alles, was man im Editor erstellen und verändern kann, sind Objekte. Objekte können einen Wert repräsentieren. Werte können aber auch hergeleitet werden, ohne direkt von einem Objekt zu stammen. Objekte bestehen sowohl zur Entwurfszeit als auch zur Laufzeit, es gibt keine feste Grenze dazwischen. Objekte sind also im Grunde wie Begriffe/Wörter für Menschen; sie sind Knoten, an die sich Eigenschaften hängen lassen. Alle manuell erstellten Objekte können benannt werden, um sie direkt mit dem jeweiligen Begriff zu verknüpfen.

Eigenschaft

Eine Eigenschaft hängt an Objekten, wird aber nur für deren Klasse oder eines ihrer Merkmale bestimmt. Eine Eigenschaft wird für ein Objekt mit einem Wert verbunden. Zum Beispiel die Eigenschaft "Alter", die einen Menschen mit der Zeitspanne "20 Jahre" verknüpft.

Klassen

Klassen halt. Eine Klasse erweitert eine Klasse um bestimmte Besonderheiten, die auf ihre Instanzen übertragen werden. Instanzen sind Objekte, die deren Merkmale von ihrer Klasse übernehmen. Das Konzept hier noch weiter zu erklären, würde nicht viel bringen. Wenn man einen Menschen als Instanz betrachtet, dann ist "der Mensch" eine seiner Klassen. Er kann nämlich auch eine Instanz der Klasse "der schlaue Mensch" sein, was ihn automatisch zu einen Menschen macht.

Merkmal

Ein Merkmal ist eine Ansammlung an Eigenschaften, die man jeder Klasse hinzufügen kann. Dieses Konstrukt ist deswegen notwendig, da nicht alle Eigenschaften nur "von oben" kommen. Besonders wenn es darum geht, was man mit etwas machen kann und wie das zu tun ist, bringt die reine Klassenhierarchie wenig. Man kann zum Beispiel mit einem Menschen reden, jedoch aber auch mit einem Smartphone. Diese beiden entstammen zum Beispiel erst der Klasse der Gegenstände, jedoch kann man nicht mit jedem Gegenstand reden. Hier wäre dann das Merkmal "Beredbar" sinnvoll.

Typ

Ein Typ kann entweder eine Klasse oder ein Merkmal sein. Typen werden zum Beispiel für Eigenschaften verwendet, um anzugeben, was man ihnen zuweisen darf. Intern werden Typen durch das Merkmal "Klassisch" dargestellt. Klassisch ist alles, was sich Klassen-ähnlich verhält, also Merkmale für Instanzen repräsentiert. Dies sind soweit nur Klassen und Merkmale.

Wertträger

Dies sind jene Klassen, die in der echten Welt ein Wort wie "Ja", "Nein" oder ein Adjektiv darstellen. Oder Mengenangaben, Aufzählungen und dergleichen mehr. So gibt es die Klassen der Zahlen, Wahrheitswerte (Ja oder Nein), Mengen, Listen, Zuordnungstabellen und Auswahlwerte.

Mengen

Mengen sind Objekte, in die sich programmatisch andere Objekte reinpacken lassen. Die Objekte die hineinkommen, entsprechen immer einer nähesten gemeinsamen Klasse, notfalls der Klasse "Objekt", was bedeutet, dass man ALLES dort hinein tun könnte. Wäre aber etwas blödsinnig.

Listen

Listen sind eine Untermenge der Mengen, also ist die Liste eine Kindklasse der Menge, also ist jede Liste auch eine Menge. Bei Listen kommt gegenüber der Menge hinzu, dass sie eine Ordnung haben, sich deren Inhalt also in einer bestimmten Reihenfolge auflisten lässt. Text ist auch eine Liste, nämlich aus Zeichen.

Zuordnungstabellen

Ein umständliches Wort. Im Englischen einfach nur "Map" genannt. Die machen es sich leicht, die Amerikaner. Sie sind Mengen, deren Einträge sich jedoch über sogenannte Schlüssel aufrufen lassen. Bei einem Dateiordner sind die Schlüssel die Dateinamen und die Dateiinhalte die Werte.

Übrigens...

Bis hierhin lesen nur Nörds. Also zur Belohnung viel Spaß mit dem aktuellen Stand: Prog