Как макросы Clojure отличаются от макросов C?

Я новичок в обучении Clojure. Я начал пару месяцев назад. Я пытаюсь изучить макросы.

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

Итак, я разместил question с простым примером относительно этого в StackOverflow. Я получил свои сомнения от ответа.

Это то, что я понял,

  • Макросы не будут оценивать все аргументы, отличные от функций, для оценки тела.
  • Макросы могут быть выборочными в отношении того, что оценивать, а что нет и как преобразовать одну часть кода в другую, используя синтаксис цитаты, unquote и splicing.
  • Затем вычисляется окончательный код, который выходит из макроса.

Итак, мой вопрос: как он отличается от директив препроцессора и макросов, используемых в C? Какая сила макросов Lisp/Clojure дает разработчикам, которые полностью отсутствуют и часто используются широко.

Ответ 1

Некоторые заметные отличия:

  • Макросы
  • Clojure работают с Lisp структурами данных, тогда как макросы C работают с текстом. Эта возможность является следствием того, что Lisps является homoiconic (т.е. Lisp исходный код выражается в виде структур данных Lisp). Гомоконичность не является строго необходимой для эффективной работы макросистемы, но она, безусловно, делает ее намного более удобной и естественной.
  • Вы можете выполнить Clojure макросы во время выполнения, а также время компиляции (например, используя eval)
  • Clojure макросы записаны в Clojure. Контраст с макросами препроцессора C, которые имеют собственный отдельный мини-язык. Это большое преимущество при написании более сложных макросов: вам не нужно мысленно переключаться между разными языками (вам, конечно, все же нужно мысленно различать, какой код вы хотите выполнить при обработке макроса, а какой код вы хотите генерировать в качестве вывода макроса)
  • Clojure макросы Тьюринга завершена - вы можете выполнять произвольное генерирование кода внутри них. Не соответствует стандартным макросам препроцессора C, которые несколько ограничены в своей способности выражать сложную генерацию кода. РЕДАКТИРОВАТЬ. Спасибо Джереми за ссылки на некоторые забавные хаки, благодаря которым препроцессор C может быть принужден к действию в полном завершении. Общий момент все еще стоит: на самом деле это не очень практичные способы написания кода общего назначения.

Возможно, макросы по-прежнему являются отличительной "функцией убийцы" Lisps. За небольшим дополнительным объяснением этой темы стоит прочитать эссе Пола Грэма " What Made Lisp Different"

Ответ 2

Макросы

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

Так как Clojure и Lisps обычно используют S-выражения (которые являются просто связанными списками) и позволяют использовать полный язык во время макрорасширения, вы можете создавать гораздо более сложные и полезные выражения.

Ответ 3

C макросы - это чисто текстовые переписывающие макросы. Там ничего страшного С о них в стороне от того, откуда они пришли, вы можете использовать препроцессор C cpp (1) для любого текста. Следовательно, легко создавать не-C-формы с использованием препроцессора C, и вам часто приходится прыгать через обручи, чтобы делать довольно тривиальные вещи, насколько это касается C. Из-за этого макросы C заполняются pitfalls.

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