В чём? В том, что на лиспе код пишется быстрей, чем на C и C++?
пилшите, Шура, пилшите - рабочий код (не абстрактный, а на github или даже в репах) лучший аргумент, гораздо лучше однообразных рассказов от одних и тех же персонажей о крутости лиспа из года в год
А какие языки «ненормальны»? Для разных задач нужно разное. Я и С++ использую, например. Плохой язык? А на мой взгляд - хороший. Просто для некоторых задач есть более подходящие инструменты.
Он такой же, в принципе. Но в сочетании с маргинальностью смотрится гораздо хуже.
Ты не понимаешь, лисп предназначен для другого. Лисп — для того, чтобы можно было с бородатыми корешами потереть за анафорические лямбды, поглумиться, какие все быдлокодеры, почувствовать себя илиткой.
И с чувством глубокого удовлетворения идти домой, где уже ждёт тарелочка аппетитного мамкиного борща.
Ну так я в Java подсуну свой classloader - изменится поведение оператора new.
Это изменение рантайма. Рантайм в CL не стандартизован, но всё равно примечательно, как в компилятор SBCL добавили поддержку SSE2: загружалась библиотека, определявшая новые VOP'ы и трансформации.
Навешу аннотацию - изменится поведение сеттера.
Стандартная часть языка (привет CLOS, кстати).
Переопределю методы интерфейса Iterator - изменится поведение конструкции for. И?
А можно переопределить поведение class? Или вот пусть тот же new, но чтобы он к classloader'у не ходил?
Не, на clojure посматриваю. Но сейчас я достаточно, в принципе, активно начал применять scala(хотя еще в процессе изучения), не знаю, будет ли смысл использовать clojure. Ради STM? Насколько там это полноценно и круто сделано? В scala вроде мусолится вопрос по включению библиотеки с реализацией STM в стандартную.
Или ради макросов? Так в scala тоже ввели недавно(пока не изучал подробно).
пилшите, Шура, пилшите - рабочий код (не абстрактный, а на github или даже в репах) лучший аргумент, гораздо лучше однообразных рассказов от одних и тех же персонажей о крутости лиспа из года в год
Мне в личное время неохота уже ничего писать. Программирование - это банально и скучно, не то, что двигатель там какой вскрыть.
У traited наследников они могут быть разные — дерево как данные одно, его наследники подмешивают разные конструкторы копирования в пределах одного параметрического (шаблонного) типа и не добавляют никаких данных, соответственно, поднимая в них тип обычного дерева можно получать разное копирование, также можно свободно опускать тип обратно.
struct Tree { обычное дерево };
template <class CopyStrategy> struct TreeWithSomeCopy {}; // параметрический тип ограничивающий наследников
template <> TreeWithSomeCopy<Shallow> : public Tree { тут обычный конструктор копирования };
template <> TreeWithSomeCopy<Deep> : public Tree { тут глубокий };
// если теперь есть
template <class CopyStrategy> void f(TreeWithSomeCopy<CopyStrategy> tree) { можно использовать tree как обычное Tree }
Tree tr = ...; // и какое-то обычное дерево
f(static_cast<TreeWithSomeCopy<Shallow>&>(tr)); // то тут в f скопируется поверхностно
f(static_cast<TreeWithSomeCopy<Deep>&>(tr)); // а тут -- полностью
Хочешь разные - делай методы
Я так и сказал выше, что «проще сделать нормальные методы и выбор в месте вызова», то был пример про стратегии.
struct Tree {
...
Tree clone_shallow(); // RVO
Tree *clone_deep(); // new
};
явно проще, просто ТС хочет передачу с (произвольным?) копирование в функцию без явного вызова (какого-либо) copy, как в С++ для не ссылок и указателей.
Ну и стратегии могут быть более расширяемыми (можно дописать ещё наследников в рамках TreeWithSomeCopy).
там хоть что-то стоящее есть? имеется ввиду конечный продукт, навроде nginx, firefox, blender, postgre, libreoffice etc., или практически все - лисп ради лиспа?
Не, на clojure посматриваю. Но сейчас я достаточно, в принципе, активно начал применять scala(хотя еще в процессе изучения), не знаю, будет ли смысл использовать clojure. Ради STM?
Лиспы выбирают ради скобок, макросов и динамической типизации. STM в той же скале должно быть, хотя бы в виде библиотеки.
Насколько там это полноценно и круто сделано?
Не знаю, слабо связан с java-миром, каких-то отдельных замеров не проводил. Думаю, сейчас это эталонная реализация из существующих, могу ошибаться.
Или ради макросов? Так в scala тоже ввели недавно(пока не изучал подробно).
Макросы в языках с духовн^W богатым синтаксисом хоть и возможны (scala, nemerle итд), но *на_практике* вставляют значительно больше палок в колеса, чем языки с однородным и простеньким синтаксисом (почти отсутствующим - этисамые лиспы). Если бы я программил под jvm, упаси-бох, то склонялся бы к использованию именно скалы, хотя со стороны она выглядит монстриком: java (ооп, синтаксис) + haskell (структуры данных, функциональщина) + lisp (макросы) + perl (избыточный синтаксис, всякие встроенные в язык парсилки xml и тому подобное). Симпатичный такой... монстр :)
На каком основании делается такой вывод? Большинство приличных библиотек написаны исходя из реальных потребностей. Когда пишут в стиле «лисп ради лиспа» получается обычно фигня (это справедливо и для других языков). Там есть несколько форков Maxima. Есть StumpWM.
Какие есть на github сопоставимые (с приведённым списком) продукты на node.js?
а причем тут node.js? типа еще менее нужная вещь? не буду спорить, а вот в doom3, например, я вполне себе играл, он есть на github, diaspora там есть, если говорить про web, redis, linux (ядро), textmate и многие другие проекты, которые готовы к использованию
Ну и как писал выше, про пролог(если бы он совсем не скатился - я писал еще в конце 90х кажется или не сильно позднее, на 5ом vip'е, если понимаешь, о чем я). Нет у лиспа преимуществ перед мейнстримом.
Пролог (facts, rules, unification, backward chaining) успешно встраивается в Common Lisp. Это есть в основных коммерческих реализациях LispWorks и Allegro CL. Встраиваемый пролог - одна из продающихся фишек LispWorks (у него их несколько наряду с CAPI - библиотекой GUI и графикой).
Вполне работающий компилятор с минимумом примитивов можно бесплатно скачать из сети (исходники к PAIP). Примитивы можно определять на самом лиспе. Весь компилятор занимает всего 15 килобайт на лиспе. Он уступает коммерческим прологам в скорости, но на обычных правилах (когда их мало) уступает не намного, и достаточно эффективен.
Только меняется синтаксис на более лисповский, но у пролога и не было единого синтаксиса (например, на ямахе был какой-то свой необычный пролог, были и другие непохожие).
Объективно, надо сравнивать с python, ruby, scala...
Могу описать свой небольшой опыт и сравнить разработку на Scala и Common Lisp.
В целом скорость разработки примерно одинаковая. Код на Scala короче, но у него и выше смысловая нагрузка, т.е. его чуточку тяжелее читать, с ним немного тяжелее работать. Код на лиспе многословнее (из-за динамической типизации), но от силы в раза 1,5 - 2 по сравнению со Scala. Концептуальная нагрузка на код примерно такая же.
Макросы могут изменить ситуацию. Если есть место для rule-based programming (паттерн-матчинг какой, но необязательно в смысле Scala, или какие декларативные правила, которые надо скомпилировать в развесистую лапшу кода), то код на лиспе может стать короче и лаконичнее, чем на Scala.
Код на Scala обычно легко переписывается в эквивалентный код на лиспе. Например, линеаризация списка наследования появилась в лиспе задолго до Scala. Это не изобретение Мартина Одерского. Тут особых проблем нет.
Вот и всё. Для любого объекта созданного через defclass работает ровно как в C++. Для структур надо будет доопределять gen-copy (или сделать defstruct+ которые будет делать это автоматически).
Можно добавить глубокое копирование как часть стратегии.
Можно. Затем и есть аргумент strategy. (defmethod gen-copy ((obj standard-object) (strategy :deep)) ...)
Но оно реже требуется и может иметь побочные эффекты (надо отслеживать кольцевые зависимости и при неудачном стечении обстоятельств можно копируя элемент из контейнера получить копию всего контейнера (по полю parent).
P.S. Табами пользуешься?
Пользуюсь. Но в лоровском текстовом поле они не работают, а этот код я писал сразу в нём.
А если напишешь (class eas), то ещё и все поля экспортируются.
Сложный путь: можно написать экспортилку всего, что является дженериком и имеет первый параметр нужного типа через cl:do-symbols и анализ каждого символа в пакете.
Если экспорты будут разбросаны по коду, то это затруднит его чтение и понимание.
Вообще-то в чём проблема сделать
(let (l)
(do-external-symbols (i package-name)
(push i l))
(sort l (lambda (x y)
(string< (symbol-name x) (symbol-name y)))))
?
И получишь сразу весь список внешних символов. А когда у тебя в тексте стоит (external 'foo) (defun foo ...) хотя бы сразу видно, что этот символ внешний, а не надо рядом в defpackage его искать.
Да, тут надо подумать. Тогда специальные атрибуты для слотов, которые бы говорили, как копировать? Но это вводить свой метакласс, хотя почему бы и нет?
Но это вводить свой метакласс, хотя почему бы и нет?
Вот-вот. В 95% случаев достаточно shallow-copy как в C++. А для остальных 5% проще писать стратегии :list :tree, :contained, :recurse-contained, :container, :deep .... и.т.д
К слову, проблема кольцевых ссылок не фатальна: в cl-store, например, просто создается хэш (старый->новый) куда по мере копирования заносятся объекты.
Для структур надо будет доопределять gen-copy (или сделать defstruct+ которые будет делать это автоматически).
Намекну немножко:
(defstruct foo x y z)
; FOO
(class-of (find-class 'foo))
; #<STANDARD-CLASS STRUCTURE-CLASS>
(class-slots (find-class 'foo))
; (#<STRUCTURE-EFFECTIVE-SLOT-DEFINITION for instance slot X #x302000E3143D> #<STRUCTURE-EFFECTIVE-SLOT-DEFINITION for instance slot Y #x302000E30EBD> #<STRUCTURE-EFFECTIVE-SLOT-DEFINITION for instance slot Z #x302000E30DED>)
Что именно? class-slots в стандарте нет, но она есть, вероятно, во всех реализациях, однако copy-class-slots из твоего примера в HyperSpec тоже не упоминается. А соотношение классов и типов (в т.ч. структур) упоминается: http://www.lispworks.com/documentation/HyperSpec/Body/04_cg.htm
Он и в С++ встраивается. Не переставая от этого быть прологом.
Не видел, но предполагаю, что нет той степени интеграции, что существует между лиспом и прологом.
Прологовское правило - это обычный список лиспа (s-exp). Такие правила можно порождать макросами, например, при преобразовании грамматических правил в прологовские. Результатом унификации тоже являются обычные s-exp, представляющие термы, что удобно для обработки результатов. Более того, примитивы пролога можно задавать напрямую в виде обычных функций самого лиспа (есть соглашение о наименовании), вызывая унификацию и движок самого пролога вручную по мере необходимости. Ввод нового правила - это по сути всего одна форма лиспа (читай, всего один statement в Си++). Запрос - другая форма. Все коротко и ясно.
Конечно, хорошо, что пролог тоже можно встраивать в Си++.
Ну мне попадались и подобные реализации(сейчас не найду). Я вообще не понимаю, в чем ты видишь преимущества лиспа по встраиванию пролога? Я знаю ваш лисп и мне до сих пор не понятно, с чего адепты лиспа кричат о каких-то принципиальных преимуществ. Обычный язык.
для функционального стиля глубоко пофигу, по ссылке оно, или по значению.
Если бы было по значению, то в функциональном стиле работало бы очень медленно (вызов функции = копирование, общие данные делать нельзя, ужас в общем).
Вот как раз с явой у меня не сложилось. Три раза качал rpm с сайта Oracle, они не завелись. Ладно. Пытался запустить Эклипс, он грузился 40 минут на ноутбуке. Я не дождался и снес его нафик.