Warum hat Clojure fünf Möglichkeiten, eine Klasse zu definieren, anstatt nur eine?

84

Clojure verfügt über Gen-Klasse, Reify, Proxy sowie Deftype und Defrecord, um neue klassenähnliche Datentypen zu definieren. Für eine Sprache, die syntaktische Einfachheit schätzt und unnötige Komplexität verabscheut, scheint dies eine Aberration zu sein. Könnte jemand erklären, warum es so ist? Könnte eine Defclass im Common Lisp-Stil ausreichen?

Salil
quelle

Antworten:

90

Dies ist eine Mischung aus drei verschiedenen Faktoren:

  1. Das spezielle Typsystem des JVM
  2. Die Notwendigkeit einer leicht unterschiedlichen Semantik für unterschiedliche Anwendungsfälle bei der Definition von Typen
  3. Die Tatsache, dass einige davon früher und andere später entwickelt wurden, als sich die Sprache weiterentwickelt hat.

Lassen Sie uns zunächst überlegen, was diese tun. Deftype und Gen-Klasse sind insofern ähnlich, als sie beide eine benannte Klasse für die vorzeitige Kompilierung definieren. Gen-Klasse stand an erster Stelle, gefolgt von Deftype in Clojure 1.2. Deftype wird bevorzugt und weist bessere Leistungseigenschaften auf, ist jedoch restriktiver. Eine Deftype-Klasse kann einer Schnittstelle entsprechen, aber nicht von einer anderen Klasse erben.

Reify und Proxy werden beide verwendet, um zur Laufzeit dynamisch eine Instanz einer anonymen Klasse zu erstellen. Proxy stand an erster Stelle, Reify kam zusammen mit Deftype und Defrecord in Clojure 1.2. Reify wird ebenso bevorzugt wie Deftype, bei dem die Semantik nicht zu restriktiv ist.

Das lässt die Frage offen, warum sowohl Deftype als auch Defrecord, da sie gleichzeitig auftraten und eine ähnliche Rolle spielen. Für die meisten Zwecke werden wir Defrecord verwenden wollen: Es hat all die verschiedenen Clojure-Güte, die wir kennen und lieben, Sequenzierbarkeit und so weiter. Deftype ist als Baustein auf niedriger Ebene für die Implementierung anderer Datenstrukturen vorgesehen. Es enthält keine regulären Clojure-Schnittstellen, bietet jedoch die Möglichkeit, Felder zu ändern (obwohl dies nicht die Standardeinstellung ist).

Weitere Informationen finden Sie unter:

Die Seite mit den Datentypen von clojure.org

Der Google-Gruppenthread, in dem Deftype und Reify eingeführt wurden

Rob Lachlan
quelle
1
Der Google-Gruppenthread war sehr wertvoll. Mein Verständnis ist für Java-Interop-Proxy, Gen-Klasse werden meistens durch Reify und Deftype ersetzt. Ich bin froh, dass wir jetzt weniger empfohlene Möglichkeiten haben, Typen zu definieren.
Salil
2
@Trylks: Die Fähigkeit, das Objekt als eine Folge von Schlüssel-Wert-Paaren zu behandeln. So ziemlich alles, was in Clojure beheimatet ist, kann als eine Sequenz behandelt werden, die sehr mächtig ist.
Rob Lachlan
proxywird manchmal bevorzugt, weil es die Änderung von Methoden zur Laufzeit ermöglicht. (zB The Joy of Clojure , 2. Ausgabe, schlägt vor, dass dies nützlich sein könnte, um Rückrufe in einem Web-Handler zu ändern.)
Mars
52

Die kurze Antwort lautet, dass sie alle unterschiedliche und nützliche Zwecke haben. Die Komplexität beruht auf der Notwendigkeit, effektiv mit verschiedenen Funktionen der zugrunde liegenden JVM zusammenzuarbeiten.

Wenn Sie kein Java-Interop benötigen, halten Sie sich in 99% der Fälle am besten an Defrecord oder eine einfache Clojure-Map.

  • Verwenden Sie defrecord, wenn Sie Protokolle verwenden möchten
  • Ansonsten ist eine reguläre Clojure-Karte wahrscheinlich am einfachsten und verständlichsten

Wenn Ihre Anforderungen komplexer sind, ist das folgende Flussdiagramm ein hervorragendes Werkzeug, um zu erklären, warum Sie eine dieser Optionen gegenüber den anderen auswählen würden:

http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/

Flussdiagramm zur Auswahl des richtigen Formulars für die Definition des Clojure-Typs

mikera
quelle
1
Vielen Dank für den Link zum Flussdiagramm. Es hilft, dass der Blog vom Co-Autor von O'reilly's - Programming Clojure stammt. Ich denke, diese Entscheidungsfindung ist ziemlich komplex. Sind die Unterschiede zwischen Schnittstelle und konkreter Klasse oder müssen statische Methoden definiert werden oder nicht oder sind benannte Typen oder anonyme Typen so groß, dass sie ein anderes Sprachkonstrukt benötigen?
Salil
4
Die Unterschiede sind nicht groß, aber sie unterscheiden sich philosophisch in Bezug auf das, was Sie erreichen wollen. Ich denke, die vielfältigen Ansätze in Clojure spiegeln diese zugrunde liegenden Unterschiede wider, was ein guter Grund ist, warum sie unterschiedliche Namen erhalten sollten.
Mikera
Das Flussdiagramm ist großartig, deckt jedoch nicht alle Möglichkeiten oder Gründe für die Verwendung der einen oder anderen Optionen oder Kombinationen davon ab. Kein einfaches Diagramm könnte.
Mars