Почему Clojure имеет 5 способов определить класс вместо одного?

Clojure имеет gen-class, reify, proxy, а также deftype и defrecord, чтобы определить новые типы данных типа. Для языка, который оценивает синтаксическую простоту и терпит ненужную сложность, это кажется аберрацией. Может ли кто-нибудь объяснить, почему это так? Может ли общий Lisp -строчный defclass достаточно?

Ответ 1

Это сочетание трех разных факторов:

  • Конкретная система типов jvm
  • Необходимость немного отличающейся семантики для разных случаев использования при определении типов
  • Тот факт, что некоторые из них были разработаны ранее, а некоторые позже, когда язык развился.

Итак, сначала рассмотрим, что они делают. deftype и gen-class похожи друг на друга, поскольку оба они определяют именованный класс для компиляции вовремя. На первом месте появился класс Gen, за которым следует deftype в clojure 1.2. Deftype является предпочтительным и имеет лучшие характеристики производительности, но более ограничительный. Класс deftype может соответствовать интерфейсу, но не может наследовать от другого класса.

Reify и прокси используются для динамического создания экземпляра анонимного класса во время выполнения. Сначала был прокси-сервер, reify - с deftype и defrecord в clojure 1.2. Reify предпочтительнее, так же как и deftype, где семантика не является слишком ограничительной.

Это оставляет вопрос о том, почему и ложь, и defrecord, поскольку они появились в одно и то же время, и имеют аналогичную роль. Для большинства целей мы хотим использовать defrecord: у него есть всякая доброта clojure, которую мы знаем и любим, секвенируем и так далее. Deftype предназначен для использования в качестве низкоуровневого строительного блока для реализации других структур данных. Он не включает обычные интерфейсы clojure, но у него есть опция изменяемых полей (хотя это не значение по умолчанию).

Для дальнейшего чтения:

Страница

Тема группы google, в которой были введены deftype и reify

Ответ 2

Короткий ответ заключается в том, что все они имеют разные и полезные цели. Сложность связана с необходимостью эффективно взаимодействовать с различными функциями базовой JVM.

Если вам не нужен какой-либо Java-interop, тогда 99% времени вам лучше всего придерживаться либо defrecord, либо простой карты Clojure.

  • Используйте defrecord, если вы хотите использовать протоколы.
  • В противном случае обычная карта Clojure, вероятно, самая простая и понятная

Если ваши потребности более сложны, то следующая блок-схема является отличным инструментом для объяснения того, почему вы выбрали один из этих вариантов над другими:

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

Блок-схема для выбора формы определения типа  Clojure