Dlaczego Clojure ma 5 sposobów na zdefiniowanie klasy zamiast tylko jednej?

84

Clojure ma klasy gen, reify, proxy, a także deftype i defrecord, aby zdefiniować nowe typy danych podobne do klas. Jak na język, który ceni prostotę składni i brzydzi się niepotrzebną złożonością, wydaje się to aberracją. Czy ktoś mógłby wyjaśnić, dlaczego tak jest? Czy defclass w stylu Common Lisp byłby wystarczający?

Salil
źródło

Odpowiedzi:

90

Jest to połączenie trzech różnych czynników:

  1. Konkretny system typów jvm
  2. Potrzeba nieco innej semantyki dla różnych przypadków użycia podczas definiowania typów
  3. Fakt, że niektóre z nich zostały opracowane wcześniej, a inne później, w miarę rozwoju języka.

Więc najpierw zastanówmy się, co to robi. deftype i gen-class są podobne, ponieważ obie definiują nazwaną klasę do kompilacji z wyprzedzeniem. Klasa Gen zajęła pierwsze miejsce, a następnie deftype w clojure 1.2. Deftype jest preferowany i ma lepszą charakterystykę wydajności, ale jest bardziej restrykcyjny. Klasa deftype może być zgodna z interfejsem, ale nie może dziedziczyć z innej klasy.

Reify i proxy są używane do dynamicznego tworzenia instancji klasy anonimowej w czasie wykonywania. Proxy był pierwszy, reify pojawiło się wraz z deftype i defrecord w clojure 1.2. Preferowana jest reify, podobnie jak deftype, gdzie semantyka nie jest zbyt restrykcyjna.

Pozostaje pytanie, dlaczego zarówno deftype, jak i defrecord, skoro pojawiły się w tym samym czasie i mają podobną rolę. Do większości celów będziemy chcieli użyć defrecord: ma wszystkie różne dobro, które znamy i kochamy, sekwencjonowanie i tak dalej. Deftype jest przeznaczony do wykorzystania jako element składowy niskiego poziomu do wdrażania innych struktur danych. Nie zawiera zwykłych interfejsów clojure, ale ma opcję zmiennych pól (chociaż nie jest to ustawienie domyślne).

Aby przeczytać więcej, sprawdź:

Strona z typami danych clojure.org

Wątek grupy Google, w którym wprowadzono deftype i reify

Rob Lachlan
źródło
1
Wątek grupy Google był bardzo cenny. Rozumiem, że w przypadku proxy java-interop, klasy gen są zastępowane głównie przez reify i deftype. Cieszę się, że mamy teraz mniej „zalecanych” sposobów definiowania typów.
Salil
2
@Trylks: Możliwość traktowania obiektu jako sekwencji par klucz-wartość. Prawie wszystko, co pochodzi z clojure, można traktować jako sekwencję, która jest bardzo potężna.
Rob Lachlan
proxyjest czasami preferowany, ponieważ umożliwia modyfikację metod w czasie wykonywania. (np. The Joy of Clojure , wyd. 2, sugeruje, że może to być przydatne do modyfikowania wywołań zwrotnych w module obsługi sieci).
Mars,
52

Krótka odpowiedź jest taka, że ​​wszystkie mają różne i przydatne cele. Złożoność wynika z potrzeby efektywnej współpracy z różnymi funkcjami podstawowej maszyny JVM.

Jeśli nie potrzebujesz żadnej współpracy z Javą, to w 99% najlepiej jest trzymać się defrecord lub prostej mapy Clojure.

  • Użyj defrecord, jeśli chcesz używać protokołów
  • W przeciwnym razie zwykła mapa Clojure jest prawdopodobnie najprostsza i najbardziej zrozumiała

Jeśli Twoje potrzeby są bardziej złożone, poniższy schemat blokowy jest doskonałym narzędziem do wyjaśnienia, dlaczego wybrałbyś jedną z tych opcji zamiast innych:

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

Schemat blokowy wyboru odpowiedniego formularza definicji typu clojure

mikera
źródło
1
wielkie dzięki za link do schematu blokowego. Pomaga, że ​​blog jest współautorem książki O areilly's - Programming Clojure. Myślę, że ten proces decyzyjny jest dość złożony. Czy różnice między interfejsem a klasą konkretną lub potrzeba zdefiniowania metod statycznych lub typu nazwanego lub typu anonimowego są tak duże, że wymagają innej konstrukcji językowej?
Salil
4
różnice nie są ogromne, ale są filozoficznie różne pod względem tego, co próbujesz osiągnąć. Myślę, że wiele podejść w Clojure odzwierciedla te podstawowe różnice, co jest dobrym powodem, dla którego należy nadać im różne nazwy.
mikera
Schemat blokowy jest świetny, ale nie obejmuje wszystkich możliwości lub powodów używania jednej lub drugiej opcji lub ich kombinacji. Żaden prosty wykres nie mógł.
Mars