Да интересная дискуссия, даже зарегился на RSDN, чтоб сюда запостить.
С Лиспом я знаком года 2-3, с тех пор как плотно начал юзать емакс, но именно CL, ну и схемой, заинтересовался только в конце прошлого года. Сначала прочитал, правда не до конца, первый том "Мир Лиспа", как то мне не очень понравилось и я забил, так что не советую вам её читать, очень скучно плюс только в том, что про Common Lisp это единственная книга на русском. Потом, когда появилась в инете Practical Common Lisp, и я его прочитал, да ещё открыл для себя SLIME, всё стало намного интереснее. Язык ИМХО самый навороченный из всех, и причина этому именно макросы в сочетании с простым представлением программ и данных. Чего стоят те же макросы loop & format.
Вы тут обсуждали отсутствие стат. типизации, я вспомнил про declare, хотя это не совсем то, но во первых она выдаёт варнинги при компляции, если при вызове указан не тот тип + ускоряет выполнение, не то что вам нужно? Только конечно это не распространяется на стандартные функции. Я написал небольшой макрос, который делает возможным быстрое объявлении функций, с заданным типом аргументов.
(defmacro defun-declare-type (name (&rest params) &body body)
`(defun ,name ,(mapcar #'cadr params)
(declare ,@params)
,@body))
теперь заместо:
(defun test (b a)
(declare (string b) (integer a))
(format t "~a have ~d apples." b a))
я могу писать так:
(defun-declare-type test ((string b) (integer a))
(format t "~a have ~d apples." b a))
И при создании функции, с не правильным типом второго аргумента в вызове test:
(defun-declare-type test2 ((string a) )
(test "ABC" a))
Выдаётся варнинг:
In: DEFUN-DECLARE-TYPE TEST2
; (TEST "AA" A)
; ==>
; A
; Warning: Result is a BASE-STRING, not a (VALUES &OPTIONAL INTEGER &REST T).
;
; Compilation unit finished.
; 1 warning
совсем маленькое улучшение, но оно показывает, как легко в язык добавляются новые конструкции, причём по нажатию М-. или во время отладки я всёравно попадаю на определения этой функции.
Можно привести пример посложнее из PCL, макрос генерирует класс бинарных данных:
(define-binary-class id3v2.3-tag (id3-tag)
((extended-header-size (optional :type 'u4 :if (extended-p flags)))
(extra-flags (optional :type 'u2 :if (extended-p flags)))
(padding-size (optional :type 'u4 :if (extended-p flags)))
(crc (optional :type 'u4 :if (crc-p flags extra-flags)))
(frames (id3-frames :tag-size size :frame-type 'id3v2.3-frame))))
превращается в:
(define-generic-binary-class id3v2.3-tag
(id3-tag)
((extended-header-size
(optional :type 'u4 :if (extended-p flags)))
(extra-flags
(optional :type 'u2 :if (extended-p flags)))
(padding-size
(optional :type 'u4 :if (extended-p flags)))
(crc
(optional :type 'u4 :if (crc-p flags extra-flags)))
(frames
(id3-frames :tag-size size :frame-type 'id3v2.3-frame)))
(defmethod com.gigamonkeys.binary-data::read-object progn
((#:objectvar id3v2.3-tag) #:streamvar)
(declare (ignorable #:streamvar))
(with-slots (identifier major-version revision flags size extended-header-size
extra-flags padding-size crc frames)
#:objectvar
(setf extended-header-size
(read-value 'optional #:streamvar :type 'u4 :if (extended-p flags)))
(setf extra-flags
(read-value 'optional #:streamvar :type 'u2 :if (extended-p flags)))
(setf padding-size
(read-value 'optional #:streamvar :type 'u4 :if (extended-p flags)))
(setf crc
(read-value 'optional #:streamvar :type 'u4 :if (crc-p flags extra-flags)))
(setf frames
(read-value 'id3-frames #:streamvar :tag-size size :frame-type 'id3v2.3-frame)))))
А вот сам код макроса:
(defmacro define-generic-binary-class (name (&rest superclasses) slots read-method)
(with-gensyms (objectvar streamvar)
`(progn
(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (get ',name 'slots) ',(mapcar #'first slots))
(setf (get ',name 'superclasses) ',superclasses))
(defclass ,name ,superclasses
,(mapcar #'slot->defclass-slot slots))
,read-method
(defmethod write-object progn ((,objectvar ,name) ,streamvar)
(declare (ignorable ,streamvar))
(with-slots ,(new-class-all-slots slots superclasses) ,objectvar
,@(mapcar #'(lambda (x) (slot->write-value x streamvar)) slots))))))
(defmacro define-binary-class (name (&rest superclasses) slots)
(with-gensyms (objectvar streamvar)
`(define-generic-binary-class ,name ,superclasses ,slots
(defmethod read-object progn ((,objectvar ,name) ,streamvar)
(declare (ignorable ,streamvar))
(with-slots ,(new-class-all-slots slots superclasses) ,objectvar
,@(mapcar #'(lambda (x) (slot->read-value x streamvar)) slots))))))
(defun slot->defclass-slot (spec)
(let ((name (first spec)))
`(,name :initarg ,(as-keyword name) :accessor ,name)))
(defun new-class-all-slots (slots superclasses)
(nconc (mapcan #'all-slots superclasses) (mapcar #'first slots)))
(defun slot->write-value (spec stream)
(destructuring-bind (name (type &rest args)) (normalize-slot-spec spec)
`(write-value ',type ,stream ,name ,@args)))
(defun slot->read-value (spec stream)
(destructuring-bind (name (type &rest args)) (normalize-slot-spec spec)
`(setf ,name (read-value ',type ,stream ,@args))))
Уже сложнее, но зато получается очень хорошая абстракция, не говоря уже об уменьшении объёма кода.
И именно благодаря столь-удачному подходу к макропрограммированию CL можно ИМХО назвать самым мощным ЯП общего применения.
Ещё мне очень нравиться скорость выполнения, с учётом дин. типизации.
Я как-то переписывал одну вычислительную программку с Python — на CL, так вот при использовании компиляторов CMUCL или SBCL скорость выполнения возросла в 4-5 раз, при примерно одинаковом количестве кода. В CL можно было бы сделать оптимизацию, и тогда бы скорость наверное возросла ещё в несколько раз, но мне было лень.
Ну и конечно REPL + инкрементальная компиляция очень сильно облегчает (можно сказать делает более увлекательным что-ли

) написание и тестирование программ.
Тем кому не нравятся скобки могу ответить, что нужно юзать структурное кодирование, парные скобки вставляются автоматически, и с помощью С-М- комбинаций можно легко передвигаться по сколь-угодно сложным спискам. То есть скобки здесь это огромны плюс.
Вот напримерпример есть функция:
(defun foo (bar baz)
(+ (* baz 2)
(expt bar (* baz 2)^)))
Курсор стоит на место каре, хочу я добавить let к этому выражению,
нажимаю два раза С-M-U(ctrl-alt-u), курсор переноситься на два уровне вверх, т.е. перед скобкой, стоящей перед плюсом. потом нажимаю M-1 ( скобки автоматом добавляются как в начале так и в конце:
(defun foo (bar baz)
((+ (* baz 2)
(expt bar (* baz 2)))))
При таком подходе намного реже требуется пользоваться стрелками, потомучто мы работаем не с последовательностью знаков, а с вложенными списками и лисп-символами, т.е. над более высокими абстракциями. к тому же префикс C-M- для удобства можно заменить просто на C-.
Можно говорить очень много об отдельных мегафичах CL, таких как например CLOS, но это отдельные и очень объёмные темы.
Так что всё-таки всем советую прочитать хотя-бы Practical Common Lisp, чтобы понять все преимущества Лиспа.
Ну и напоследок хочу сказать что мне не нравиться. Так как стандарт создавался почти 20 лет назад, а он основывался на намного более ранних разработках, в нём присутствует достаточное количество небольших несогласованностей, как например порядок аргументов в функциях доступа к n-ому элементу массива и списка, или невнятные названия некоторых стандартных функций, или атавизмов, как ключ &aux в определении функций. Но в принципе это не играет особой роли при разработке.