Benutzer-Werkzeuge

Webseiten-Werkzeuge


start:themen:boo

Boo! - schon wieder eine Programmiersprache

:start:themen:start:themen:boo.png

Für die Common Language Infrastruktur (CLI) wurden bereits schon eine ganze Reihe Compiler entwickelt. Für diese Plattform, die entgegen Vorurteilen eben nicht nur die Basis vom nur für Microsoft Windows verfügbaren .NET-Framework, sondern auch OpenSource Alternativen wie Mono und DotGnu ist, gibt es nicht nur die „offiziell“ von Microsoft angebotenen C++, C#, J# und VisualBasic- Compiler, sondern auch „Exoten“ aller Art wie Smalltalk, Ruby, Lisp, Lua, Eiffel etc. etc. ( Hier gibt es eine Übersicht verfügbarer Programmiersprachen).

Was bringt also einen fähigen und nicht von Unterbeschäftigung oder Langeweile getriebenen Programmierer wie Rodrigo Barreto de Oliveira dazu, die inzwischen kaum noch zu überblickende Programmiersprachenvielfalt noch um eine weitere Sprache mit einem noch dazu so wenig vertrauenerweckenden und sinnfreien Namen zu bereichern?

Nun, seine persönlichen Motive stellt er in diesem Manifest dar. Ich will hier seine in diesem Papier geäußerte Kritik an den verfügbaren .NET Sprachen aufgreifen und auch in etwas geraffter Form zeigen, welche Schwächen auch ich bei den prominenteren Sprachen für die CLI sehe und wie man sie dank BOO vielleicht los wird.

Wem meine anfänglichen Betrachtungen hier zu abstrakt erscheinen und daher auch Boo für was abstrakt-theoretisches frisch aus dem Elfenbeinturm hält, sei getröstet: BOO lehnt sich syntaktisch stark an Python an- wer also Python schon kennt, wird sich in BOO gleich zuhause fühlen. BOO bringt aber zusätzlich einige eher objektorientierte Eigenschaften aus Ruby und ergänzt das mit funktionalen Erweiterungen aus Haskell und nutzt dabei CLI-Gegebenheiten wie Attribute geschickt, um eine Erweiterbarkeit der Sprachsyntax und des gesamten Übersetzungsvorgangs zu erreichen. Die Erweiterbarkeit der Sprache selbst etwa um neue Kontrollstrukturen und die interaktive Nutzung auch per Kommandozeileninterpreter unterscheidet BOO von den objektprozeduralen Sprachen, die man von Microsoft's .NET- Framework her kennt und deren Unterschiede eigentlich nur kosmetisch sind: Ob VB.NET, C# oder Managed C++ - alle diese Sprachen sind gleich unflexibel und erlauben dem Entwickler nicht die Realisierung wirklich neuartiger Konzepte unter Anpassung der Sprachen selbst, wie es bei Sprachen wie Smalltalk, Haskell, Scheme oder sogar Lua selbstverständlich ist.

Mächtigkeit muss nicht Komplexität bedeuten

Eine einheitliche Definition, was die Mächtigkeit einer Programmiersprache ausmacht, läßt sich natürlich kaum geben, denn die Anforderungen sind zu unterschiedlich. Vielleicht wäre der Begriff „Fitness“ sogar passender- die Eignung einer Programmiersprache, mit ihr eine gestellte Aufgabe optimal lösen zu können. So mag es bis heute Gründe geben, Low-Level „Sprachen“ wie Assembler oder Fortran einzusetzen, weil momentan hippe Sprachen wie Java oder gar Javascript für die Aufgabe schlicht nicht brauchbar sind.

Einen Gerätetreiber etwa kann man wohl bisher bei keinem Betriebssystem in Java oder Javascript entwickeln- hier kommt man heutzutage aber wenigstens mit C oder sogar C++ als für solch eine Umgebung „mächtigste“ Programmiersprache zurecht.

Andererseits sind all diese historisch von Algol abstammenden prozeduralen bzw. heute meist objektprozeduralen Sprachen zu unflexibel und in ihrem Sprachumfang nur schlecht erweiterbar- man kann sie also an eine wirklich neue Aufgabe nicht anpassen. Versucht man dennoch eine „gewaltsame“ Anpassung, kommt es zu einer kombinatorischen Explosion der Komplexität und mal als Verbesserung gedachte Sprachmerkmale werden zu schwer überwindlichen Hindernissen. Hier kann man von einem „Paradigmenbruch“ sprechen, spätestens wenn Qualitätsmerkmale der Sprache ausgehebelt werden müssen, um die gegebene Aufgabe lösen zu können.

Ist eine Variable ein Behälter oder ein Etikett?

Allen prozeduralen Sprachen wie C, Pascal, Modula oder Ada und auch ihren objektprozeduralen Nachfolgern wie C++, C#, VB.NET und Java ist z.B. die statische Typisierung gemein, d.h. es gibt eine Deklarationspflicht für Variablen.

Das hat sich in den meisten Fällen auch bewährt, denn es bringt eine Menge Vorteile:

  • der Quellcode dokumentiert sich besser, der Leser kann zweifelsfrei feststellen, womit er es bei einer Variablen zu tun hat
  • eine gute IDE erleichtert auch die Verwendung typisierter Objekte, kann eine korrekte Auswahl von Elementen und Methoden anbieten, über die ein Objekt verfügt und so auch zu wesentlich effizienterem und sicherem Arbeiten verhelfen.
  • durch die statische Typisierung erkennt der Compiler bereits die falsche Verwendung eines Objekts und Fehler treten nicht erst zur Laufzeit auf.
  • der Compiler kann erheblich performanteren Code erzeugen, weil sonst notwendige Typprüfungen oder gar redundante Typumwandlungen zur Laufzeit entfallen können.

Besonders der letzte Punkt ist der eigentliche Grund für statische Typisierung, weil er die Architektur heutiger Hardware wiederspiegelt. Objekte belegen Speicherplatz und je genauer der Compiler Größe und Lebensdauer eines Objekts kennt, desto effizienter kann er mit dieser begrenzter Ressource umgehen und den Zugriff darauf optimieren.

Gerade hardwarenahe Sprachen wie C/C++ ermöglichen deshalb den direkten Zugriff auf den Speicher und erlauben dem Programmierer bis aufs Bit genau die Festlegung von Allokation und Speicherort in Form einer Speicheraddresse. Die anderen genannten Vorteile sind erwünschte Begleiterscheinungen, aber keine Notwendigkeiten.

Jedenfalls ist mit dieser der Hardwarerealität nahen Sichtweise die Vorstellung von Variablen als Behälter (Container) verbunden. Der Behälter und seine Bezeichnung werden als atomar angesehen, ja gleichgesetzt. Diese Vorstellung verliert aber bereits durch die objektorientierten Erweiterungen der prozeduralen Sprachen an Bedeutung, weil sie wirklicher Objektorientierung im Weg steht. Spätestens, wenn es verschiedene Sichtweisen auf bzw. Spezialisierungen oder Schnittstellen zu einem Objekt gibt, gibt es auch mehrere Rollen und „Etiketten“, die verschiedene Merkmale des selben Objekts adressieren. Technische Gegebenheiten wie Speicheradressen treten dabei in den Hintergrund oder stehen bei der Objektmodellierung nur im Weg.

Sprachen, welche den Entwickler von der Speicherverwaltung entlasten, werden auch mit der wachsenden Hardwareleistung für immer größere Aufgabenbereiche interessant und heute wird bereits mehr mit Java, C# oder Interpretersprachen wie PHP und Perl gearbeitet als mit den traditionellen Native-Code-Compilern.

Wenn man nun in Variablen eher Referenzen als Objekte selbst erkennt, stellt sich die Frage nach dem Sinn statischer Typisierung neu. Sieht man sich ein modernes Java- oder C#- Programm an, wird die statische Typisierung hier sehr oft durch forcierte Typumwandlungen umgangen. Für Kollektionen bzw. Datenbank-Datensätze findet man entweder auf bestimmte Elementklassen festgelegte und somit sehr unflexible Spezialisierungen oder aber total generische Klassen, die nur mit Typumwandlungen beim Zugriff auf Elemente zu verwenden sind. Der Vorteil statischer Typisierung fällt hier also weg, weil durch das Typecasting Fehler auch erst zur Laufzeit aufgedeckt werden und darüber hinaus wird auch mehr und weniger gut lesbarer Quellcode benötigt, der mit vielen solchen Typecasts verschandelt werden muß.

Kenner der CLI und von C++ werden jetzt sicher auf Erweiterungen wie Generics oder Templates hinweisen, aber solche „Krücken“ verschleiern nur das eigentliche Problem- eben die statische Typisierung- und verkomplizieren die Syntax der ohnehin schon viel zu schwer zu beherrschenden Sprachkonstrukte noch erheblich weiter.

Statische Typisierung stellt letztlich eine Art von Spezialisierung dar, welche nur bei der prozeduralen Programmierung wirklich Sinn macht. Sobald ein anderes Paradigma, wie echte Objektorientierung oder funktionale oder prädikative Programmierung zum Einsatz kommen, ist eine solche Spezialisierung eher hinderlich.

Der Versuch, solche Dinge nachträglich einer prozeduralen Sprache „aufzusatteln“ endet meist in Code-Monströsitäten, mit denen kein Entwickler mehr gerne arbeitet.

Minimalismus kontra Featuritis

Wer bisher nur mit prozeduralen Sprachen gearbeitet hat, erkennt deren Unflexibilität erst, wenn er es mit funktionalen Sprachen wie Scheme oder Haskell, echter Objektorientierung bei Smalltalk oder Ruby oder Prädikatenlogik wie bei Prolog zu tun bekommt. Interessanterweise ist die Programmiersprache mit der größten Flexibilität und den vielfältigsten Möglichkeiten zur Modellierung von Architekturen und Konzepten der Informationsverarbeitung eine der ältesten und einfachsten überhaupt.

Viele der in anderen Sprachen verwirklichten Konstrukte wurden zuerst in der 1959 von John Mc Carthy entwickelten Sprache LISP erprobt oder lassen sich in LISP leicht nachbilden. Dabei kennt diese Sprache eigentlich keine festgelegten syntaktischen Elemente außer Symbolen, Literalen, Klammern und Whitespace. Damit lassen sich beliebige Daten- und Kontroll- Strukturen, Funktionen, Klassen, Namensräume und was auch immer aufbauen und erproben. Die Syntax der rein objektorientierten Sprache Smalltalk bzw. der Smalltalk nahestehenden modernen Sprache Ruby ist ähnlich verblüffend simpel aufgebaut, aufwändige Compiler und eine komplizierte Syntax mit vielen reservierten Worten und nur aufwändig zu beschreibenden Grammatik scheinen eine Spezialität der prozeduralen Programmiersprachen zu sein.

Ein Kompromiss ist hier sicher Python, das zwar noch ein deutliches objektprozedurales Erbe hat, aber auch wesentlich bessere Abstraktionsmöglichkeiten bietet als Java oder C#.

Neben dem bereits kritisiertem Zwang zur statischen Typisierung ist die als Verstoss gegen wirkliche Objektorientierung aufzufassende Trennung von Code und Daten ein Grundproblem objektprozeduraler Sprachen wie Java oder C#.

Methoden können nicht Variablen zugewiesen, als Parameter übergeben oder als Ergebnis zurückgegeben. Auch hier wurde bei der C# mit Delegate eine objektprozedurale Krücke geschaffen, die aber wie alle Krücken auch eher behindernd als komfortabel ist. Closures, also mit einem speicherbarem Kontext verbundene Codeblöcke kennt keine der bekannten CLI- Sprachen, sind aber in „Interpretersprachen“ wie den genannten Python, LUA, Ruby und sogar Perl eine Selbstverständlichkeit.

Closures wären ein weitaus vielseitigeres, mächtigeres Konstrukt als Delegaten und letztlich auch nicht schwerer zu verstehen oder einzusetzen als diese.

Das Fehlen solcher Konstrukte der funktionalen und rein objektorientierten Programmierung in den bekannten .NET Sprachen ist um so unverständlicher, weil sie sich

  • mit der CLI durchaus realisieren lassen
  • zumindest C# eine wirklich neue Programmiersprache sein soll
  • viele Sprachen (siehe oben) sie bereits kennen
  • durch sie die Sprache selbst besser an neue Aufgaben angepasst werden könnte

BOO Quintessenz

Damit kommen wir endlich zu BOO. BOO baut auf der simplen Syntax von Python auf, kennt keinen Deklarationszwang, ist aber trotzdem typensicher und verwendet die durch die CLI erforderliche statische Typisierung, wo sie erforderlich und sinnvoll ist, erleichtert aber auch eine vollständig automatisierte, dynamische Typisierung wie Ruby (duck typing), kann Funktionen als Parameter, Rückgabewerte und Variableninhalte nutzen (first class functions), ermöglicht generative Datenstrukturen, Closures und das von Haskell bekannte Currying (aka Schönfinkeln).

<note important>wird fortgesetzt</note>

start/themen/boo.txt · Zuletzt geändert: 2018/09/21 16:45 (Externe Bearbeitung)