Textkritik: Forth

TOP5: Diverses, Verschiedenes, ...

  • Homepage
  • Über mich
  • Fotos
  • Musik
  • Kontakt

May 2: Textkritik: Forth

Textkritik wird eine Serie, in der ich Pro und Contra diverser Programmiersprachen (aus meiner Sicht) beleuchte. Und damit das auch nur ein wenig glaubwürdig bleibt, fange ich am besten mit meinem "dokumentierten Favoriten" an, Forth. Da es nicht bloß eine C-Variation ist, ist eine Beschreibung der Eigenschaften wohl gerechtfertigt.

Forth ist eine Sprache, die sich in den frühen 70ern entwickelt hat, und zur Klasse der "Concatenative Languages" gehört (die erst später definiert wurde). Das bedeutet, man kann den Programmfluss an (fast) jeder Stelle aufteilen in neue Funktionen ("Worte" in Forthterminologie), die man dann zusammensetzt, ohne dass sich das Verhalten ändert - eine sehr schöne Eigenschaft für Refactoring. (Zum Vergleich: "int a; a=5*rand();" einfach aufzuteilen würde scheitern: "int a;" "a=5*rand();" verliert die Deklaration im zweiten Teil, "int a; a=5" "*rand()" ist kompletter Murks)

Die Sprache ist entstanden zu einer Zeit, als es noch keine riesige Betriebssystem- und Bibliotheksinfrastruktur gab. Entsprechend bietet Forth seine eigene Systemabstraktion, auch wenn diese sehr dünn ausfällt. Darauf aufbauend besitzt ein typisches Forth ein Interpreter/Compiler-Gespann, dass sich die Bälle zuspielt: Es startet üblicherweise im Interpreter (sofern man nicht direkt in ein anderes Programm springen will), der bei Bedarf den Compiler aktiviert. Entwicklung ist dadurch "live", die meisten Befehlssequenzen lassen sich erst am Interpreter testen und bei Erfolg zu Worten (=Funktionen) compilen, die in der weiteren Entwicklung verwendet werden können. Auf diese Weise kann gerade Hardwarezugriff sehr schön experimentell getestet werden.

Die Übergabe von Argumenten findet über einen Stack statt - ein Wort hat also keine Definition "soviele Argumente nehme ich (vom Typ X), und soviel gebe ich zurück", es wird stattdessen eine globale Datenstruktur eingesetzt.

Womit ich bei den Problemen wäre:

  • (zur Syntax: ": foo ( n -- n | n n) bar baz ;" definiert ein Wort "foo", dass der Ausführung von "bar" und "baz" entspricht. "( n -- n n)" ist ein optionaler Kommentar, der üblicherweise angibt, dass foo ein Element auf dem Stack verbraucht und eins oder zwei zurückgibt.)
  • Die Anzahl der Rückgabe-Elemente ist also nicht eindeutig festgelegt, ebensowenig wie die Eingabemenge, was sehr sinnvoll genutzt werden kann (und wird!), wodurch die Einarbeitung in fremden Code aber recht spannend sein kann.
  • : send-packet ( len data -- result true | false )
    0 0 2 PKT_DATA send-phy-packet ;

    Was bedeutet das? So wirklich überschaubar ist der Datenfluss nicht (in diesem Fall sieht es wohl so aus, dass send-packet im Prinzip ein Wrapper um send-phy-packet ist, der einige konstante Parameter hinzufügt, und dessen Ergebnis einfach durchreicht). Was zum zweiten Problem führt:
  • Datenstrukturen sind bei Forth nicht sonderlich üblich. Daten werden auf dem Stack übergeben. Und wenn 6 Elemente benötigt werden, um eine Situation zu beschreiben, liegen halt 6 (anonyme) Elemente auf dem Stack. Und das ist manchmal eine ziemlich aufwendige Datenschieberei, um an die einzelnen Elemente zu kommen (idealerweise arbeitet man die von hinten nach vorn ab, bzw. ordnet die Daten so an, dass das möglich ist).
    In anderen Sprachen wäre hier vermutlich schon eine Datenstruktur zum Einsatz gekommen, die dem ganzen die richtigen Namen gibt.

Natürlich lässt sich das auch in Forth namentlich erledigen, etwa indem Worte konstruiert werden, die einem einen "sinnvollen" Zugriff auf Stackelemente geben (": pkt.len 5 pick ;" - also "wähle das 5-letzte Element vom Stack und kopiere es oben auf den Stack"), was allerdings zu Lasten der Effizienz gehen kann - also eine Abwägung zwischen Effizienz und Lesbarkeit.

Eine andere Möglichkeit wäre, derartige Datenstrukturen außerhalb des Stacks vorzuhalten. Das wiederum bedeutet, jedes Element explizit anzufordern, auf dem Stack zu bearbeiten und in die Datenstruktur zurückzuschreiben, was weder die Effizienz, noch die Lesbarkeit fördert.

Möglichkeit Nummer 3 ist schließlich, solche Datenkolosse gar nicht erst aufkommen zu lassen - was nicht immer möglich ist, zum Beispiel wenn man vorhandene Hardware ansteuern soll (das obige Beispiel ist eine Adaption aus einem USB-Treiber).

Posted by Patrick Georgi in Programmiersprachen Comments: (0) Trackbacks: (0)

Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)

No comments


Add Comment

E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
Standard emoticons like :-) and ;-) are converted to images.
 
 

Statische Seiten

 

Layout by Andreas Viklund | Serendipity template by Carl