LINUX.ORG.RU

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

 


3

7

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

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

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

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

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

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

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

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

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

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

★★★★

Ребята, просьба профи не обижать.
Например lovesan меньше стал публиковать посты.
За пол месяца всего три поста.
По крайней мере в Лисп он профи и у него много хороших постов.
Понимаете, он публикует полезные посты, а над ним всё время подсмеиваются.
ИМХО это очень плохой стиль общения.
Да разве только lovesan стал меньше постить?

Ребята, просьба корректно вести диалоги.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 2)
Ответ на: комментарий от monk

Если кратко - макросы бывают к месту и по делу, типа serde. Но там и применять удобно и композится с остальным кодом отлично.

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

Собственно проблема кодогенерации вообще и макросов в частности именно в этом, как по мне, что у тебя инструмент превращается в непоймичо и это непоймичо надо расковыривать отдельно. А сделать так, чтобы было композабельно и при этом не требовало лезть внутрь сделать могут немногие. Ну и остальное уже не единожды упоминали, что если в лиспах макросинг это нормальный путь, потому что у тебя весь синтаксис это AST жопой к программисту, то в том же расте у тебя есть обычные макросы с наркоманским синтаксисом и proc-macro, которые считай трансформер token stream непоймичего в растовый token stream.

Благо в расте от этого инструмента можно отказаться до тех пор, пока совсем не припрёт.

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

Собственно проблема кодогенерации вообще и макросов в частности именно в этом, как по мне, что у тебя инструмент превращается в непоймичо и это непоймичо надо расковыривать отдельно. А сделать так, чтобы было композабельно и при этом не требовало лезть внутрь сделать могут немногие. Ну и остальное уже не единожды упоминали, что если в лиспах макросинг это нормальный путь, потому что у тебя весь синтаксис это AST жопой к программисту, то в том же расте у тебя есть обычные макросы с наркоманским синтаксисом и proc-macro, которые считай трансформер token stream непоймичего в растовый token stream.

Действительно. Вот и ответ. Макросы в стиле лиспа сложно отлаживать и поддерживать в IDE. Как я уже писал, единственное исключение Racket, но в нём как раз при выборе между макросом и функцией всегда выбирают функцию.

Макросы в стиле лиспа в общем случае сложно писать. По крайней мере, практически везде, кроме самого Common Lisp, есть альтернативный «простой» путь написания макросов (как обычные макросы в расте).

Макросы в стиле лиспа являются протекающей абстракцией, так как используемый в них символы могут иметь не то значение, которое ожидается, в момент применения. Где-то с этим борются вводя гигиену (и заметно усложняя макросы, так как теперь каждый узел тащит контекст), где-то (как в Common Lisp) просто запоминают, где грабли.

Для макроса в стиле лиспа аргументы макроса должны быть корректными синтаксическими деревьями. Это ограничивает возможный синтаксис. И если в лиспе ограничение не так заметно, так как все синтаксические структуры и так имеют вид последовательности элементов в скобках, то для других языков почти всегда невозможно сделать ещё один if. Приходится делать что-то вроде if2(cond(), { do_if_true(); }, { do_if_false(); });, что ужасно некрасиво.

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

каким образом оно там что усложняет и каким образом оно плохо отлаживаемое если это обычные функции, которые ты точно так же можешь в дебаггере отлаживать?

вот насмотрятся какой-то хероты типа раста или ракеты, и потом экстраполируют свой негативный опыт на лисп, хотя в глаза его не видели

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

«Макросы» и в 1С есть.

С начала года в одном из модулей удалил 6000 строк без потери функциональности.

Дело было так.

У нас дирекция любит сводные отчёты.
Это как бы одним махом «видим всё» (да ещё с итогами на каждый день месяца).
Изобрёл «велосипед», а теперь его убрал (использую ранее, разработанные алгоритмы).

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

каким образом оно там что усложняет и каким образом оно плохо отлаживаемое если это обычные функции, которые ты точно так же можешь в дебаггере отлаживать?

У тебя в дебаггере строки исходника нет, если этот исходник сформирован макросом. И стектрейс показывает функции из раскрытия макроса.

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

каким образом оно там что усложняет и каким образом оно плохо отлаживаемое если это обычные функции, которые ты точно так же можешь в дебаггере отлаживать?

А ещё, если брать именно Common Lisp, то ошибку в функции я могу исправить прямо в отладчике, сделав defun с правильным кодом, и продолжить выполнение. А после исправления макроса надо как-то перекомпилировать весь затронутый код.

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

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

Вон, тот же @monk спокойно и профессионально общается по теме Lisp. Если бы @lovesan так же общался, то глядишь и не подсмеивался бы над ним никто.

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

... видеть в нем какой-то авторитет, то это не значит что так считают остальные.

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

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

Всё же ныне век «наскального» программирования.
Разработчики создают компиляторы в которых программист должен обо всём позаботиться самостоятельно.
Много всяческих ограничений в синтаксисе, ...
Радует лишь то, что создано много хороших алгоритмов.
Технологии же разработки проектов ныне - «наскальные».
Технологии создания проектов по существу такие же, как в далёкие шестидесятые.

Можно ли по другому?

Конечно.
Беда в том, что большинство не хотят заниматься решением проблем и ждут готовое.

Всё как обычно!

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 2)
Ответ на: комментарий от monk

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

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

почему макросы это костыль который множит на ноль всю прозрачность отладки.

Он так привык. Я думал, что в 1С очень читабельный и понятный код. Пока не попытался рассказать, что в каком модуле и как устроено программисту, перешедшему с C#. Только тогда я осознал, что функция на несколько тысяч строк не самый лучший способ описать сложный алгоритм.

К тому же в Common Lisp макросы используют достаточно ограниченно. Нет аналога Typed Racket, нет аналога контрактов с сопроводимой информацией. Самое сложное, что могло бы быть на макросах - это CLOS и loop, но они часть языка и имеют поддержку от компилятора. Программисты на Си тоже заявят, что define ничуть не мешает отладке.

Не способен принять другие подходы в разработке

Это тоже можно считать достоинством. Иначе он тоже стал бы эти другие подходы тащить в Common Lisp и делать очередной личный диалект лиспа.

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

Это тоже можно считать достоинством.

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

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

Смотря какую информацию ты от него хочешь получить. Если информацию о том, как правильно в Common Lisp что-либо сделать или о том, чего не хватает в других ЯП относительно лиспа, то это достоинство.

Также, как упорство в вере полезно для священника, несмотря на то, что является недостатком, например, для учёного.

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

Смотря какую информацию ты от него хочешь получить.

От него? Вообще никакую, ничего нового я от него не услышал, он просто транслирует то что прочитал в субреддитах о превосходстве лиспа, не более того.

Если информацию о том, как правильно в Common Lisp что-либо сделать или о том, чего не хватает в других ЯП относительно лиспа, то это достоинство.

Он зачастую рассуждает о вещах в других ЯП о которых понятия не имеет, а не рассказывает о том как что-то правильно сделать в Lisp. Думаю, мы уже слишком много времени уделили его персоне, предлагаю на этом закончить.

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

Кстати, по теме.

Помню, что в лиспе до макросов была идея делать плагины компилятора, а потом (как и с синтаксисом) времянка стала стандартом.

Существуют ли какие-то языки программирования, в которых есть стандартная возможность делать плагины компиляторов?

То есть, чтобы это был не макрос в теле программы, а что-то вроде https://www.itcodar.com/python/can-you-add-new-statements-to-python-s-syntax.html но в нормальном документированном виде и без перекомпилирования всего компилятора?

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

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

Кстати для компиляторов это не сложно и сделать.

Как?

Если компилятор встречает алиас (например if), то он считает, что в исходном тексте if.
Всё!

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

не понимаю зачем спорить с человеком который не понимает вообще нихера из того что говорит

я вот подустал это делать, потому как уже не такой молодой-шутливый как раньше

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

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

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

#define из Си.

Плагин - это не макрос. Это набор кусков кода для компилятора. Например, чтобы можно было дописать систему типов. Или проверку инвариантов.

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

#define из Си.

Точнее а-ля #define, так как ... (да Вы и сами знаете).
Например пусть это будет #definekey.

Другой способ - доработка IDE.

В этом случае IDE проверяет наличие определений типа #definekey и
перед запуском компиляции производит замену алиасов на исходные названия ключевых слов.

Этот способ пригоден для любого компилятора.
Такой способ позволит разработать много «вкусняшек».

Плагин - это не макрос.

Гм., за плагины ничего не говорил.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 5)

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

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

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

А зачем вообще нужны макросы в программе?

  1. указания препроцессору как собирать программу – ок, это в некотором смысле отделено от кода, оно останется, в виде простейших директив include и пр.

  2. писать один раз код для разных типов данных – С++ шаблоны, с контролем типов данных (список типов) или контролем реализованных операций на типах.

  3. вычисления на этапе компиляции – С++ шаблоны, опции компилятора, …

  4. расширение функционала языка, его выразительности, т.е. преждевременное развитие языка, например, ООП с наследованием в С часто в фреймворках реализцется на макросах – в С++ ООП есть…

Какие еще есть области применения макросов?

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

..если макрос может породить другой макрос, и принимая во внимание, что обратное в общем случае неверно - то есть макрос Б порожденный макросом А, не может породить макрос А, получаем семантический порядок на макросах, и таким образом, можно ожидать существование матери-макроса!(ну или царь-макроса), из которого можно породить любой макрос! и автоматически мать-макрос может породить любой мыслимый код.

вот поиском матери-макроса и предлагаю заняться собравшимся.

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

зачем спорить с человеком который не понимает вообще нихера из того что говорит

Надеюсь Вы последовательны и никогда не спорите сами с собой. И даже не общаетесь!

Где энкодер на лиспе?

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

Какие еще есть области применения макросов?

Писать один раз почти повторяющийся код. Когда вместо

public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Мяу!");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public int getWeight() {
       return weight;
   }

   public void setWeight(int weight) {
       this.weight = weight;
   }
}

можно написать

macro eclass Cat {

   private String name;
   private int age;
   private int weight;

   @constructor_accessors;

   public void sayMeow() {
       System.out.println("Мяу!");
   }
}
monk ★★★★★
()
Ответ на: комментарий от monk

Для геттеров и сеттеров кажется есть какие то стандартные фичи в разных ЯП?

А так, конечно, хотелось бы какой то макроязык для таких вещей…

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

Для геттеров и сеттеров кажется есть какие то стандартные фичи в разных ЯП?

Есть, но ненастраиваемые. В Java вообще дошли до того, что повесили функцию макросов на IDE. То есть IDE создает эти самые геттеры/сеттеры и прочий мусор, скрывает его, если реализация не изменённая, позволяет настраивать процесс создания. Но из-за этого хорошая IDE для Java содержит половину компилятора (лексер, парсер, анализ типов, …).

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

зачем вообще нужны макросы в программе

Самое главное забыл, трансформация кода. Условный пример с потолка: макрос debugFor который берёт код и во все циклы вида for i from 1 to 10 вставляет первой строкой print i. Другой пример: берём код и все вызовы fetch('url...') заворачиваем в retry(fetch('url...')). Или заменить все new A на new B. И т.п.

no-such-file ★★★★★
()
Ответ на: комментарий от soomrack

для дебага еще

Дело не в дебаге. Суть в том, что у тебя есть некое декларативная программа на dsl (привет программированию в xml/yaml). Ты берёшь этот код, суешь в макрос и получаешь новую программу с новыми свойствами. Сравнивая с xml, макросы это что-то вроде xslt.

В языках без макросов это всё делается динамически во время работы программы. Т.е. например new A не заменяется в коде на new B. Вместо этого в коде используется фабрика, которая выбирает динамически какой new вызывать.

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

Мне кажется, что этот случай попадает в «преждевременное расширение выразительности языка», т.е. реализация концепций, которые уже состоялись и удобны, но язык в своей семантике еще их не отразил – всякие ООП в си, декораторы, некоторые паттерны и пр.

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

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

преждевременное расширение выразительности языка

Почему же «преждевременное»? Да и невозможно же все на свете запихать в синтаксис языка. Можешь смотреть на макросы как на некие плагины-библиотеки расширяющие синтаксис. По-моему это гениальное в своей элегантности решение: обращаться с синтаксисом так же как с кодом, имея возможность подключать нужные батарейки по необходимости. В т.ч. сторонние, или свои велосипеды.

Вот например «плагин» позволяющий использовать Си синтаксис в лиспе https://github.com/y2q-actionman/with-c-syntax.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Ну потому что это то что нужно сейчас, но в семантически в привязке к правилам языка это пока не отражено.

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

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

поскольку ты не работаешь с сущностями, которые описывает код, ты работаешь с буквами, которые интерпретируются в код

Не очень понял в чём претензия? Что код который передаётся в макрос на настоящий код для языка? Так он настоящий: символы, литералы и т.д. всё как положено. Семантика кода определяется логикой макроса, да, так в этом и мякотка. Язык будет такой, как ты его определяешь. Ещё раз повторю, что макросы это просто способ писать «плагины» к компилятору. Какая разница как это реализуется технически?

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

Ну тут на самом деле вопрос в том, что мы подразумеваем под макросами, как правильно заметил AntonI. Я говорю про макросы в С/C++, которые по факту тупо работа с текстом, без привязки к его сути, которые позволяют делать всякое типа «#define True False».

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

говорю про макросы в С/C++

Тема же про ЛИСП макросы, при чём тут Си? Сишные макросы в наше время нафиг не нужны, т.к. есть inline и constexpr. От препроцессора там давно уже используется только define/ifdef лапша.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Тема про макросы и почему они не в стиле лиспа… Мой посыл в том, что для языка С/С++ макросы это временный костыль, используемые кейсы применения которого постепенно перетекают в синтаксис языка.

Сишные макросы в наше время нафиг не нужны, т.к. есть inline и constexpr. От препроцессора там давно уже используется только define/ifdef лапша.

Ну такое, в ядре и в куче фреймворков макросы Си по-прежнему актуальны.

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

а в лиспах ваших можно любой внешний лисповый код(типа чужой) подвергнуть обработке своим макросом?

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

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

Мало кто знает, что Луговской в своё время ограбил Амазон макросом. После этого его переписали на джаве для повышения безопасности.

vbr ★★★★
() автор топика