Засланый казачок в семейство Lisp
Засланый казачок в семейство Lisp систем или откуда взялся Clojure
Недавно в конференции lisp@conference.jabber.ru зашла речь о противопоставлении Clojure Common Lisp 'у.
С моей стороны, было сомнение в определении в Common Lisp синтаксического сахара для быстрой записи
структур данных (а точнее - короткой записи), например таких как Хэш-таблицы. Один из аргументов был
следующий: избытычный синтаксис усложняет использование макросов. Но как известно "хорошая мысля
приходит опосля" :) Так что есть одно "НО"! Необязательно сокращённую запись структур данных делать
такую кривую как в Clojure, можно сделать вполне в духе не нарушая дизайна, а именно:
;;; Синтаксис для хэш-таблиц
(defun disp-maps (stream char arg)
(declare (ignore char arg))
(princ (cons 'hashmap (read stream nil))))
(defun hashmap (&rest keys-and-vals)
(let ((hash-table (make-hash-table)))
(loop for (key value . rest) on keys-and-vals by #'cddr
do (setf (gethash key hash-table) value)
finally (return hash-table))))
(set-dispatch-macro-character #\# #\M #'disp-maps)
;;; Проверка
(setq ht #M(:key1 'val1 :key2 'val2))
(maphash #'(lambda (k v) (print (list k v))) ht)
Вот только неизбежное зло: надо помнить что #M по сути превращается в (hashmap ...)
В остальном всё вполне в духе Common Lisp. Плиз покритикуйте сие творчество... Кстати, может лучше
#H ?
Недавно в конференции lisp@conference.jabber.ru зашла речь о противопоставлении Clojure Common Lisp 'у.
С моей стороны, было сомнение в определении в Common Lisp синтаксического сахара для быстрой записи
структур данных (а точнее - короткой записи), например таких как Хэш-таблицы. Один из аргументов был
следующий: избытычный синтаксис усложняет использование макросов. Но как известно "хорошая мысля
приходит опосля" :) Так что есть одно "НО"! Необязательно сокращённую запись структур данных делать
такую кривую как в Clojure, можно сделать вполне в духе не нарушая дизайна, а именно:
;;; Синтаксис для хэш-таблиц
(defun disp-maps (stream char arg)
(declare (ignore char arg))
(princ (cons 'hashmap (read stream nil))))
(defun hashmap (&rest keys-and-vals)
(let ((hash-table (make-hash-table)))
(loop for (key value . rest) on keys-and-vals by #'cddr
do (setf (gethash key hash-table) value)
finally (return hash-table))))
(set-dispatch-macro-character #\# #\M #'disp-maps)
;;; Проверка
(setq ht #M(:key1 'val1 :key2 'val2))
(maphash #'(lambda (k v) (print (list k v))) ht)
Вот только неизбежное зло: надо помнить что #M по сути превращается в (hashmap ...)
В остальном всё вполне в духе Common Lisp. Плиз покритикуйте сие творчество... Кстати, может лучше
#H ?
[#]
На самом деле, это иллизия, что буквальная запись стурктур данных усложняет жизнь макросам. Ведь есть же в CL #() для векторов и ничего, как-то живем :)
Также ошибочно считать, что в CL нет буквальной записи. Ее нет только из коробки, но разве это проблема:
http://github.com/vseloved/rutils/blob/master/hash-table.lisp#L9a
А вот в Clojure, да, нет возможности сделать что-то подобное... :)
Также ошибочно считать, что в CL нет буквальной записи. Ее нет только из коробки, но разве это проблема:
http://github.com/vseloved/rutils/blob/master/hash-table.lisp#L9a
А вот в Clojure, да, нет возможности сделать что-то подобное... :)
[#] Ответ на комментарий от vseloved 13.07.2010 14:25
Пример metabang-bind показывает, что литералы объект бывают полезны - там можно делать деструктурирование массивов.
Проблемы буквальной записи объектов в том, что могут быть проблемы с сохранением их в fasl. Из плюсов же: константность (можно использовать load-time-value, но с литералом проще), возможность анализировать литералы в макросах.
Насчет иллюзии про сложности с макросами согласен. Макросы усложняются не сложной записью объектов, а сложной записью выражений, связанных с передачей управления. Для макроса же просто будет объект.
Проблемы буквальной записи объектов в том, что могут быть проблемы с сохранением их в fasl. Из плюсов же: константность (можно использовать load-time-value, но с литералом проще), возможность анализировать литералы в макросах.
Насчет иллюзии про сложности с макросами согласен. Макросы усложняются не сложной записью объектов, а сложной записью выражений, связанных с передачей управления. Для макроса же просто будет объект.
[#]
Мне тоже пришлось сделать подобную процедуру чтения для #<FUNCTION NAME> и #<PACKAGE NAME>, а то не получается восстановить эти объекты после их записи в строки. Ещё может быть полезно #S() для множеств (раз уж в стандарте есть #(), #2A(), ... и есть #H() в библиотеках).
Что касается макросов при нерегулярном синтаксисе - я тоже не вижу тут проблем, если, например, такая нерегулярная запись
одназначным образом преобразуется в регулярную, то и с использованием макросов на таком коде не возникнет проблем.
Что касается макросов при нерегулярном синтаксисе - я тоже не вижу тут проблем, если, например, такая нерегулярная запись
for i = 1 .. 10 {
print 10 + i;
}
одназначным образом преобразуется в регулярную, то и с использованием макросов на таком коде не возникнет проблем.
[#] Ответ на комментарий от vseloved 13.07.2010 14:25
Опасность в том, что наблюдается отход от единообразия.
Вот к примеру, запись (vector 3 4) против записи #(3 4). Читабельность против сокращенной записи.
Если кол-во синтаксического сахара для записи разнообразных типов/структур будет увеличиваться, то
лисп-сообщество ещё больше будет походить на сектантство. Я полагаю надо выдерживать генеральную
линию: "Единство кода и данных". (vector 3 4) это и код и данные. И я вижу что как всегда и везде могу
применить ф-ию apply с vector или разобрать этот список для какого либо анализа или генерации кода (врочем разбор массива в bind это конечно контраргумент). Но если проводить усовершенствования в стиле
#{:key1 'val1 :key2 'val2} это в итоге перерастёт в нагромождение разнообразного синтаксиса. Lisp-way это если
записать так: (mk-hash :key1 'val1 :key2 'val2) - к примеру. Вполне вписывается в дизайн языка и не привносит
никаких ненужных нюансов.
И всё же отдельной, так сказать, концепцией - можно ввести (а точнее следовать уже введённому) способ
сокращённой записи разнообразных структур через #, второй символ соотв. типу и скобок, например #H()
Но тогда, надо вести учёт соответствия символов в сокращённом синтаксисе и символов функций/макросов.
И это довольно элементарно, ну будет некая системная табличка:
---------------------------
H | MK-HASH
---------------------------
... | ...
---------------------------
Используя эту табличку я смогу спокойно манипулировать данными введёнными посредством сокращённой
записи.
Вот к примеру, запись (vector 3 4) против записи #(3 4). Читабельность против сокращенной записи.
Если кол-во синтаксического сахара для записи разнообразных типов/структур будет увеличиваться, то
лисп-сообщество ещё больше будет походить на сектантство. Я полагаю надо выдерживать генеральную
линию: "Единство кода и данных". (vector 3 4) это и код и данные. И я вижу что как всегда и везде могу
применить ф-ию apply с vector или разобрать этот список для какого либо анализа или генерации кода (врочем разбор массива в bind это конечно контраргумент). Но если проводить усовершенствования в стиле
#{:key1 'val1 :key2 'val2} это в итоге перерастёт в нагромождение разнообразного синтаксиса. Lisp-way это если
записать так: (mk-hash :key1 'val1 :key2 'val2) - к примеру. Вполне вписывается в дизайн языка и не привносит
никаких ненужных нюансов.
И всё же отдельной, так сказать, концепцией - можно ввести (а точнее следовать уже введённому) способ
сокращённой записи разнообразных структур через #, второй символ соотв. типу и скобок, например #H()
Но тогда, надо вести учёт соответствия символов в сокращённом синтаксисе и символов функций/макросов.
И это довольно элементарно, ну будет некая системная табличка:
---------------------------
H | MK-HASH
---------------------------
... | ...
---------------------------
Используя эту табличку я смогу спокойно манипулировать данными введёнными посредством сокращённой
записи.
[#] Ответ на комментарий от LinkFly 14.07.2010 17:22
Я думаю, что "сахар" это хорошо и он позволит привлечь больше PHP-программистов. А "единство кода и данных" это не необходимость, а просто такая возможность, которая может быть полезной в некоторых случаях.
[#] Ответ на комментарий от LinkFly 14.07.2010 17:22
Да ну, какое сектанство? Common Lisp сообщество всегда было самое прагматичное. А наиболее прагматичный вариант — это дать возможность программисту самому решать и предоставить разумный вариант по умолчанию.
На счет страха, что будет зоопарк: на самом деле, таких особых структур данных, которые требуют спец записи — раз, два и обчелся. Векторы, хеши, возможно, множества, деревья (хотя те в общем-то во многих случаях легко выражаются через списки), регексы (то, что и в других языках, в общем). А всё остальное — специфично. Тем не менне, для своих нужд тоже можно сделать какую-то спец. нотацию (для дат там, например, IP-адресов или как в http://www.weitz.de/cl-interpol/). Всегда должен быть выбор, так что make-hash тоже разумно иметь как альтернативу.
На счет страха, что будет зоопарк: на самом деле, таких особых структур данных, которые требуют спец записи — раз, два и обчелся. Векторы, хеши, возможно, множества, деревья (хотя те в общем-то во многих случаях легко выражаются через списки), регексы (то, что и в других языках, в общем). А всё остальное — специфично. Тем не менне, для своих нужд тоже можно сделать какую-то спец. нотацию (для дат там, например, IP-адресов или как в http://www.weitz.de/cl-interpol/). Всегда должен быть выбор, так что make-hash тоже разумно иметь как альтернативу.