LINUX.ORG.RU

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

 


3

7

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

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

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

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

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

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

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

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

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

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

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

Касательно «опиши задачу».

Задача:

  1. сделать макрос, который опередяет енумы, вида.
(define-enum ENUM-NAME
  ITEM1
  ITEM2
  ...)
  1. Написать функцию enum-value, которая берет два символа - имя енума, и имя итема, и возвращает числовое значение. Усложнение задачи - написать МАКРОС, enum, принимающий два символа, не вычисляющий их, и вычисляющийся при компиляции в собственно число-значение. В CL кстати также можно написать compiler macro, для таких случаев, что обычно и делается, в том же CFFI итд.

  2. Написать макрос foo, который во время компиляции печатает на стандартный вывод значение итема foo из енума my-enum.

  3. Подряд вызывать define-enum определяющий этот my-enum, и foo, но каждый раз, чтобы my-enum имел foo разным значением. Вывести все так чтобы показывалось как у меня в примере - 0 и 1. Это всё должно происходить при компиляции, последовательно.

А, и да, все это в одном файле.

Нет, сайд-эффекты в САМИХ макросах делать нельзя(исключение тут - собственно для печати, ну это для примера). Всмысле ну как. Что в CL, что в Clojure на самом деле, нельзя сказать, сколько раз вызовется макрос, поэтому так никто не делает, это говнокод. Есть хаки с compiler-let из cltl2, которые дают некоторые гарантии, но в целом - нет.

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

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

Зачем делается переопределение? Ну часто, оно делается для бутстрапа чего-либо, какой-то библиотеки скажем, или даже компилятора, типа SBCL.

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

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

CL-USER> (setf (macro-function 'foo)
               (lambda (whole env)
                 (declare (ignore env))
                 (destructuring-bind (head x op y) whole
                   (declare (ignore head))
                   (list op x y))))
; #<FUNCTION (LAMBDA (WHOLE ENV)) {24318B4B}>
CL-USER> (foo 1 + 2)
3
lovesan ★★
()
Ответ на: комментарий от lovesan

Задача: сделать макрос

Охренительная задача, я тебе скажу. Прям вот просыпаешься, бывало, утром, и думаешь: чего бы я сейчас больше всего хотел? Написать макрос, конечно! Зачем? Для чего? Что за глупые вопросы, все хотят писать макрос.

сайд-эффекты в САМИХ макросах делать нельзя

надо сделать как в CL

Красавчик, чо — даже не пытаешься спрятать подвох %)

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

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

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

А если не видно разницы, то зачем платить больше? %)

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

Без сайд эффектов в макросах это как сделать в Clojure?

Ну, к примеру, так

(ns dev.enums)

(def enums (atom {}))

(defn add-enum [name & items]
  (swap! enums assoc name (vec items)))

(defmacro defenum [name & items]
  `(apply add-enum '~name '~items))

(defn enum-value-index [enum-name enum-value]
  (if-let [enum (get @enums enum-name)]
    (.indexOf enum enum-value)
    -1))
;; (re)defining enums and retrieving values

(defenum my-enum one two three)
(println (enum-value-index 'my-enum 'one))

(defenum my-enum two three one)
(println (enum-value-index 'my-enum 'one))

;; no such enum
(println (enum-value-index 'no-such-enum 'one))
;; no such value
(println (enum-value-index 'my-enum 'no-such-value))

@enums
0
2
-1
-1

Драгоценного всёвавремякампиляцыи, там, наверное, нет — но работать будет, и работать будет одинаково, AOT или не AOT. Я думаю, этого достаточно %) А по краткости и понятности — даже лучше.

Можно и вообще без макросов, ага. Просто придётся больше имён квотить.

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

знание матчасти не роскошь

Ну как сказать. Матчасти в мире превеликое множество, а голова-то одна. Надо как-то отделять релевантную матчасть от нерелевантной. Релевантную приоретизировать.

И вот тут особенности реализации SBCL прям как-то не врываются в (мои личные) чарты. Даже если они в каком-то там стандарте упоминаются.

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

То что ты написал это вообще нерелеватно. Это вообще не про то. Ты так и не понял смысла.

Макрология без compile-time сайд-эффектов, и взаимодействий друг между макросами это всего лишь +- синтаксическая подстановка типа как в сишечке, а не полноценное метапрограммирование, и глядя на такие макросы многие люди не в теме, действительно думаю «а, да макросы это все ненужно». Отсюда и идут бредни про «Clojure это лисп» или тем более про eval в python каком-нибудь.

EVAL-WHEN это фундаментальная фишка для метапрограммирования. Он как раз для сайд эффектов и нужен, потому что в самих макросах сайд-эффекты как я уже сказал, это не комильфо. А с помощью eval-when мы модифицируем среду компиляции, таким образом позволяя макросам общаться друг с другом. У меня в библиотеке, в частности, это используется для бутстрапа интеропа с дотнетом.

Вот пример: https://github.com/Lovesan/bike/blob/master/src/api-known.lisp

Тут всякие определения функций из дотнета. Которые генерируют в compile-time функции-обертки в лиспе. Которые уже потом, используются уже дальше в процессе бутстрапа библиотеки при ее компиляции. Вопрос - откуда макросы знают как и что генерировать? Да потому, что во время компиляции до этого из дотнета были вытащены какие-либо типы данных, загружен рантайм дотнета, и так далее. Еще во время компиляции, да. И таких примеров куча. В лиспе это используется в хвост и гриву.

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

А потом на форумах начинают п*здеть о том что якобы макросы не нужны, и вообще это баг а не фича. Воинствующее невежество. А, ну и еще да, в Clojure вся эта байда действительно неотлаживаемая, в отличие от CL, потому что нормального рантайма лиспа и лиспового процесса вычисления Clojure не имеет. Хотя даже на Java его сделать было +- можно(см. ABCL), но Рич Хикки «чукча не читатель», и весь опыт построения лиспов и лиспо-подобных языков в мусорку выкинул, зато хайпа развел, и все эти кложурщики ему уподобляются.

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

То что ты написал это вообще нерелеватно. Это вообще не про то. Ты так и не понял смысла.

Енумы определяются-переопределяются, значения ищутся, всё работает. То, что оно при этом не попёрдывает с переподвыподвертом вприсядку — вот это как раз вообще нерелевантно решению поставленной задачи.

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

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

Замели сайд-эффекты макросов под коврик специальной формы и радуются. Look ma, no side-effects! %)

Технически в кложе тоже можно во время компиляции позапускать процессы, почитать файлы, да хоть в сеть сходить — как говорится, если нельзя, но очень хочется, то можно. Если понимаешь, что делаешь и зачем — а не просто «потому что могу».

мы модифицируем среду компиляции

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

И, как я вижу, не сильно по этому поводу страдают. Решают свои задачи, если надо — и с помощью макросов.

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

Да и на здоровье, пусть себе звездят. Кому не нужны — тому не нужны %) Для огромного количества задач они действительно ни к чему.

нормального рантайма лиспа и лиспового процесса вычисления Clojure не имеет. Хотя даже на Java его сделать было +- можно(см. ABCL), но Рич Хикки «чукча не читатель»

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

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

кложуристы не понимают ни лисп, ни макросы по большому счету, и работают с ними как с сишными «макросами». Типа, какие-то мелочи для удобства

Собственно, вот как и для чего правоверные кложуристы используют макросы:

* Rules of the Macro Club

  *Do not write a macro if a function will suffice*.

  Macros are not first-class; they do not lend themselves well to functional
  programming.

  (They also create additional cognitive load.)

* When to Use a Macro

  1. To run code at compile time (e.g., to get compile-time values or do
     expensive calculations)
  2. To access unevaled arguments (e.g., to define new, convenient syntactic
     constructs)
  3. To emit inline code (e.g., to get the correct line number of some code
     inside this same code)
  4. Any combination of these

(источник: https://ericnormand.me/mini-guide/when-to-use-a-macro-in-clojure)

Борщелисперы используют их как-то не так или для чего-то другого?

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

Вроде бы сайдэффекты не есть хорошо? Ну если делаешь что то сложное…

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

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

Вроде бы сайдэффекты не есть хорошо?

От них всё равно полностью избавиться нее получится, так что они скорее просто данность. Вот неконтролируемые сайд-эффекты по всему мясокомбинату (как это принято в PLOP-язычках, не исключая, кстати, и борщелисп) не есть хорошо, да.

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

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

С наступающим!

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

Енумы определяются-переопределяются, значения ищутся, всё работает. То, что оно при этом не попёрдывает с переподвыподвертом вприсядку — вот это как раз вообще нерелевантно решению поставленной задачи.

Ты смысл не понял вообще, еще раз. Я тебе привел пример своей библиотеки, но ты это тем более не понял.

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

Вперед, пиши на ассемблере. Инструменты ему вторичны.

Замели сайд-эффекты макросов под коврик специальной формы и радуются. Look ma, no side-effects! %)

Да они не в макросах происходят, алло. Сайд-эффекты в макросах нельзя контролировать, ни в CL, ни в Clojure, из-за специфики их вычисления. Потому что они могут компилятором раскрываться рандомно и по несколько раз. Поэтому в CL есть eval-when и прочее, это специальные формы, которые определяют порядок вычисления в разные моменты обработки кода рантаймом. В Clojure до этого просто не додумались, потому что Рич Хики не осилил.

Технически в кложе тоже можно во время компиляции позапускать процессы, почитать файлы, да хоть в сеть сходить — как говорится, если нельзя, но очень хочется, то можно. Если понимаешь, что делаешь и зачем — а не просто «потому что могу».

Да как это сделать то нормально, я тебя спросил? Но ты блеать, даже этого не понял, и высрал вообще непонятно что. А я тебе скажу как - это как в Template Haskell, максимум - определить отдельный модуль, который будет загружаться до компиляции, вот как. Но это дерьмо неудобное.

И, как я вижу, не сильно по этому поводу страдают. Решают свои задачи, если надо — и с помощью макросов.

Ты не понимаешь, что кложурскими макросами задачи которые в CL ими решаются - не решить.

Да и на здоровье, пусть себе звездят. Кому не нужны — тому не нужны %) Для огромного количества задач они действительно ни к чему.

Пиши на ассемблере, я говорю - там вообще ничего нет, ведь ненужно.

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

Да потому что он не осилил нихера вообще сделать. Clojure это кривой урезанный недокоммонлисп. Еще раз. Урезанный. Тупой. Недоделанный. Common Lisp. С парой тупых идей, типа STM, которые не вяжутся вообще с современными ОС и так далее, и хреново на них работают, отчего их никто почти и не использует. И еще с упорином в виде персистентных структур данных, которые на JVM мегахерово ложатся, и оттого глючат и тормозят.

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

Я вообще дико угораю как мудак, с этой ФУНКЦИОНАЛЬЩИНЫ в Clojure, ну типа персистентные структуры данных и так далее. Когда там даже сраной оптимизации хвостовых вызовов нету. Не ну идиоты, а? Ну реально, какая к херам функциональщина без хвостовых вызовов вообще?

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

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

Я себе в принципе могу представить ситуацию, когда вот людям начальство запрещает писать на чем-то кроме JVM, но дико хочется чего-то около-лиспового, или там около-ФП, вообще, и поэтому можно мириться с этим Clojure, и со всем дерьмом оттуда. Ну как это у Рича Хики было собственно, у самого.

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

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

Ты смысл не понял вообще

Ничоси, проспался шоле %)

Да как это сделать то нормально

Да руками, ё-моё. Куча макросов имеет сайд-эффекты (def<shit> макросы по определению сайд-эффектят — модифицируют текущее окружение, это смысл вообще их существования) и никто не делает из этого драмы.

Кстати, не ясно, откуда ты взял, что макросы в кложе могут раскрываться несколько раз. И даже если могут — почему программист не может этот факт учесть при написании макроса.

Clojure это кривой урезанный недокоммонлисп. Еще раз. Урезанный

Давай скажем честно — это не Clojure урезанный, а борщелисп неоправданно (с практической точки зрения) переусложнён. Все эти лютые eval-when, перекладывающие головную боль с автора языка на программиста, или этот лего-адок в обобщённых функциях, куда за каким-то хреном затащили чуть ли не целый специализированный фреймворк для элементарной вещи — композиции функций, и до кучи приколотили туда свою реализацию ООП. И, что самое смешное, гораздо более простые мультиметоды в кложе (которые позволяют диспетчеризацию по произвольной функции от списка аргументов) в итоге оказываются не только более понятными, но и более общими и, соответственно, выразительными.

И такого говна там вагонами. Получается, что если ты не сын Зевса, то эти конюшни проще обойти стороной и приобщиться к идеям лиспа, не погружаясь с головой в исторически обусловленные и одобренные комитетом кровькишки — то есть используя более современные реализации лиспа. Какую-то из схем (если интерес больше академический), кложу (если более практический), или даже какое-то из кложеподобных поделий, которых нынче развелось превеликое множество (если интерес слегка извращённый, но недостаточно, чтобы таки да заныривать в кровькишки).

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

типа персистентные структуры данных

Если ты не вкуриваешь, зачем нужны функциональное программирование и неизменяемые структуры данных (особенно в современном массово мультипроцессорном мире) — это, конечно, печально. Я понемаю, что в благословенную эпоху лисп-машин такой херни не было и можно (и даже нужно) было херачить объекты setf-ами in-place и потом спать спокойно по ночам. Но эти времена прошли и больше никогда не вернутся.

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

Там есть «оптимизация» хвостовых вызовов — вручную, через recur %) Функциональщина получается вполне нормальная, никто особо не жалуется.

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

залечивать что оно лучше Common Lisp

Вообще я такого не говорил, но. Как минимум Clojure проще, и поэтому легче в освоении и использовании (сохраняя при этом основные черты лиспа) — и это уже немаленькое преимущество.

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

кложурскими макросами задачи которые в CL ими решаются - не решить

У нас есть такие задачи, такиииие задачиии — но мы вам о них не расскажем %)

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

Там есть «оптимизация» хвостовых вызовов — вручную, через recur

Мне даже впадлу объяснять что-то, если ты даже таких базовых вещей не понимаешь. Это крайне узкий случай, и это не оптимизация хвостовых вызовов, этот случай затрагивает только простейшие вещи. Хвостовую рекурсию. Взаимную же рекурсию, которая сплошь и рядом в настоящем ФП, это дерьмо никак оптимизирует.

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

Простой пример нужности настоящей оптимизации хвостовых вызовов - это например gen_server в эрланге. Когда у тебя состояния туда сюда переходят, и там ессно повсюду хвостовые вызовы. Но Clojure на такое не способна. Какое нахер там ФП? Это смешно просто.

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

Кстати, не ясно, откуда ты взял, что макросы в кложе могут раскрываться несколько раз. И даже если могут — почему программист не может этот факт учесть при написании макроса.

Про это Рич Хикки сам говорил. Иди и гугли, мне даже лень ссылки давать.

Давай скажем честно — это не Clojure урезанный, а борщелисп неоправданно (с практической точки зрения) переусложнён.

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

Получается, что если ты не сын Зевса,

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

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

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

Ну а что такое метапрограммирование? Это программирование специализированного вычислителя (интерпретатора/компилятора языка программирования) — обучение его новым трюкам, автором языка не предусмотренным. Расширение синтаксиса, изменение порядка вычислений, генерация кода — это всё оно. Всё это в кложе есть и используется.

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

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

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

Иди и гугли

Слив защитан. Если бы Рич был каким-нибудь двинутым на идейной чистоте хаскелистом, от него можно было бы ожидать заявлений в духе «избегать побочных эффектов любой ценой!111». Но он не двинутый академик, он практик — и язык у него специально и намеренно ориентирован в первую очередь на практическое применение.

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

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

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

К тому же, вот что действительно является академической идеей, это например STM, и связанная с этим байда. Абсолютно непрактичная херота, тем более на JVM

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

Метапрограммирование это программирование вычислителя под задачу. В Clojure это невозможно в полной мере.

В полной мере это нигде невозможно, каждый ищет свой баланс между гибкостью и юзабельностью %)

Рич свой баланс нащупал, его устраивает. Множество кложуристов тоже. В конце концов, те же питонщики же как-то живут вообще практически без метапрограммирования и не жужжат (почти).

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

https://groups.google.com/g/clojure/c/Ch4aaR_pTD0?pli=1

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

В OnLisp (10.3 Non-functional Expanders) речь, как я вижу, в основном про изменение аргументов in-place. Но как ты их изменишь, если они неизменяемые? %) Целый класс багов и стога вырванных волос пролетают мимо, какая жаль (нет).

Между тем, там же

it is safe to assume that macro calls in compiled code will not be re-expanded at runtime. Otherwise

Писечка в том, что в кложе нет никакого otherwise. Любой код компилируется в байткод перед выполнением.

As long as Clojure remains exclusively a compiler and not an interpreter (unlike some CL implementations) I think it is safe to assume that macros will only run at compile time.

А криков-то было. Интерпретатор нипричёёооом, эвал-вен убер аллеееес! %)

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