Rodrigo Barreto de Oliveira war frustriert. Keine der vorhandenen Programmiersprachen eignete sich für sein anstehendes Projekt. In Python vermisste er eine strenge statische Typprüfung und eine gute Anbindung an das Dotnet-Framework. Die Alternative C# hat zwar eine nette Syntax, verursacht aber eine Menge Tipparbeit.
Also entwarf er eine eigene Skriptsprache (Abbildung 1) mit einem kleinen Pacman-Gespenst als Logo. Sie sollte auf Python basierten, sich aber auf die Common Language Infrastructure (CLI) und das Dotnet-Framework stützen. Das Ergebnis würzte er noch mit ein paar Konzepten aus C# und Ruby.
Heraus kam die objektorientierte Sprache Boo [1], die Dank der Mono-Umgebung [2] auch unter Linux zeigt, was in ihr steckt. Weil das obligatorische Hello World mit der Zeile »print("Hallo Welt")« etwas zu trivial ist, zeigt Listing 1 eine GTK#-Variante (Abbildung 2).
Boo steckt einen zusammenhängenden Codeblock nicht wie vielfach üblich in geschweifte Klammern, sondern arbeitet wie Python mit Einrückungen. Nicht einmal ein »end« als Abschluss ist erlaubt. Ebenso sind Semikolons am Ende einer jeden Zeile verpönt und nur in Zweifelsfällen angebracht. Im Gegenzug erlaubt Boo nicht nur die Python-Kommentare mit einer Raute #, sondern auch die von C und C++ bekannten Varianten mit »/*« und »//«.
Abbildung 1: Auf der übersichtlichen Homepage des Boo-Projekts findet sich neben umfangreichen Codebeispielen auch eine sehr gute Einführung in die Sprache.
Abbildung 2: Das Hallo-Welt-Programm in der GTK#-Fassung: Kein Button, dafür nur sieben Zeilen Code.
01 import Gtk
02
03 Application.Init()
04 window = Window("Hallo Welt", DefaultWidth: 300, DefaultHeight: 200)
05
06 window.DeleteEvent += def():
07 Application.Quit ()
08
09 window.ShowAll()
10 Application.Run()
|
Typisierte Variablen
Im Unterschied zu Python verwendet Boo normalerweise statische, typisierte Variablen, man muss also ihren Typ vor dem ersten Einsatz angeben. Das hat den Vorteil, dass der Compiler schon zur Übersetzungszeit fehlerhafte Zuweisungen erkennt und direkt anmahnt. Damit der Programmierer nicht wie in anderen Sprachen mit den Typen jonglieren muss, denkt der Compiler von Boo aber mit und leitet den korrekten Typ automatisch nicht vollständig qualifizierter Variablen ab (Type Inference). Beispiel:
a as int // a ist ein Integer
a=3
b=a
print b
Damit ist klar, dass »b« nur eine Zahl sein kann. Sogar die erste Zeile wäre im Beispiel überflüssig, da der Compiler aufgrund der zugewiesenen »3« genügend Informationen besitzt. Nur wenn diese direkte Ableitung nicht funktioniert, ist die Angabe des Typs zwingend.
Recht einfach gestaltet sich auch der Umgang mit Listen, Arrays und Hashtables. Um beispielsweise eine Liste »a« mit den Zahlen 1 bis 5 zu füllen, könnte man entweder mit »a = List(range(1,5))« direkt ein Listenobjekt erstellen oder die Kurznotation verwenden:
a= [1,2,3,4, 'fuenf']
print(a[:3])
Die eingebaute Funktion »range()« liefert Abschnitte aus dem angegebenen Bereich. Wie das Beispiel zeigt, darf man sogar gemischte Typen in die Liste einfügen. Die Kurznotation beim »print«-Befehl bezeichnet die Boo-Terminologie als Slicing-Operation, die in diesem Fall die ersten drei Werte aus »a« zurückliefert. Als zusätzliches Schmankerl unterstützt Boo reguläre Ausdrücke, darunter sogar den »Match«-Operator »=~« aus Perl:
beispieltext = "Dies ist ein Test"
if beispieltext =~ @/Te.t/:
print("ist enthalten")
Dieses Beispiel fahndet im »beispieltext« nach der Zeichenkette »Test«, wobei zwischen »e« und »t« auch ein beliebiger anderer Buchstabe auftauchen darf. Der reguläre Ausdruck steckt zwischen den Zeichen »@/« und »/«.
Funktionen im Python-Stil
Bei der Definition von Funktionen fühlen sich Python-Kenner sofort zu Hause:
def Hallo(name as string):
return "Ihr Name: ${name}"
print Hallo("Klaus")
Boo behandelt Funktionen als Objekte erster Klasse. Das Konzept der so entstehenden First Class Functions stammt ursprünglich aus den funktionalen Programmiersprachen. Mit derartigen Funktionen darf der Programmierer alles anstellen, was die Programmiersprache hergibt. So kann er sie ruhigen Gewissens in eine Variable stecken oder als Argumente an andere Funktionen übergeben. Selbstverständlich darf er sie auch als Ergebnis zurückliefern:
def funktion1(item as int):
return item // mache nichts
def funktion2():
return funktion1
Boo erlaubt es ihm sogar, eine Funktion innerhalb einer anderen zu definieren:
def Aussen():
Innen = def():
print("innen wurden aufgerufen")
Innen()
Aussen()
Damit ist es nur noch ein kleiner Schritt zu den so genannten Closures. Dieses Konzept hat Boo ebenfalls aus der funktionalen Programmierung geerbt. Unter einem Closure, Block oder Funktionsabschluss ist ein Stück Programmcode beziehungsweise ein Funktionsrumpf zu verstehen, der die lokalen Variablen seiner umgebenden Funktion sehen und verwenden kann.