LINUX.ORG.RU

Почему макросы в стиле лиспа не стали популярными?

 


3

7

В лиспе есть чудесное свойство: код и данные выглядят одинаково. Это позволяет очень легко и естественно писать код, генерирующий другой код. Что называется макросом.

Однако в индустрии данный подход применяется нечасто.

К примеру в С используется отдельный язык, генерирующий текст (препроцессор).

В С++ используется отдельный язык на шаблонах для метапрограммирования.

В Scheme тоже изобрели отдельный язык.

Из похожих подходов я видел только D, в котором можно написать функцию, возвращающую текст. Эту функцию можно вызывать во время компиляции и её результат компилятор тоже откомпилирует. Этот подход похож на лисп, хотя и гораздо менее удобен. Но больше нигде я такого не видел.

Если говорить про не-лисповые языки, то естественным кажется ввести официальный API для AST (по сути там ерунда) и разрешить писать функции, возвращающие этот самый AST. Это будет всё же лучше, чем текст и концептуально более похоже на лисп. Но я такого не видел. Разве что в Java есть annotation processor-ы, но и там такой подход это на уровне хаков скорей.

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

Мне кажется идея генерировать код тем же языком, которым он пишется, на 100% естественной и удачной. Единственное, что я бы тут дополнил - сохранять весь этот сгенерированный код в хорошо отформатированном виде где-то в каталоге с остальными артефактами сборки и предоставлять простую навигацию, чтобы не нужно было гадать, что же там эти макросы нагенерировали. Но это уже мелочи и вопрос инструментария, а не что-то фундаментальное.

Почему же так не делают? Зачем почти в каждом языке изобретают какие-то особые пути для метапрограммирования?

★★★★

естественным кажется ввести официальный API для AST и разрешить писать функции, возвращающие этот самый AST.

Nim.

Почему же так не делают?

Потому что художники видят по-разному.

dataman ★★★★★
()

Однако в индустрии данный подход применяется нечасто.

Потому что если нет гомоиконности, то это не особо интересно. Всё равно ничего умнее тупой замены ты не сделаешь. Тогда зачем городить огород? Гораздо лучше использовать отдельный макроязык заточенный на строки, вместо того чтобы пердолиться с обработкой строк на Си.

no-such-file ★★★★★
()

В питоне так можно бол-мен.

А вообще первое правило работы со строками в с/с++ - не работать со строками в с/с++. Оно там на редкость убогое.

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

AntonI ★★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)

В лиспе есть чудесное свойство: код и данные выглядят одинаково. Это позволяет очень легко и естественно писать код, генерирующий другой код. Что называется макросом.

Потому что моноиконность оказалась не нужна.

Почему же так не делают? Зачем почти в каждом языке изобретают какие-то особые пути для метапрограммирования?

Делают. Смотри Template Haskell, например.

hateyoufeel ★★★★★
()

Если говорить про не-лисповые языки, то естественным кажется ввести официальный API для AST (по сути там ерунда) и разрешить писать функции, возвращающие этот самый AST.

Проблема в том, что в том же русте есть структуры, есть trait и есть impl. И все это должно быть сгенерировано ДО вызова функций. Поэтому там проще сделать отдельный синтаксис для макросов.

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

И все это должно быть сгенерировано ДО вызова функций. Поэтому там проще сделать отдельный синтаксис для макросов.

Да не, никто не мешает сделать API для работы с AST и отдельный лёгкий синтаксис для сплайсинга.

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

Так это оно и есть. Там есть крейты дающие доступ к ast и легкий синтаксис для компоновки этого в код.

Ну короче вот. Ща погуглил, шкалка такое тоже может. В окамле есть metaocaml. В других язычках наверняка тоже.

Короче, ТС не осилил. Реально в подобные штуки только сишка с плюсами не могут, но на них просто всем срать.

hateyoufeel ★★★★★
()
Ответ на: комментарий от ya-betmen

ладно, покажи мне хотя бы пару крупных проектов на лиспе

https://github.com/razum2um/awesome-clojure#awesome-products-in-clojure

где эта фича активно используется

Макросы даже в стандартной библиотеке активно используются, и в сторонних библиотеках периодически можно видеть. В приложениях обычно пореже.

Nervous ★★★★★
()

В лиспе есть чудесное свойство: код и данные выглядят одинаково. Это позволяет очень легко и естественно писать код, генерирующий другой код. Что называется макросом.

Однако в индустрии данный подход применяется нечасто.

К примеру, по рабоче-крестьянски:

в sh применение (вложенных) eval иногда приводит к заметно более компактному коду, чем без eval.

Это разве не промышленное применение?

x22 ★★
()

Почему же так не делают?

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

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

Гомоиконность ну вообще никак не связана с возможностью генерировать код на ЯП кодом на ЯП.

Зато с удобством связана. Структурами данных манипулировать гораздо проще, чем строками — потому что они, ну этсамое, структурированы.

Например, для доступа к элементам списка есть стандартные функции; для доступа к элементам строки (если они крупнее отдельного символа) надо раскочегаривать регулярки.

Или парсить строки в структуры данных и манипулировать уже ими, а потом сериализовать обратно в строки (ничего криминального, лиспы тоже это делают) — но возникает вопрос: зачем лишняя морока с особенным, кудрявым текстовым представлением для кода, если у структур данных уже есть текстовое представление? Почему не переиспользовать его?

Ведь это проще.

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 5)
Ответ на: комментарий от anonymous

Потому что моноиконность оказалась не нужна

Гомоиконность ну вообще никак не связана с возможностью генерировать код на ЯП кодом на ЯП.

В случае лиспа ещё как связана.

hateyoufeel ★★★★★
()

Чтобы сделать макросы «как в лиспе», тебе нужна не только представимость кода в виде простых структур данных, как в лиспе, но и специфическое устройство рантайма языка программирования, как в лиспе - когда у тебя нет «скомпилированных программ» или «скриптов», а есть работающая система, в которую встроен компилятор как отдельная подсистема, и разницы между компиляцией и рантаймом на самом деле особо нет.

Эти два свойства практически гарантированно делают язык диалектом лиспа.

Плюс, реализовать это, тем более в эффективном виде, на самом деле не так просто, как кажется. А большинство современных языков и платформ делаются методом херак херак и в продакшн, «думать некогда, трясти надо», абсолютно на коленке и через жопу(специалистов по компиляторам и прочему вообще мало, а теперь на это наложилась полнейшая деградация даже американского образования в этой отрасли).

И вдобавок к этому, многие языки делаются «для идиотов», потому что у недалеких менеджерков в последние десятилетия существует ничем не подтвержденная идея фикс о том, что 9 женщин могут родить ребенка за месяц и это якобы дешевле.

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

На всяких Rust, Scala или Template Haskell это настолько неудобно и непрактично, что этим никто не занимается, кроме как для каких-то совсем примитивных случаев. Чаще изобретают интерпретаторы XML, YAML и JSON.

lovesan ★★
()
Последнее исправление: lovesan (всего исправлений: 1)
Ответ на: комментарий от nionio35

Tcl в некотором роде похож на лисп, но является непрактичным скриптовым говном. Идея Tcl в целом провалилась, как Столлман и предупреждал.

lovesan ★★
()
Последнее исправление: lovesan (всего исправлений: 1)
Ответ на: комментарий от nionio35

в tcl eval вроде оно.

в tcl это без eval делается. Вообще можно почти всё перемонстрячить, чтобы получился нужный DSL.

и сразу в ответ для TC: именно потому и не приживаются «макросы в стиле лисп».

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

MKuznetsov ★★★★★
()
Последнее исправление: MKuznetsov (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

Нет, с Tcl проблема в структуре - она неудобная в отличие от лиспа

И нет никакой проблемы с «абсолютной свободой действий» нет, точно так же как и с функциями. Потому что это одно и то же практически. Серьезная квалификация, по идее нужна везде, потому что говнокодеры нихера нормального произвести не способны в принципе.

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

весь т.н. «lisp curse» и аргументы похожие на него, это бред собачий, т.к. опровергаются банальном тем, что в symbolics, texas instruments и других похожих компаниях, работали сотни тысяч людей, и ничего им в макросах не мешало. точно впрочем и сегодня с лиспом работает огромная куча народу, и в гугле всяком и так далее.

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

Да, TCL «сила». Сталкивался, когда с Cisco и прочим IVR трахался. Правду про него на лурке написали:

«программируемый язык метапрограммирования, позволяющий запрограммировать все то, что можно напрограммировать на Си, находясь при этом в дугу пьяным.»

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

про голанг забыл

У тебя, наверное, был в детстве велосипед с маленькими колёсиками по бокам, для неопытных велосипедистов? А потом, когда научился держать равновесие, ты начал ездить уже без них?

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

про голанг забыл

У тебя, наверное, был в детстве велосипед с маленькими колёсиками по бокам, для неопытных велосипедистов? А потом, когда научился держать равновесие, ты начал ездить уже без них?

Это было к вопросу о языках, применяемых гуглом, и о том, что не стоит на эту конторку ориентироваться.

hateyoufeel ★★★★★
()

писать код, генерирующий другой код

Что мешает на любом другом ЯП генерить код при помощи print+regexp? У императивщины есть хотя бы то преимущество, что кодогенератор пишется в человеческих абстракциях - это очень дорогого стоит.

yu-boot ★★★★★
()
Последнее исправление: yu-boot (всего исправлений: 1)
Ответ на: комментарий от yu-boot

То, что в лишпе это делается прямо в язычке понятным способом, без препроцессора сбоку. Другое дело, что естественное для list processor оказывается неестественным для всего остального.

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

Пытаться заимплементить это сишным препроцессором несколько странно. Не надо генерить весь код, сгенерите миллиард if'ов любым скриптовым языком для любого другого языка без объявления переменных и прочего суходроча.

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

Не надо генерить весь код, сгенерите миллиард if’ов любым скриптовым языком для любого другого языка без объявления переменных и прочего суходроча.

Проще сразу взять руст и не страдать.

cumvillain
()

Почему же так не делают?

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

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

max_lapshin ★★★★★
()

Но больше нигде я такого не видел.

В Nemerle наверно лучшие макросы для обычного компилируемого статически типизированного языка. Тут https://stdray.livejournal.com/69945.html неплохо описаны. Основные плюшки возможность менять синтаксис, писать макросы практически на том же языке что и остальной код из-за квазицитирования и гигиеничность макросов, что-то лучше сделать для компилируемого языка сложно. В Scala в одно время прямо сдирали «новые макросы» из немерле, но как сейчас не скажу. В rust (процедурные) макросы гораздо слабее немерловских, синтаксис глобально менять не могут, из коробки работают с токенами вместо ast, но там все исправляют библиотеки, они добавляют и ast и квазцитирования, и становятся близкими (кроме изменения синтаксиса) к немерловским.

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

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

Это бред собачий, с учетом сколько крупных систем на диалектах лиспа было написано и существует - от операционных систем(Symbolics Genera) до Emacs. От систем корректировки орбит телескопа хаббл(SPIKE) до графовых баз данных(AllegroGraph). От видеоигр, до собственно компиляторов Common Lisp.

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

eval в питоне ничем не удобнее вызова сишного компилятора через system и последующей загрузки сошки. Эта херня в реальности неудобна, и неюзабельна, во-первых, а во-вторых использование eval в рантайме это огромные security риски.

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

использование eval в рантайме это огромные security риски

На произвольном пользовательском вводе, ты хотел сказать? А то придётся и обычный REPL признать огромным сесурити риском.

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 1)