Dylan (Programmiersprache) - Dylan (programming language)

Dylan
Dylan-logo.png
Paradigma Multiparadigma : funktional , objektorientiert
Entwickler Open-Source-Community Apple Computer , Harlequin , Carnegie Mellon University
Erstmals erschienen 1992 ; vor 29 Jahren ( 1992 )
Stabile Version
2020.1 / 10. Oktober 2020 ; vor 8 Monaten ( 2020-10-10 )
Schreibdisziplin Stark, dynamisch
Plattform IA-32 , x86-64
Betriebssystem Plattformübergreifend
Dateinamenerweiterungen dylan
Webseite opendylan .org
Wichtige Implementierungen
Öffne Dylan , Gwydion Dylan
Dialekte
Infix-Dylan (AKA Dylan), Präfix-Dylan (AKA Lisp)
Beeinflusst von
CLOS , ALGOL , Schema , EuLisp
Beeinflusst
Lasso , Python , Ruby , Julia

Dylan ist eine Programmiersprache mit mehreren Paradigmen , die die funktionale und objektorientierte Programmierung (OOP) unterstützt und dynamisch und reflektierend ist und gleichzeitig ein Programmiermodell bietet, das die Generierung von effizientem Maschinencode unterstützt, einschließlich einer fein abgestuften Kontrolle über dynamisches und statisches Verhalten . Es wurde in den frühen 1990er Jahren von einer Gruppe unter der Leitung von Apple Computer erstellt .

Eine kurze und gründliche Übersicht über die Sprache finden Sie im Dylan-Referenzhandbuch.

Dylan leitet sich von Scheme und Common Lisp ab und fügt ein integriertes Objektsystem hinzu, das vom Common Lisp Object System (CLOS) abgeleitet ist. In Dylan sind alle Werte (einschließlich Zahlen, Zeichen, Funktionen und Klassen ) erstklassige Objekte . Dylan unterstützt Mehrfachvererbung , Polymorphismus , mehrere Dispatch , Stichwort Argumente , Objekt Innerlichkeit, Muster -basierten Syntax Erweiterung Makros und viele andere erweiterte Funktionen. Programme können eine feinkörnige Kontrolle über die Dynamik ausdrücken, indem sie Programme zulassen, die ein Kontinuum zwischen dynamischer und statischer Programmierung einnehmen, und die evolutionäre Entwicklung unterstützen (was ein schnelles Prototyping gefolgt von inkrementeller Verfeinerung und Optimierung ermöglicht).

Das Hauptziel von Dylan ist es, eine dynamische Sprache zu sein, die sich gut für die Entwicklung kommerzieller Software eignet . Dylan versucht, potenzielle Leistungsprobleme anzugehen, indem er "natürliche" Grenzen für die volle Flexibilität von Lisp- Systemen einführt , die es dem Compiler ermöglichen, kompilierbare Einheiten wie Bibliotheken klar zu verstehen .

Dylan leitet einen Großteil seiner Semantik von Scheme und anderen Lisps ab; Einige Dylan-Implementierungen wurden ursprünglich in bestehenden Lisp-Systemen erstellt. Dylan hat jedoch eine ALGOL- ähnliche Syntax anstelle einer Lisp-ähnlichen Präfix-Syntax.

Geschichte

Dylan wurde in den frühen 1990er Jahren von einer Gruppe unter der Leitung von Apple Computer gegründet . Zu einem bestimmten Zeitpunkt in seiner Entwicklung war es für die Verwendung mit dem Apple Newton- Computer vorgesehen, aber die Dylan-Implementierung erreichte rechtzeitig keine ausreichende Reife, und Newton verwendete stattdessen eine Mischung aus C und dem von Walter Smith entwickelten NewtonScript . Apple beendete seine Dylan-Entwicklung im Jahr 1995, obwohl es eine "Technologie-Release"-Version (Apple Dylan TR1) zur Verfügung stellte, die eine fortschrittliche integrierte Entwicklungsumgebung (IDE) enthielt .

Zwei weitere Gruppen trugen zum Design der Sprache bei und entwickelten Implementierungen: Harlequin veröffentlichte eine kommerzielle IDE für Microsoft Windows und die Carnegie Mellon University veröffentlichte einen Open-Source- Compiler für Unix- Systeme namens Gwydion Dylan. Beide Implementierungen sind jetzt Open Source. Die Harlequin-Implementierung heißt jetzt Open Dylan und wird von einer Gruppe von Freiwilligen, den Dylan Hackers, gepflegt.

Die Dylan-Sprache trug den Codenamen Ralph. James Joaquin wählte den Namen Dylan für "DYnamic LANguage".

Syntax

Viele der Syntaxfunktionen von Dylan stammen aus seinem Lisp-Erbe. Ursprünglich verwendete Dylan eine Lisp-ähnliche Präfix-Syntax, die auf s-Ausdrücken basierte . Als das Sprachdesign abgeschlossen war, wurde die Syntax in eine ALGOL-ähnliche Syntax geändert, in der Erwartung, dass sie einem breiteren Publikum von Programmierern bekannter sein würde. Die Syntax wurde von Michael Kahl entworfen. Es wird ausführlich im Dylan-Referenzhandbuch beschrieben.

Lexikalische Syntax

Dylan unterscheidet nicht zwischen Groß- und Kleinschreibung . Dylans lexikalische Syntax ermöglicht die Verwendung einer Namenskonvention, bei der Bindestrich-Minus- Zeichen verwendet werden, um die Teile von Mehrwort-Identifikatoren zu verbinden (manchmal auch " Lisp-Fall " oder " Kebab-Fall " genannt). Diese Konvention ist in Lisp-Sprachen üblich, kann jedoch nicht in Programmiersprachen verwendet werden, die Bindestriche-Minuszeichen, die nicht Teil eines numerischen Literals sind, als einzelnes lexikalisches Token behandeln , selbst wenn sie nicht von Leerzeichen umgeben sind .

Neben alphanumerischen Zeichen und Bindestrich-Minus-Zeichen erlaubt Dylan bestimmte nicht-alphanumerische Zeichen als Teil von Bezeichnern. Bezeichner dürfen nicht aus diesen nicht-alphanumerischen Zeichen oder nur aus numerischen Zeichen bestehen. Bei Unklarheiten wird Whitespace verwendet.

Beispielcode

Eine einfache Klasse mit mehreren Slots:

define class <point> (<object>)
  slot point-x :: <integer>,
    required-init-keyword: x:;
  slot point-y :: <integer>,
    required-init-keyword: y:;
end class <point>;

Konventionsgemäß werden Klassen mit Kleiner-als- und Größer-als-Zeichen benannt, die als spitze Klammern verwendet werden , zB die <point>im Codebeispiel genannte Klasse .

In end class <point>beiden classund <point>sind optional. Dies gilt für alle endKlauseln. Sie können beispielsweise eine Anweisung schreiben end ifoder einfach nur endbeenden if.

Dieselbe Klasse, so minimal wie möglich umgeschrieben:

define class <point> (<object>)
  slot point-x;
  slot point-y;
end;

Die Slots sind jetzt beide als <object>. Die Slots müssen manuell initialisiert werden.

Konventionsgemäß beginnen Konstantennamen mit "$":

define constant $pi :: <double-float> = 3.1415927d0;

Eine faktorielle Funktion:

define function factorial (n :: <integer>) => (n! :: <integer>)
  case
    n < 0     => error("Can't take factorial of negative integer: %d\n", n);
    n = 0     => 1;
    otherwise => n * factorial(n - 1);
  end
end;

Hier n!und <integer>sind nur normale Bezeichner.

Es gibt keine explizite Return-Anweisung . Das Ergebnis einer Methode oder Funktion ist der zuletzt ausgewertete Ausdruck. Es ist ein üblicher Stil, das Semikolon nach einem Ausdruck in der Rückgabeposition wegzulassen.

Module vs. Namespace

In vielen objektorientierten Sprachen sind Klassen das wichtigste Mittel zur Kapselung und Modularität; jede Klasse definiert einen Namensraum und steuert, welche Definitionen extern sichtbar sind. Außerdem definieren Klassen in vielen Sprachen eine unteilbare Einheit, die als Ganzes verwendet werden muss. Beispielsweise Stringerfordert die Verwendung einer Verkettungsfunktion das Importieren und Kompilieren gegen alle String.

Einige Sprachen, einschließlich Dylan, enthalten auch einen separaten, expliziten Namensraum oder ein Modulsystem, das die Kapselung auf allgemeinere Weise durchführt.

In Dylan sind die Konzepte von Compile-Unit und Import-Unit getrennt, und Klassen haben mit beiden nichts spezifisch zu tun. Eine Bibliothek definiert Elemente, die zusammen kompiliert und behandelt werden sollen, während ein Modul einen Namensraum definiert. Klassen können nach Wunsch des Programmierers zu Modulen zusammengefasst oder überschnitten werden. Oftmals existiert die komplette Definition für eine Klasse nicht in einem einzelnen Modul, sondern ist auf mehrere verteilt, die optional zusammengefaßt werden. Verschiedene Programme können unterschiedliche Definitionen derselben Klasse haben, einschließlich nur der, die sie benötigen.

Betrachten Sie beispielsweise eine Add-On-Bibliothek für die Regex- Unterstützung auf String. In einigen Sprachen muss die Funktionalität zum StringNamespace hinzugefügt werden, damit die Funktionalität in Strings eingeschlossen werden kann . Sobald dies der Fall ist, wird die StringKlasse größer, und Funktionen, die keine Regex verwenden müssen, müssen immer noch in erhöhter Bibliotheksgröße dafür "bezahlen". Aus diesem Grund werden diese Arten von Add-Ons normalerweise in eigenen Namensräumen und Objekten platziert. Der Nachteil dieses Ansatzes ist, dass die neuen Funktionen nicht mehr Teil von String ; Stattdessen ist es in einem eigenen Satz von Funktionen isoliert, die separat aufgerufen werden müssen. Anstelle von myString.parseWith(myPattern), was aus OO-Sicht die natürliche Organisation wäre, wird etwas wie myPattern.parseString(myString)verwendet, was die Reihenfolge effektiv umkehrt.

Unter Dylan können viele Schnittstellen für denselben Code definiert werden, zum Beispiel könnte die String-Verkettungsmethode sowohl in der String-Schnittstelle als auch in der "concat"-Schnittstelle platziert werden, die alle verschiedenen Verkettungsfunktionen aus verschiedenen Klassen zusammenfasst. Dies wird häufiger in mathematischen Bibliotheken verwendet, wo Funktionen in der Regel auf sehr unterschiedliche Objekttypen anwendbar sind.

Eine praktischere Verwendung des Schnittstellenkonstrukts besteht darin, öffentliche und private Versionen eines Moduls zu erstellen, etwas, das andere Sprachen als Zusatzfunktion enthalten , die ausnahmslos Probleme verursacht und Syntax hinzufügt. Unter Dylan kann jeder Funktionsaufruf einfach in die "Private"- oder "Development"-Oberfläche gestellt werden und öffentlich zugängliche Funktionen in Public. Unter Java oder C++ ist die Sichtbarkeit eines Objekts im Code definiert, was bedeutet, dass ein Programmierer, um eine ähnliche Änderung zu unterstützen, gezwungen wäre, die Definitionen vollständig neu zu schreiben und nicht zwei Versionen gleichzeitig haben könnte.

Klassen

Klassen in Dylan beschreiben slots(Datenelemente, Felder, Ivars usw.) von Objekten auf ähnliche Weise wie die meisten OO-Sprachen. Alle Zugriffe auf Slots erfolgen über Methoden, wie in Smalltalk . Auf Basis der Slot-Namen werden automatisch Standard-Getter- und -Setter-Methoden generiert. Im Gegensatz zu den meisten anderen OO-Sprachen werden andere auf die Klasse anwendbare Methoden oft außerhalb der Klasse definiert, und daher enthalten Klassendefinitionen in Dylan normalerweise nur die Definition des Speichers. Beispielsweise:

define class <window> (<view>)
  slot title :: <string> = "untitled", init-keyword: title:;
  slot position :: <point>, required-init-keyword: position:;
end class;

In diesem Beispiel ist die Klasse " <window>" definiert. Die Syntax von <Klassenname> ist nur eine Konvention, um die Klassennamen hervorzuheben – die spitzen Klammern sind lediglich ein Teil des Klassennamens. Im Gegensatz dazu besteht die Konvention in einigen Sprachen darin, den ersten Buchstaben des Klassennamens groß zu schreiben oder dem Namen ein C oder T voranzustellen (zum Beispiel). <window>erbt von einer einzelnen Klasse, <view>und enthält zwei Slots, titledie eine Zeichenfolge für den Fenstertitel und positioneinen XY-Punkt für eine Ecke des Fensters enthalten. In diesem Beispiel wurde dem Titel ein Standardwert zugewiesen, der Position jedoch nicht. Die optionale init-keyword- Syntax ermöglicht es dem Programmierer, beim Instanziieren eines Objekts der Klasse den Anfangswert des Slots anzugeben.

In Sprachen wie C++ oder Java würde die Klasse auch ihre Schnittstelle definieren. In diesem Fall enthält die obige Definition keine expliziten Anweisungen, daher wird in beiden Sprachen der Zugriff auf die Slots und Methoden berücksichtigt protected, dh sie können nur von Unterklassen verwendet werden. Damit nicht verwandter Code die Fensterinstanzen verwenden kann, müssen sie deklariert werden public.

In Dylan werden solche Sichtbarkeitsregeln nicht als Teil des Codes betrachtet, sondern als Teil des Modul-/Schnittstellensystems. Dies erhöht die Flexibilität erheblich. Beispielsweise könnte eine in der frühen Entwicklung verwendete Schnittstelle alles als öffentlich deklarieren, während eine beim Testen und Deployment verwendete dies einschränken könnte. Bei C++ oder Java würden diese Änderungen Änderungen am Quellcode erfordern, so dass die Leute dies nicht tun werden, während dies in Dylan ein völlig unabhängiges Konzept ist.

Obwohl es in diesem Beispiel nicht verwendet wird, unterstützt Dylan auch die Mehrfachvererbung .

Methoden und generische Funktionen

In Dylan sind Methoden nicht intrinsisch mit einer bestimmten Klasse verbunden; Methoden können als außerhalb von Klassen existierend betrachtet werden. Wie CLOS basiert Dylan auf multiplem Dispatch (Multimethods), wobei die spezifische aufzurufende Methode basierend auf den Typen all ihrer Argumente ausgewählt wird. Das Verfahren muss zur Kompilierzeit nicht bekannt sein, wobei verstanden wird, dass die erforderliche Funktion basierend auf den Präferenzen eines Benutzers verfügbar sein kann oder nicht.

Unter Java würden dieselben Methoden in einer bestimmten Klasse isoliert. Um diese Funktionalität zu verwenden, ist der Programmierer gezwungen, diese Klasse zu importieren und explizit darauf zu verweisen, um die Methode aufzurufen. Wenn diese Klasse zur Kompilierzeit nicht verfügbar oder unbekannt ist, wird die Anwendung einfach nicht kompiliert.

In Dylan ist Code von der Speicherung in Funktionen isoliert . Viele Klassen haben Methoden, die ihre eigenen Funktionen aufrufen und dadurch wie die meisten anderen OO-Sprachen aussehen und sich auch so anfühlen. Code kann sich jedoch auch in generischen Funktionen befinden , dh sie sind nicht an eine bestimmte Klasse angehängt und können von jedem nativ aufgerufen werden. Das Verknüpfen einer bestimmten generischen Funktion mit einer Methode in einer Klasse wird folgendermaßen erreicht:

define method turn-blue (w :: <window>)
  w.color := $blue;
end method;

Diese Definition ähnelt denen in anderen Sprachen und würde wahrscheinlich in der <window>Klasse gekapselt sein . Beachten Sie den := setter-Aufruf, der syntaktischer Zucker für color-setter($blue, w).

Die Nützlichkeit generischer Methoden kommt besonders gut zur Geltung, wenn man sich "generischere" Beispiele ansieht. Eine gängige Funktion in den meisten Sprachen ist beispielsweise die to-string, die eine von Menschen lesbare Form für das Objekt zurückgibt . Zum Beispiel könnte ein Fenster seinen Titel und seine Position in Klammern zurückgeben, während ein String sich selbst zurückgibt. In Dylan könnten diese Methoden alle in einem einzigen Modul namens " to-string" zusammengefasst werden, wodurch dieser Code aus der Definition der Klasse selbst entfernt wird. Wenn ein bestimmtes Objekt a nicht unterstützte to-string, konnte es einfach im to-stringModul hinzugefügt werden.

Erweiterbarkeit

Dieses ganze Konzept mag einigen Lesern sehr seltsam vorkommen. Der to-stringfür ein Fenster zu behandelnde Code ist nicht in <window>? Dies macht möglicherweise keinen Sinn, bis Sie bedenken, wie Dylan den Anruf der to-string. In den meisten Sprachen wird beim Kompilieren des Programms to-stringnach <window>gesucht und durch einen Zeiger (mehr oder weniger) auf die Methode ersetzt. In Dylan tritt dies beim ersten Ausführen des Programms auf; die Laufzeit erstellt eine Tabelle mit Details zu Methodennamen/Parametern und sucht Methoden dynamisch über diese Tabelle. Das bedeutet, dass sich eine Funktion für eine bestimmte Methode überall befinden kann, nicht nur in der Compile-Time-Unit. Letztendlich wird dem Programmierer eine beträchtliche Flexibilität eingeräumt, wo er seinen Code platzieren kann, indem er ihn wo angemessen nach Klassenlinien sammelt und nach funktionalen Linien, wo dies nicht der Fall ist.

Daraus folgt, dass ein Programmierer Funktionen zu bestehenden Klassen hinzufügen kann, indem er Funktionen in einer separaten Datei definiert. Zum Beispiel möchten Sie vielleicht allen <string>s eine Rechtschreibprüfung hinzufügen , die in den meisten Sprachen Zugriff auf den Quellcode der String-Klasse erfordern würde – und solche grundlegenden Klassen werden selten in Quellform ausgegeben. In Dylan (und anderen "erweiterbaren Sprachen") könnte die Methode zur Rechtschreibprüfung im spell-checkModul hinzugefügt werden, die alle Klassen definiert, auf die sie über das define methodKonstrukt angewendet werden kann . In diesem Fall könnte die eigentliche Funktionalität in einer einzigen generischen Funktion definiert werden, die einen String nimmt und die Fehler zurückgibt. Wenn das spell-checkModul in Ihr Programm kompiliert wird, erhalten alle Strings (und andere Objekte) die zusätzliche Funktionalität.

Apfel Dylan

Apple Dylan ist die von Apple Computer produzierte Implementierung von Dylan . Es wurde ursprünglich für das Apple Newton- Produkt entwickelt.

Verweise

Externe Links