LINUX.ORG.RU

[lisp] несколько гадостей

 


1

0

Пора всё же написать про лисп несколько гадостей (уже наболело).

1. По де-факто стандарту определение пространства имён и реализация этого пр-ва имён - это два разных файла. Т.е., как в С, а не как в Паскале. Это неудобно и вообще прошлый век.

Нет операции (with-namespace &body body) - может быть, её и можно сделать, но по-нормальному её можно сделать только через модификацию readtable. Но если две подсистемы меняют readtable и (не дай Бог) поменяют один и тот же символ, то между ними будет проблема и конфликт. Поэтому, для низкоуровневых вещей, которые потом предполагается включить в свой постоянный инструментарий, readtable менять нежелательно. А если импортировать множество разных мелких пр-в имён, то неизбежны конфликты при импорте. То есть, среда не поощряет создания множества мелких пространств имён, а поощряет создание небольшого числа крупных. А это плохо (жду конструктивных возражений).

2. Вообще, убогие пространства имён. Они не могут быть вложенными, как a::b::c. Только "плоские" пространства имён a::b В обычных языках, как правило, класс/структура задают пр-во имён. Собственно, если и есть в ООП какая-то сила, так это в том, что не нужно писать instance.field, а можно просто написать field, если находишься внутри метода. А вовсе не в том, что думают об этом апологеты ООП. В лиспе так написать нельзя (хотя есть конструкция with-slots, но в маленьких функциях она не помогает сократить код).

3. многословность. Сложение двух строк по стандарту (concatenate 'string "as" "df") а также в стандарте некоторым примитивным операциям присвоены имена, подобные destructuring-bind или multiple-value-bind

4. Вместо мойЛюбимыйИдентификатор нужно писать мой-любимый-идентификатор. Вроде мелочь, а места на экране жрётся море. Конечно, никто не заставляет, но есть проблемы с введением идентификаторов, чувствительных к регистру, поскольку в стандарте по умолчанию всё приводится к верхнему регистру и все стандартные символы - в верхнем регистре. То, есть, (print 'мойЛюбимыйИдентификатор) = МОЙЛЮБИМЫЙИДЕНТИФИКАТОР

5. Отсюда - следствие: В обычных языках пишем instance1.field1.field2 В лиспе (в лучшем случае) - (field2-of (field1-of instance1)) 4 лишних символа на каждую ссылку, не считая пробела, который тоже жрёт место на экране. Если брать голый, ничем не подслащённый стандарт, то может оказаться и (slot-value (slot-value instance1 'field1) 'field2)

6. Дебаггер. Вряд ли получится посмотреть переменные в том виде, как они определены в исходнике. Компилятор слишком умный и их соптимизирует. Вряд ли получится поставить брекпойнт на точке в исходнике, по той же причине. Отлаживаться нужно через логи и трейсы. Впрочем, динамичный характер языка делает такую отладку лёгкой.

7. Не хватает множества идиом. Например, есть push (деструктивно добавить элемент в начало списка). Но нет nconcf (добавить к переменной, содержащей список, другой список). Поскольку эти идиомы реально нужны, каждый вводит их по-своему и единый язык распадается на множество диалектов. То же сложение строк никто не делает как (concatenate 'string ...) Я ещё не вполне в культуре, но я не думаю, что есть де-факто стандарт на сокращённую запись этого выражения.

8. CLOS. Да, CLOS велик и могуч, в нём есть мультиметоды. Но в нём есть и такая фишка, как "изменить класс в рантайме". Это круто, но это - накладные расходы.

9. Отсутствует декларативная статическая типизация. Да-да, она самая. На самом деле, информация, содержащаяся в определении типов, очень помогает писать более лаконичный код. На самом деле, возможность (необязательной) статической типизации в лиспе есть, но нет возможности использовать её для сокращения кода.

Конечно, да. Лисп велик и могуч и на нём можно делать то, что нельзя больше делать ни на чём другом. У него есть такие степени свободы, которые продвинутый си-плюс-плюсник просто никогда в жизни не сможет осознать. Но... Обычный язык общего назначения в лиспе нужно тоже делать DSL-ем. В том числе, нужен и DSL для полноценной работы со списками (а уж это - основа лиспа). DSL для удобного определения CLOS-классов. Каждый пишет такой DSL, но нет стандарта де-факто. Я об этом уже писал.

Последнее время я работаю с лиспом более плотно, чем раньше. Я уже начинаю сомневаться в целесообразности темы "лисп как препроцессор для С". Есть и практические к тому иллюстрации. Лисп сообщество не смогло по сей день решить некоторые тривиальные задачи:

1. asdf. Не умеет делать clean (т.е., нет стандарта на эту операцию). Не умеет пересобирать часть систем. Например, если я говорю recompile :force t своей системе, у меня пересобирается вся лисповая часть sbcl. И это - только одна из недоделок asdf. Т.е., при всей крутости лиспа, де-факто стандартное средство сборки гораздо слабее, чем make.

2. средства автодокументирования. Несмотря на то, что в лиспе делать автодокументацию на порядок удобнее, чем в каком-нибудь там С++, я не нашёл средства документированиия, подобного doxygen по мощности. Только не надо меня лечить, что есть describe и т.п. Документация удобна, когда она существует в виде книжечки. Один раз прочитал - и уже знаешь, куда тыркаться. Интерактивная помощь нужна, если что-то забыл, разбираться по ней не всегда удобно (хотя apropos рулит, конечно).

Лисперы, ваши комменты приветствуются. Особенно, в части решения означенных проблем.

Я не ставлю себе цель обосрать лисп. На самом деле, я всё равно хочу писать на лиспе. Просто я давно пытаюсь понять - если он так крут, то почему же он уступил своё первое место мейнстрим-языка высокого уровня какой-то там Яве? Мне кажется, что я привёл здесь причины, основная из которых, если обобщать - лисп неудобен для простых вещей, в которых не нужны макросы. А этих простых вещей всё же больше. Видимо, при разработке следовало сделать более лёгким основной синтаксис. Создание макросов затруднилось бы, но макросов в коде обычно не больше 10-20%, так что общая трудоёмкость могла бы снизиться, а удобочитаемость заведомо повысилась бы.

И, конечно, интересно найти выход из этого положения.

★★★★★
Ответ на: комментарий от den73

> Так, похоже, все отсюда ушли, но я вспомнил ещё одну гадость. Которая ясно показывает...

Если тебе кто-то что-то показывает...

> loop facility... Которая вообще-то не lispy way. Т.е., для задачи общего назначения (итерация) сделали DSL, нарушающий базовые принципы лиспового синтаксиса.

> Другой стандартный оператор цикла do настолько ужасен, что я до сих пор им не могу пользоваться без чтения мануала.

@&ядь, есть же defmacro - напиши свои "правильный" и lispy way, не нарушающий базовые принципы лиспового синтаксиса, который не настолько ужасен - и выложи на paste.lisp.org, а мы обсудим.

Вот косяки в иных пакетах с экспортом/импортом - +10! (вернее то ,что "лёгким движением руки" ваш пакет может стать несовместим с кучей других) Хотя это больше к проблемам "шаловливых ручек". А то что "система позволяет" и "типа тем самым провоцирует" - голову надо на плечах иметь. А кому столько свободы/возможностей не надо - вас кто-то насильно заставляет лиспом пользоваться? Есть же Java.

yyk ★★★★★
()
Ответ на: комментарий от yyk

> Вот косяки в иных пакетах с экспортом/импортом - +10! (вернее то ,что "лёгким движением руки" ваш пакет может стать несовместим с кучей других) Хотя это больше к проблемам "шаловливых ручек". А то что "система позволяет" и "типа тем самым провоцирует" - голову надо на плечах иметь.

ИМХО основная задача компилятора и языка -- предупреждать о возможных проблемах, так что они как раз дублируют функции головы.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> @&ядь, есть же defmacro - напиши свои "правильный" и lispy way, не нарушающий базовые принципы лиспового синтаксиса, который не настолько ужасен - и выложи на paste.lisp.org, а мы обсудим

Слющай, дорогой, а? Язык для чего создан - для того, чтобы на нём программировать какие-то полезные задачи или для того, чтобы спустя полвека развития и 10 (или сколько там - 15) лет после стандарта разрабатывать свои конструкции цикла, поскольку сообщество не смогло за это время разработать достойного варианта?

den73 ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

yyk, Вы предлагаете мне придумать свой оператор цикла и выложить его на какой-то там paste.что-то. Вы смеётесь? Давайте мысленно проиграем эту ситуацию. Допустим, я это сделаю. У меня получится iterate, который по дизайну гораздо лучше, чем loop. Всего лишь нужно его пропатчить, чтобы он не экспортировал for, а понимал его как ключевое слово, в любом пакете, как это делает loop. Но кто примет мои предложения (будь они хоть трижды разумны) в качестве стандарта? Да никто! Никто не откажется ни от loop, ни от своих поделок для частных случаев loop (а таких поделок - море). Если бы это было возможно, то iterate уже занял бы пустующее место и стал стандартом де-факто. Но iterate явно недоработан с точки зрения совместимости с произвольными пакетами. Раз он недоработан, значит за прошедшие годы им особо никто реально не пользовался. И на мою поделку будет реакция "вот ещё один чудак придумал замену loop". Ничего не изменится. Кто чем пользовался, тот тем и будет пользоваться дальше. Что это значит? Что сообщество фрагментировно. Лисп не един, а разбит на множество диалектов, причём, не в DSL, а в самом ядре языка. Это значит, силы не складываются должным образом. Каждый делает свой велосипед. Он не надеется, что на пути ему попадётся магазин запчастей. Он везёт все свои запчасти с собой. Ох, и тяжело ему ехать! Такие велосипедисты в природе не выживают. У Вас, конечно, на всё готов рецепт "не нравится - не пользуйся". Да, когда я еду на велосипеде, я стараюсь взять с собой минимум запчастей. Также я предпочитаю покупать их в магазине, хотя у меня есть собственный токарный станок. Мне кажется, что такое поведеине разумно. И если мне кто-то говорит, что хорошо делать свой велосипед самому и ехать на нём с полным набором запчастей, то у меня возникает некоторое недоверие. den73 (c другой машины)

anonymous
()
Ответ на: комментарий от anonymous

> Хотя это больше к проблемам "шаловливых ручек".

Хм. Вот я сейчас копаюсь в некоем проекте. Там заюзан пакет, к-рый экспортирует символ for. Iterate также экспортирует for. Всё, приехали. Никак не решить конфликт - если проимпортировать for из того пакета, то будут ограничения на iterate. Если проимпортировать из iterate, то софтина поменяет свой смысл и перестанет работать.

Ладно, я могу пропатчить iterate и не экспортировать for.

Но iterate накладывает ограничение: в конструкции (for i in list) for и i должны быть из одного пакета. Отсюда 1. почему я должен знать, в каком пакете живёт for (ведь это ключевое слово) 2. почему я должен писать (for that-package::i in list)? Это - неудобно. 3. А если завтра я не буду импортировать for? Опять менять весь код и убирать that-package:: ?

Можно здесь поподробнее, у кого именно в данном случае шаловливые ручки? ИМХО нужно патчить iterate, чтобы ему было по барабану, из какого пакета у меня for. Но я догадываюсь, что мой патч никто не примет (никому он не нужен). Таким образом, у меня заведётся свой собственный custom iterate, который я должен буду сам чинить. А мне оно надо?

den73

anonymous
()
Ответ на: комментарий от den73

> Этот факт говорит о многом...

Мне это говорит только о том, что язык позволяет такое делать. А людишки - они и есть людишки, всё время ошибаются.

> Другой стандартный оператор цикла do настолько ужасен, что я до сих пор им не могу пользоваться без чтения мануала.

Не используй do. Напиши макрос.

mv ★★★★★
()
Ответ на: комментарий от anonymous

> А мне оно надо?

Ну раз ты знаешь про проблемы в разных решениях, то надо. Если хочешь богатую, стандартизированную, постоянно пересматриваемую и развиваемую библиотеку - пиши на дотнете или джаве :) Только во всём другом говна уже не тележка, а целый железнодорожный состав. И постоянно будешь ловить себя на мысли: "Как, этого нет? А вот в Лиспе есть... "

mv ★★★★★
()
Ответ на: комментарий от den73

> ...поскольку сообщество не смогло за это время разработать достойного варианта?

Вот так вот одним "росчерком пера" обозвал дебилами всю ту прорву народа, которая участвовала в утверждении стандарта... И за одно всех тех, кто с успехом этим самым лупом пользовался/пользуется... Ндас...

Может всё-таки это ты "не в ногу"? Хотя это уже не имеет значения.

yyk ★★★★★
()
Ответ на: комментарий от anonymous

>yyk, Вы предлагаете мне придумать свой оператор цикла и выложить его на какой-то там paste.что-то.

Да.

>Вы смеётесь?

Нет.

>Давайте мысленно проиграем эту ситуацию. Допустим, я это сделаю. У меня получится iterate, который по дизайну гораздо лучше, чем loop. Всего лишь нужно его пропатчить, чтобы он не экспортировал for, а понимал его как ключевое слово, в любом пакете, как это делает loop.

Зачем? По мне что (for что (best-iterate:for - всё едино

>Но кто примет мои предложения (будь они хоть трижды разумны) в качестве стандарта? Да никто!

В качестве стандарта? Конечно нет! В качестве библиотеки? А почему нет если она окажется действительно полезной? Вы посмотрите на более-менее крупные проекты на лиспе: почти все пишут и используют свой tools, причём не редко это не DSL а просто либы "общего назначения" - кому-то каких-то конструкций/функций не хватает. И таки есть clocc, которым таки некоторые пользуются.

>Никто не откажется ни от loop, ни от своих поделок для частных случаев loop (а таких поделок - море).

Естественно, да вам это и не нужно ;)

>Если бы это было возможно, то iterate уже занял бы пустующее место и стал стандартом де-факто. Но iterate явно недоработан с точки зрения совместимости с произвольными пакетами. Раз он недоработан, значит за прошедшие годы им особо никто реально не пользовался. И на мою поделку будет реакция "вот ещё один чудак придумал замену loop". Ничего не изменится.

Так как Вы зачем-то хотите (чтобы все забыли loop и использовали ваш iterate) конечно не будет. Но если вещь получится действительно стоящая - будут использовать и ещё как. Тот-же asdf тоже не идеал, и в стандарт не входит, но "де-факто" стандарт, хотя отдельные личности не ззабывают и cclan.

>Что это значит? Что сообщество фрагментировно. Лисп не един, а разбит на множество диалектов, причём, не в DSL, а в самом ядре языка. Это значит, силы не складываются должным образом. Каждый делает свой велосипед.

Обратная сторона полной свободы. И от этого никуда не деться. Но свободные исходники очень неплохо компенсируют "маргинальность" отдельных личностей, лепящих в свои стоящие проекты собственные велосипеды. Хотя я и в этом не вижу большого вреда кроме тех случаев, когда использование пакета требует его непременного импортирования - вот это и есть зло!

>Он не надеется, что на пути ему попадётся магазин запчастей. Он везёт все свои запчасти с собой. Ох, и тяжело ему ехать! Такие велосипедисты в природе не выживают. У Вас, конечно, на всё готов рецепт "не нравится - не пользуйся". Да, когда я еду на велосипеде, я стараюсь взять с собой минимум запчастей. Также я предпочитаю покупать их в магазине, хотя у меня есть собственный токарный станок. Мне кажется, что такое поведеине разумно. И если мне кто-то говорит, что хорошо делать свой велосипед самому и ехать на нём с полным набором запчастей, то у меня возникает некоторое недоверие. den73 (c другой машины)

А по мне вы хотите всех пересадить на ВАЗ, при этом обещая, что если его всем миром "допилят", то получится лучшая в мире машина - вот этого я и не понимаю.

yyk ★★★★★
()
Ответ на: комментарий от anonymous

>Хм. Вот я сейчас копаюсь в некоем проекте. Там заюзан пакет, к-рый экспортирует символ for. Iterate также экспортирует for. Всё, приехали. Никак не решить конфликт - если проимпортировать for из того пакета, то будут ограничения на iterate. Если проимпортировать из iterate, то софтина поменяет свой смысл и перестанет работать.

пропатчить оба пакета на момент кривизны

>Ладно, я могу пропатчить iterate и не экспортировать for.

>Но iterate накладывает ограничение: в конструкции (for i in list) for и i должны быть из одного пакета.

Кривость лютая. Никак нельзя обойти? :)

>Отсюда 1. почему я должен знать, в каком пакете живёт for (ведь это ключевое слово)

это ключевое слово из текущего пространства имён для функций/макр, если вы его хотите использовать без префикса

> 2. почему я должен писать (for that-package::i in list)? Это - неудобно.

я уже с этим согласился и не понимаю почему не может быть иначе

>Можно здесь поподробнее, у кого именно в данном случае шаловливые ручки?

у авторов iterate

>ИМХО нужно патчить iterate, чтобы ему было по барабану, из какого пакета у меня for. Но я догадываюсь, что мой патч никто не примет (никому он не нужен).

а вот это почему? Вы же не про iterate из стандарта - вы про отдельную либу. Если только этот косяк не сделан намеренно по неизвестным мне причинам и не преподносится как возможность :) В любом случае на форуме можно поднять бучу, особенно если предложить готовое работающее решение.

>Таким образом, у меня заведётся свой собственный custom iterate, который я должен буду сам чинить. А мне оно надо?

Не знаю. Это извечная проблема - пользоваться написанным (и не важно - в стандарте оно или нет, ладно - не особо важно) или писать своё. В каждом случае надо принимать отдельное решение взвешивая все "за" и "против".

yyk ★★★★★
()
Ответ на: комментарий от yyk

> Вот так вот одним "росчерком пера" обозвал дебилами всю ту прорву народа, которая участвовала в утверждении стандарта... И за одно всех тех, кто с успехом этим самым лупом пользовался/пользуется...
Чтобы разговор был более предметен, несколько цитат и ссылок:

> There is no standardized mechanism for users to add extensions to loop.

http://www.lispworks.com/documentation/HyperSpec/Body/06_ai.htm

> Differences Between Iterate and Loop:

> ...

> # iterate's syntax is more Lisp-like than loop's, having a higher density of parens.

> # The current implementation of iterate, unlike the current version of loop (as documented in Common Lisp, 2nd Ed.), is extensible

http://common-lisp.net/project/iterate/doc/Differences-Between-Iterate-and-Lo...

Чтобы не было иллюзий насчёт тривиальности loop. Исходник loop в sbcl - 92 кб. Исходник iterate - 122 кб.

Примеры расширения loop:
В ap5 для итерации по запросам с помощью loop в проект включён полный исходник ansi loop (84 кб) и сделано расширение к нему (3,6 кб)
www.ap5.com

В clsql (4.0.0) для итерации по запросам с помощью loop принято решение, зависящее от реализации лиспа. Для clisp в проект включён пропатченный исходник ansi-loop (107 кб). Плюс к тому, есть файл расширений, развязанный по реализациям. Всего 9кб для всех реализаций. В итоге можно делать запросы типа
> (loop for (forename surname)

> being each tuple in

> [select [first-name] [last-name] :from [employee]

> :order-by [last-name]]

> collect (concatenate 'string forename " " surname))

http://clsql.b9.com/

Также имеется расширение iterate для clsql
Пример расширения iterate - драйвер для запросов cl-sql:
>

> (iter (for (id name) in-clsql-query "select id, name from users" on-database *users-database*)

> (format t "User with ID of ~A has name ~A.~%" id name))

Размер исходного текста - 0,9 кб.
http://common-lisp.net/project/iterate-clsql/

Угадайте, что будет с loop при попытке использовать в одном лисп-образе одновременно ap5 и clsql?

Пожалуйста, не пожалейте времени проглядеть те ссылки, которые я привёл. Тогда мы сможем лучше понимать друг друга. Пока что я Вам пишу о конкретных ситуациях, а Вы мне отвечаете общими соображениями.

> >Отсюда 1. почему я должен знать, в каком пакете живёт for (ведь это >ключевое слово)

>это ключевое слово из текущего пространства имён для функций/макр, >если вы его хотите использовать без префикса

Нет. Я живу в пакете symbolicweb, а for является внешним символом другого пакета am-util. Значит, в пакете symbolicweb вместо

(iter (for ...) ...)

я должен писать так:

(iter (am-util:for ...) ...)

Круто? К сведению, iterate придуман ещё в 1989 году.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Короче, проблему с iterate я худо-бедно решил. Правда, получилось малость через задницу. Избежать конфликта с for я не смог, т.к. for у меня уже был макросом, а iterate раскрывает макросы (и это хорошо). В итоге у меня теперь ifor вместо for и iwith вместо with. Мне повезло в том, что есть такая фича, как iter:defsynonym, с её помощью я и выкрутился. Мне повезло и ещё в одном. Для iter:finally этот defsynonym не работает, но, по счастью, этот символ был ничем не занят и я его просто проимпортировал к себе. Пропатчить iter, чтобы он не закладывался на конкретные символы (а не их имена, как loop) , видимо не так уж и просто.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

>Чтобы разговор был более предметен, несколько цитат и ссылок:

Отлично! Можно я попытаюсь несколько систематизировать вашу информацию? :)

1. Нет стандартного способа расширять loop. - Да, плохо.

2. Есть куча нестандартных, не совместимых, иные реализации и вовсе не имеют. - см. п.1.

3. loop не тривиален и объём реализующего кода огромен - если что-то захочется в нём менять самому - это будет совсем не просто. - Да, и с учётом п.1. не уверен что надо это делать.

4. Библиотека iterate ключи расширений (clauses & drivers) привязывает к пространству имён где их объявляли. - Косяк реализации (имхо)

Какие выводы? У каждого свои :)

Мои (понятно - имхо):

- на расширение loop-а "забить" пока не "засветит" хоть какая надежда расширения стандарта. В CLSQL проделана огромная работа, но, по мне, овчинка выделки не стоит;

- iterate на много компактнее и проще - допилить на момент "отвязки" ключей от места объявления новых правил и пропихнуть в библиотеку - гораздо более реальная задача.

yyk ★★★★★
()
Ответ на: комментарий от den73

> Пропатчить iter, чтобы он не закладывался на конкретные символы (а не их имена, как loop) , видимо не так уж и просто.

Думаю не так уж и сложно.

yyk ★★★★★
()
Ответ на: комментарий от yyk

Думаю, что нового стандарта лиспа ждать в обозримом будущем не стОит. Ну в общем, там в комментариях что-то пишут про то, что символы как-то используются для индексирования чего-то там. Наверное, можно. Но, в общем, синонимы решают проблему (не оттого ли они и возникли?). В любом случае, без синонимов мою проблему с for я бы не мог решить. Потому что в моём пакете for - это макрос, а пакет у меня общий с другим разработчиком. Если запретить в iterate макросы, то это плохо (половина лисповости пропадает сразу). Если затенить for, то перестанет работать остальной код в пакете. Разве только если сделать, чтобы можно было писать (:for ...)

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

Впрочем, остальные проблемы лиспа от этого никуда не делись...

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

> ...Разве только если сделать, чтобы можно было писать (:for ...)... С другой стороны, привязка к пакетам может быть рассмотрена даже как благо, т.к. два разных расширения легко могут определить одно и то же ключевое слово, каждая в своём пакете, и конфликта у них между собой не будет.

Угу, для использования ключей вне области определения пришлось бы присобачивать "типизированный pattern matching" или что-то с аналогичной функциональностью...

Вспомнил что мне не нравилось: фиксация количества аргументов у дженериков (причём количество анализируемых у меня в памяти ограничено двумя, но не могу поручиться)

Или ты об этом писал? =)

yyk ★★★★★
()
Ответ на: комментарий от mv

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

Некорректный способ ведения дискуссии.

www_linux_org_ru ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.