Clojure менее гомоиконичен, чем другие lisps?
Утверждение, которое, как я помню, повторялось в видеороликах Clojure for Lisp Programmers , состоит в том, что большая слабость ранних Лиспов, особенно Common Lisp, заключается в том, что слишком многое связано со структурой списка Лиспов, особенно
cons
клетки. Вы можете найти одно появление этого утверждения только на 25-й минуте связанного видео, но я уверен, что помню, что слышал его где-то еще в серии. Важно отметить, что в тот же момент видео мы видим этот слайд, показывающий нам, что в Clojure есть много других структур данных, а не только старые списки Lispy:Это меня беспокоит. Мои познания в Лиспе довольно ограничены, но мне всегда говорили, что ключевым элементом его легендарной метапрограммируемости является то, что все — да, все — это список, и именно это предотвращает ошибки, которые мы получаем при попытке метапрограмма на других языках . Означает ли это, что, добавляя новые первоклассные структуры данных, Clojure уменьшил свою гомоиконичность, тем самым усложнив метапрограммирование по сравнению с другими Лиспами, такими как Common Lisp?
4 ответа
что все - да, все - это список
что никогда не было правдой для Lisp
CL-USER 1 > (defun what-is-it? (thing)
(format t "~%~s is of type ~a.~%" thing (type-of thing))
(format t "It is ~:[not ~;~]a list.~%" (listp thing))
(values))
WHAT-IS-IT?
CL-USER 2 > (what-is-it? "hello world")
"hello world" is of type SIMPLE-TEXT-STRING.
It is not a list.
CL-USER 3 > (what-is-it? #2a((0 1) (2 3)))
#2A((0 1) (2 3)) is of type (SIMPLE-ARRAY T (2 2)).
It is not a list.
CL-USER 4 > (defstruct foo bar baz)
FOO
CL-USER 5 > (what-is-it? #S(foo :bar oops :baz zoom))
#S(FOO :BAR OOPS :BAZ ZOOM) is of type FOO.
It is not a list.
CL-USER 6 > (what-is-it? 23749287349723/840283423)
23749287349723/840283423 is of type RATIO.
It is not a list.
и поскольку Lisp является программируемым языком программирования, мы можем добавить внешние представления для не списочных типов данных:
Добавьте примитивную нотацию для класса FRAME.
CL-USER 10 > (defclass frame () (slots))
#<STANDARD-CLASS FRAME 4210359BEB>
Принтер:
CL-USER 11 > (defmethod print-object ((o frame) stream)
(format stream "[~{~A~^ ~}]"
(when (and (slot-boundp o 'slots)
(slot-value o 'slots))
(slot-value o 'slots))))
#<STANDARD-METHOD PRINT-OBJECT NIL (FRAME T) 40200011C3>
Читатель:
CL-USER 12 > (set-macro-character
#\[
(lambda (stream char)
(let ((slots (read-delimited-list #\] stream))
(o (make-instance 'frame)))
(when slots
(setf (slot-value o 'slots) slots))
o)))
T
CL-USER 13 > (set-syntax-from-char #\] #\))
T
Теперь мы можем читать/печатать эти объекты:
CL-USER 14 > [a b]
[A B]
CL-USER 15 > (what-is-it? [a b])
[A B] is of type FRAME.
It is not a list.
Давайте разберем слово «гомоиконичность».
- гомо- , что означает «такой же»
- iconic , здесь означает что-то вроде «представленный»
- -ity , что означает «что-то, имеющее такое свойство»
Качество, которое делает макросы возможными (или, по крайней мере, намного проще), заключается в том, что сам язык представлен теми же структурами данных, которые вы используете для представления других объектов. Обратите внимание, что это слово не «монолистизм» или «все есть список». Поскольку Clojure использует те же структуры данных для описания своего синтаксиса, что и для описания других значений во время выполнения, нет никаких оснований утверждать, что он не является гомоиконичным.
Как ни странно, я мог бы сказать, что один из способов, которым Clojure наименее гомоиконичен, — это функция, которая на самом деле похожа на старые версии lisp: он использует символы для представления исходного кода. В других Лиспах это вполне естественно, потому что символы используются для множества других вещей. Однако в Clojure есть ключевые слова, которые гораздо чаще используются для присвоения имен данным во время выполнения. Символы используются довольно редко; Я бы даже сказал, что они в основном используются для представления элементов исходного кода! Но все другие функции, которые Clojure использует для представления элементов исходного кода, часто используются и для представления других вещей: последовательностей, векторов, карт, строк, чисел, ключевых слов, иногда наборов и, возможно, некоторых других нишевых вещей, о которых я не думаю. в настоящее время.
Как указывали предыдущие ответчики, «гомоиконичность» означает не что иное, как то, что знаменитый «
code
является
data
": Код языка программирования состоит на 100 % из структур данных этого языка, поэтому его легко построить и им можно манипулировать с помощью функций языка, обычно используемых для управления структурами данных.
Поскольку структуры данных Clojure включают в себя списки, векторы, карты... (те, которые вы перечислили), и поскольку код Clojure состоит из списков, векторов, карт... Код Clojure является гомоиконичным.
То же самое верно для Common Lisp и других lisp.
Common Lisp содержит также списки, векторы, хэш-таблицы и т.д. Однако код Common Lisp более жестко придерживается списков, чем код Clojure (аргументы функции находятся в списке аргументов, а не в векторе, как в Clojure). Таким образом, вы можете более последовательно использовать функции для управления списками в целях метапрограммирования (макросы).
Является ли это «более» гомоиконичным, чем Clojure, — это философский вопрос. Но определенно вам нужен меньший набор функций для манипулирования кодом, чем в Clojure.
В Clojure единственной из тех дополнительных структур данных, которые требуются для некоторых языковых конструкций, кроме списков, являются векторы, и они находятся в хорошо известных местах, таких как последовательности аргументов функции или пары символов/выражений а
let
. Все они могут использоваться для литералов данных, но литералы данных реже используются при написании макроса Clojure по сравнению с вызовами функций и вызовами макросов, которые всегда находятся в списках.
Я не знаю ничего в Clojure, что усложняет написание макросов, чем в Common Lisp, и есть несколько особенностей, отличных от Clojure, которые могут сделать его немного проще, например, поведение, при котором выражения Clojure, заключенные в обратные кавычки, по умолчанию будут пространством имен. -qualify символы, что часто требуется для предотвращения случайного «захвата» имени.