- Self (Programmiersprache)
-
Self The Power of Simplicity
Objektorientierte SpracheBasisdaten Entwickler Sun Aktuelle Version 4.4
(16. Juli 2010)Betriebssystem Windows, Linux, Mac OS X, Sun Solaris. Kategorie objektorientierte Programmiersprache Lizenz implementationsspezifisch Deutschsprachig keine http://selflanguage.org/ Self [sɛlf] ist eine Programmiersprache, die vor allem in Hinblick auf Ausdrucksfähigkeit und Formbarkeit hin von Randall B. Smith und David Ungar entworfen wurde. Um diese Ziele zu erreichen, wurde ein rein prototypenbasiertes Objektmodell mit einheitlichem Zugriff auf Zustand und Verhalten der Objekte, also auf ihre Attribute und Methoden, entwickelt. Im Gegensatz zu anderen Programmiersprachen ist es in Self möglich, den Zustand von Objekten zu vererben und die Vererbung zur Laufzeit dynamisch anzupassen.
Die Version 4.3 von Sun wurde im Juni 2006 veröffentlicht. Sie läuft auf Intel- und PowerPC-basierten Apple-Rechnern und auf Sun SPARC, aber nicht unter Linux oder Microsoft Windows.[1] Die Weiterentwicklung wird nicht mehr durch Sun betrieben. Version 4.4 läuft unter Mac OS X und Linux.[2] Seit Februar 2011 gibt es eine experimentelle Version 4.5.[3]
Ideen und Konzepte, die ihren originären Ursprung in der Programmiersprache Self haben, wurden über die Jahre sowohl in das Squeak Smalltalk-System übernommen, als auch in Programmiersprachen wie Slate oder io weitergeführt.
Die herausragende Self-Eigenschaft ist wohl das Self-Universum, eine Art grafische Benutzeroberfläche (GUI), innerhalb derer mit dem Self-Laufzeitsystem interagiert werden kann. Eine Interaktion erfolgt hierbei in der Regel über die Maus und/oder die Tastatur. Visualisiert wird das Self-Universum mittels eines GUI-Frameworks namens Morphic. Betritt eine Person das Self-Universum, so geschieht dies immer über die Lobby, eine Anspielung auf die Empfangshalle eines Hotels. In ihr befinden sich alle im System vorhandenen Objekte, Module, Namensräume und Traits. Das Konzept des (Programmier-)Universums als auch das Morphic-Konzept wurde nahezu identisch im Squeak Smalltalk-System umgesetzt.
Trotz der diversen Portierungen sollte man bedenken, dass es sich bei der Programmiersprache Self um ein eher akademisch motiviertes Unterfangen handelt. Trotzdem hatte und haben die in Self erprobten Neuheiten Einfluss auf neuere objektorientierte Programmiersprachen.
Inhaltsverzeichnis
Geschichte
Self wurde ursprünglich 1986 von David Ungar und Randall B. Smith während ihrer Arbeit am Xerox-Parc-Institut entworfen. Ihr Ziel war es, nachdem Smalltalk-80 veröffentlicht worden war, die Software-Technologie weiter voranzubringen. Sie wechselten zur Stanford-Universität und veröffentlichten im Folgejahr den ersten funktionierenden Compiler. Das erste vollständige Self-System wurde 1990 veröffentlicht; im Jahr darauf wechselte die Gruppe zu Sun Microsystems, wo das Self-System als Experimentierfeld für die Entwicklung genutzt wurde. Hier kam Urs Hölzle dazu, der neue Compiler- und VM-Techniken entwickelte und an der Entwicklung der Programmiersprache Strongtalk beteiligt war. 1995 wurde die Entwicklung bei Sun Microsystems offiziell eingestellt: „Self 4.0 ist, in einem gewissen Sinn, der Gipfelpunkt des Self-Projekts, das aber bei Sun offiziell nicht weitergeführt wird.“[4] Allerdings gab es noch die Entwicklergruppe an der Stanford Universität sowie verschiedene andere Initiativen. Trotz des offiziellen Stops wurde im September 2002 die Version 4.1.6 veröffentlicht.[5] Im April 2004 erschien dann die Version 4.2 und im Juni 2006 die Version 4.3.[1] Seit Juli 2010 gibt es eine Version 4.4.[2] Eine experimentelle Version 4.5 gibt es seit Februar 2011.[3]
Die Entwicklungsumgebung
Eine der wenigen Einschränkungen, die die Self-Entwicklungsumgebungen mit sich bringt, ist die Tatsache, dass die Laufzeitumgebung auf einer virtuellen Maschine basiert, die starke Ähnlichkeit mit den VMs früher Smalltalksysteme hat. Daraus resultiert, dass Programme keine eigenständige Entität darstellen, wie dies zum Beispiel bei C-Programmen der Fall ist. Self-Programme benötigen immer die zugehörige Speicherumgebung, um ausgeführt werden zu können. Als Folge müssen Anwendungen als Abbild des Speichers ausgeliefert werden. Dieses Speicherabbild wird auch Snapshot genannt. Snapshots sind oftmals sehr groß und umständlich zu benutzen.
Auf der anderen Seite ist die Self-Entwicklungsumgebung sehr mächtig. Programme können zu jedem Zeitpunkt unterbrochen werden, um einzelne Werte oder ein Stück Code zu ändern. Das Programm lässt sich anschließend genau an der Stelle fortsetzen, an der es zuvor unterbrochen wurde. Diese Art des „on the fly“-Entwickelns wirkt sich sehr positiv auf die Produktivität aus.
Zusätzlich ist die Entwicklungsumgebung auf das schnelle und kontinuierliche Ändern einzelner Objekte zugeschnitten. Das Refactoring des „Klassen“-Designs ist beinahe so einfach wie das Entfernen einer Methode via Drag & Drop, um sie so einem neuen Objekt zuordnen zu können. Einfachere Aufgaben wie das Erstellen von Test-Methoden können durch eine Kopie bewältigt werden. Die entsprechende Methode wird anschließend in die Objekt-Kopie gezogen, um sie so verändern zu können. Im Vergleich zu traditionellen Systemen besitzt nur das soeben neu erzeugte Objekt auch neuen Quellcode. Es ist folglich nicht erforderlich, Code neu zu kompilieren, um ihn testen zu können. Sobald sichergestellt wurde, dass die Test-Methode entsprechend der Spezifikation funktioniert, kann diese zurück in das Ausgangs-Objekt kopiert werden.
Die Self-VM war lange Zeit nur für Macintosh auf PowerPC-Architektur verfügbar, wurde jedoch im Juli 2006 auf Intel-Macintosh portiert.
Sprachbeschreibung
Im Folgenden soll nur ein erster Eindruck dieser Programmiersprache vermittelt werden. Für umfassende Informationen sei auf The Self Language[6] und Teaching Self and Prototype-Based Programming[7] verwiesen.
Self ist eine objektorientierte, prototypenbasierte Programmiersprache. Die Grundlage bilden einige wenige einfache Konstrukte: Prototypen, Steckplätze und das Verhalten. Im Gegensatz zu den Konzepten von Smalltalk und vielen anderen Programmiersprachen gibt es weder Klassen noch Variablen. Das Programmiermodell beruht im Wesentlichen auf kommunizierenden Objekten und einer einfachen Form von Vererbung.
In Self werden keine Programme im eigentlichen Sinn geschrieben – stattdessen wird das „Self-Universum“ beschrieben. Hier gibt es nur Objekte, die durch das Verschicken von Nachrichten miteinander kommunizieren. Dieses Verschicken einer Nachricht entspricht einem Prozedur-Aufruf in der klassischen Programmierung. Neue Objekte werden durch Klonen bestehender Objekte erzeugt.
Durch das Prototypen-Konzept ist eine interaktive Programmierung möglich: Der Programmierer interagiert mit den Objekten im Self-Universum.
In Self wird nicht zwischen dem Zustand eines Objekts und seinem Verhalten unterschieden. Der Zustand eines Objekts ergibt sich aus seinen Eigenschaften, also den Werten seiner Attribute. Das Verhalten eines Objekts wird in seinen Methoden beschrieben. Dadurch, dass es keinen Unterschied zwischen dem Zustand und dem Verhalten gibt, wird die Lücke verringert, die in anderen Programmiersprachen zwischen Objekten, Prozeduren und Variablen besteht. In Self ist alles eins.
So liefert beispielsweise der Aufruf einer Eigenschaft – sofern das Objekt oder eines seiner Vorfahren diese Eigenschaft denn hat – deren Wert:
aPerson name
In dieser Anweisung wird die Eigenschaft name des Objekts aPerson aufgerufen bzw. abgefragt. Mit der folgenden Anweisung wird derselben Eigenschaft ein Wert zugewiesen:
aPerson name:'myself'
Sprachkonstrukte
Objekte bestehen aus (theoretisch) beliebig vielen Steckplätzen (slots). Ein Steckplatz hat einen Namen und kann ein Attribut (Wert) oder eine Methode (Prozedur) enthalten. In jedem Fall ist es ein Objekt, das er aufnimmt. Ein Objekt besteht somit aus einem oder mehreren Steckplätzen für Objekte auf die mittels des Namen zugegriffen werden kann.
Ein Methoden-Objekt enthält eine oder mehrere Anweisungen, die ausgeführt werden, wenn das Methoden-Objekt aufgerufen wird. Ein Attribut-Objekt liefert sich selbst zurück, wenn es wie ein Methoden-Objekt aufgerufen wird.
Neue Objekte werden durch Klonen erzeugt. Dazu wird ein passendes Objekt als Prototyp benutzt, also sozusagen als eine Kopiervorlage. Jedes Objekt kann als Prototyp genutzt werden.
Ein kurzes Programm
Hier das klassische Hallo-Welt-Programm:
'Hello, World!' print.
Der Punkt am Ende der Anweisung bewirkt, dass die Rückgabe des Wertes – die eigentlich bei allen Nachrichten erfolgt – unterdrückt bzw. vermieden wird. Hier noch einmal in einer aufwändigeren Form:
(desktop activeWindow) draw: (labelWidget copy label: 'Hello, World!').
Erläuterung: Als erstes wird das desktop-Objekt nach dem aktiven Fenster gefragt. Das desktop-Objekt sieht dazu in seiner Liste der aktuellen Fenster nach und liefert das entsprechende Objekt zurück. Diesem Objekt wird in dem Steckplatz namens draw eine zuvor erzeugte Kopie des labelWidget-Objekt eingetragen. Bevor das passiert, wurde der labelWidget-Kopie bereits die Nachricht geschickt, dass es einen Steckplatz mit dem Namen label anlegen soll und dort den Wert Hello, World! ablegen soll.
Vererbung
Theoretisch ist jedes Self-Objekt eine eigenständige Entität. Es existieren keine Klassen, keine Meta-Klassen etc., um die Eigenschaften und Verhaltensweisen eines Objektes zu spezifizieren. Änderungen am Objekt selbst beeinflussen das Verhalten anderer Objekte nicht. In Einzelfällen jedoch wäre dies durchaus sinnvoll. Grundsätzlich versteht ein Objekt nur die Nachrichten, die auch einem seiner Slots eindeutig zugeordnet werden können. Verweisen jedoch ein oder mehrere Slots auf ein parent-Objekt, so ist das Objekt imstande, Nachrichten an diese Elternobjekte zu delegieren, insofern es diese selbst nicht zu interpretieren vermag. Auf diesem Weg verwaltet Self Aufgaben, die in traditionellen objektorientierten Sprachen das Vehikel der Vererbung benötigt hätten. Diese Verfahrensweise wird zudem benutzt, um Namensräume zu implementieren.
Um den Zusammenhang der Vererbung besser veranschaulichen zu können, soll an dieser Stelle von einem Objekt namens BankKonto ausgegangen werden. Dieses soll in einer einfachen Buchhaltungsanwendung zum Einsatz kommen. Es liegt nahe, das Objekt BankKonto mit den Methoden einzahlen und abheben auszustatten. Zusätzliche Slots für einfache Daten werden ebenfalls benötigt. Das soeben spezifizierte Objekt ist ein Prototyp, stellt aber bereits ein voll funktionsfähiges Bankkonto dar.
Erstellt man einen Klon des Objekts BankKonto für „Bobs Konto“, so erhält man eine Kopie, die mit dem ursprünglichen Prototyp identisch ist. Eine effektivere Herangehensweise ist es jedoch, ein einfaches Objekt namens traits object zu erstellen, das all die Elemente enthält, die man normalerweise mit einer Klasse assoziieren würde.
Analog zum aktuellen Beispiel, würde das Objekt BankKonto als logische Konsequenz weder eine einzahlen- noch eine auszahlen-Methode besitzen. Vielmehr hat es nun ein Elternobjekt, das diese Methoden enthält. Auf diese Weise ist es möglich, unzählige Kopien des Bankkonto-Objekts zu erzeugen, deren Verhalten durch eine einzige Änderung am ElternObjekt verändert werden können.
Wie unterscheidet sich der geschilderte Sachverhalt von den traditionellen Klassen einer OO-Sprache? Was bedeutet wohl das nachfolgende Konstrukt:
myObject parent: someOtherObject.
Dieses Konstrukt verändert die „Klasse“ von myObject zur Laufzeit, indem der Wert, der mit dem Slot parent* assoziiert ist, verändert wird (der Stern ist Teil des Slot-Namens und gehört folglich nicht zur Nachricht, die dieser Slot empfängt).
Slots hinzufügen
An dieser Stelle stellt sich die Frage, wie Kopien eines Self-Objekts modifiziert werden müssen, damit diese neue Slots enthalten. Benutzt man die grafische Self-Programmierumgebung, gestaltet sich dies ziemlich einfach. Programmatisch gesehen ist es am sinnvollsten, ein so genanntes Mirror-Objekt des Objekts zu erzeugen und dann zu modifizieren. Dazu schickt man diesem Mirror-Objekt entsprechende Nachrichten.
Ein Weg, der schneller zum Ziel führt, ist den Primitiv _AddSlots: zu verwenden. Ein Primitiv hat dieselbe Syntax wie eine normale Schlüsselwort-Nachricht. Jedoch beginnt dessen Name mit einem Unterstrich. Der _AddSlots-Primitiv gilt inzwischen als überholt; wegen seiner Kürze wird er hier trotzdem verwendet.
Eines der ersten Beispiele beschäftigte sich mit dem Refactoring einer einfachen Klasse namens Fahrzeug. Das Refactoring wurde notwendig, um zwischen Personen- und Lastkraftwagen unterscheiden zu können. Versucht man das Refactoring in Self umzusetzen, so sieht dies etwa aus wie folgt:
_AddSlots: (| vehicle <- (|parent* = traits clonable|) |).
Da der Empfänger des _AddSlots:-Primitivs nicht angegeben wurde, ist dieser automatisch „self“. Wird ein Ausdruck innerhalb des Shell-Prompt der Entwicklungsumgebung eingegeben und ausgeführt, so ist das Objekt, das daraus resultierende Nachrichten empfängt, immer das lobby-Objekt. Das an _AddSlots: übergebene Argument ist das Objekt, das in das Empfänger-Objekt kopiert wird. In diesem Fall handelt es sich um ein Objekt-Literal mit genau einem Slot. Der Name des Slots ist vehicle, und dessen Wert ist ein weiteres Objekt-Literal. Die Notationsweise „
<-
“ impliziert einen weiteren Slot namens vehicle:, der notwendig ist, um den Wert des ersten Slots zu verändern.Das „
=
“ weist auf einen Slot mit konstantem Wert hin. Aus diesem Grund gibt es auch keinen korrespondierenden parent: Slot. Das Objekt-Literal, das den Initialwert vehicle repräsentiert, hat einen Slot, um auf Nachrichten des Klonens reagieren zu können. Ein Objekt, das tatsächlich leer ist, wird durch(||)
oder einfach()
dargestellt. Ein solches Objekt ist nicht in der Lage, Nachrichten zu empfangen.vehicle _AddSlots: (| name <- 'automobile'|).
In diesem Beispiel ist der Nachrichten-Empfänger das vorherige Objekt, das zusätzlich zum parent*-Slot sowohl einen name- als auch einen name:-Slot enthält.
_AddSlots: (| sportsCar <- vehicle copy |). sportsCar _AddSlots: (| driveToWork = (''some code, this is a method'') |).
Trotz der Tatsache, dass es sich zuvor bei den Objekten vehicle und sportsCar um identische Objekte handelte, beinhaltet das aktuelle Objekt nun einen Slot, dem eine Methode zugeordnet ist, die es zuvor nicht gab. Methoden können übrigens nur in Slots mit konstantem Wert enthalten sein.
_AddSlots: (| porsche911 <- sportsCar copy |). porsche911 name:'Bobs Porsche'.
Versionen
- 4.0: läuft auf SPARC-basierten Sun-Workstations mit SunOS 4.1.x, Solaris 2.3, oder Solaris 2.4 als Betriebssystem
- 4.1.6: läuft auch als Anwendung unter Mac OS X. Die Unterstützung für Mac OS 9 wurde eingestellt
- 4.2.1: läuft auch auf G3-Macs
- 4.3: läuft auch auf Intel-basierten Macs
- 4.4: läuft unter Mac OS X und Linux (x86)
Literatur
- A Self Bibliography (engl., Literaturliste bei Sun's ehemaliger Self-Group)
Siehe auch
Ähnliche Programmiersprachen
Weblinks
- Self resources at Cetus Links
- Yahoo!-Gruppe über Self
Websites der Self-Entwickler
Liste einiger Self-Implementierungen
- Die originäre Self-Implementierung von Sun
- Self-Implementierung für x86-Linux und -Cygwin
- Distributed Self
Einzelnachweise
- ↑ a b release 4.3 bei sun.com
- ↑ a b Version 4.4 bei selflanguage.org
- ↑ a b Version 4.5 auf blog.selflanguage.org
- ↑ release 4.0 bei sun.com
- ↑ release 4.1 bei sun.com
- ↑ language auf research.sun.com
- ↑ teaching auf research.sun.com
Wikimedia Foundation.