LINUX.ORG.RU

Чем так хороши макросы в Lisp?

 


4

7

Уже много всякого прочитал про лисп(в том числе Practical Common Lisp и уже даже освоился с Clojure), но никак не могу понять, чем же на самом деле являются макросы в этом языке. И этот вопрос не дает мне покоя т.к. лисп сильно повлиял на мое мышление и я вижу, что лисп (а особенно, common lisp для своего времени), действительно, лучше и удобней других языков (ну, за исключением странного скобочного синтаксиса ^^) ... Если бы его преимущества заключались в динамической типизации, сборке мусора и интерактивном цикле разработки, то их в полной мере имели бы питон, javascript и даже php.

Обычно пишут, что макросы - сильная сторона лиспа, которая отличает его от других языков и в качестве аргументов приводят неудачные примеры, которые довольно просто реализовать в других языках. Может кто-нибудь объяснить более-менее формально, что такое макросы? В чем их преимущества?

Для себя мне удалось выделить лишь два свойства макроса, отличающих его от функции:

макрос выполняется до исполнения кода; вычислением аргументов макроса управляет программист.

Первое мне видится ограничением, которое приводит к целому ряду неудобств, как, например, невозможность применить apply к макросу(особено часто хочится сделать (apply and ...) или (reduce and ...)).

А второе может быть легко реализовано посредством функций высшего порядка хоть в C и C++. Для примера, в весьма популярной книге «Приемы объектно-ориентированного проектирования. Паттерны проектирования» Э. Гамма, Р. Хелм, Р. Джонсон, Д.Влиссидес описываются пaттерны Command и Interpreter - в комбинации это в точности макросы времени выполнения...



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

Макросы — это средство реализации костылей произвольной формы. На том стоял и стоять будет Лисп.

Miguel ★★★★★
()

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

anonymous
()

часто хочится сделать (apply and ...)

В newlisp такое возможно, по-моему:


(define sum (lambda(x y) (+ x y)))
(define-macro (sum2 x y) (+ x y))
(define lst '(1 2))
(print (apply sum lst)); -->3
(print (apply sum2 lst)); -->3
(print (apply and lst)); -->2

Но тут define-macro - это по-моему не макрос, а fexpr. Просто так называется. Также, ЕМНИП, picolisp и все старые лиспы до scheme. PS Ну да, забыл сказать, все эти ЯП интерпретируемые, видимо компиляция вводит эти ограничения и приходится трахаться с макрами.

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

вообще Борщь сюда явно «не на охоту» ходит :)

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

Компиляторы eDSL нужны и мейнстримны. Только к лишпику они никакого отношения не имеют, а пишутся на C++ template metaprogramming.

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

А вот не надо ля-ля, нас тут как минимум трое в одном только этом треде.

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

Компиляторы eDSL нужны и мейнстримны. Только к лишпику они никакого отношения не имеют, а пишутся на C++ template metaprogramming.

Ага. Главное чтобы оперативной памяти на сборку хватило. И чтобы в штате были отдельные специалисты, которые отвечают за чтение сообщений об ошибках =).

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

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

Ограничения вводят fexpr'ы так как кроме eval для вычисления ничего сделать нельзя.

Например, if выглядит так:

($define! $f
   ($vau (x y z) e
      ($if (>=? (eval x e) 0)
           (eval y e)
           (eval z e))))
monk ★★★★★
()
Ответ на: комментарий от anonymous

Попробуй, напиши на макросах C++:

(load-interface "interface.xml")

чтобы во время компиляции это разворачиовалось в вызовы Gtk.

А на макросах Лиспа такое можно!

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

Гы. Имелись в виду ограничения, которые указал ТС:

Первое мне видится ограничением, которое приводит к целому ряду неудобств, как, например, невозможность применить apply к макросу(особено часто хочится сделать (apply and ...) или (reduce and ...)).

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

Так fexpr получает не значение, а выражение.

Можно сделать так:

(defun fand (&rest args)
  (cond
   ((null args) t) 
   ((funcall (car args)) t)
   (t (apply #'fand (cdr args)))))

(defmacro thunk (&body body)
  `(lambda () ,@body))

(apply #'fand (list (thunk (> 3 2)) 
                    (thunk (write "no print"))))
T

(apply #'fand (list (thunk (> 2 3))
                    (thunk (write "ok"))
                    (thunk t)))
"ok"
T

Практически, тот же fexpr.

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

Компиляторы eDSL нужны и мейнстримны. Только к лишпику они никакого отношения не имеют, а пишутся на C++ template metaprogramming.

Скажи это разработчикам The Last of Us, которые как раз сбежали с Си-плющ-плющ на Лисп.

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

Попробуй, напиши на макросах C++:
(load-interface «interface.xml»)
чтобы во время компиляции это разворачиовалось в вызовы Gtk.

это решение ненужной задачи, для С++ есть офигенный Qt, и там ты можешь либо уже использовать сгенеренный код, либо загрузить гуй через QUiLoader, а пример eDSL, это, например, интерфейс к Tk:

button(".b") -text("Say Hello") -command(hello);
pack(".b") -padx(20) -pady(6);

причем еще на древнем стандарте и без препроцессора

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

Скажи это разработчикам The Last of Us, которые как раз сбежали с Си-плющ-плющ на Лисп.

во-первых они выбрали вменяемый лисп - racket, во-вторых не сбежали, а использовали совместно

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

во-первых они выбрали вменяемый лисп - racket

Бред, s-выражения и макросы по всех лиспах одинаковые, за исключением поделок с насильно навязываемой гигиеной.

во-вторых не сбежали, а использовали совместно

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

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

Бред, s-выражения и макросы по всех лиспах одинаковые

и тем не менее лиспы они разные

Си-плющ-плющ с задачами не справлялся

т.е. в GTA 5 справился, а тут нет, ну окай, анонимусам виднее

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

Попробуй, напиши на макросах C++:

(load-interface «interface.xml»)

чтобы во время компиляции это разворачиовалось в вызовы Gtk.

Если это будет необходимо...

#include "generated-interface.cpp"

и в Makefile:

generated-interface.cpp: interface.xml
  gen-interface $> $@

Не так красиво, но цель достигнута.

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

и тем не менее лиспы они разные

А defmacro везде одинаковый.

ну окай, анонимусам виднее

Разработчикам The Last of Us виднее.

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

А defmacro везде одинаковый.

http://docs.racket-lang.org/compatibility/defmacro.html

This compatibility/defmacro library provides support for writing legacy macros. Support for defmacro is provided primarily for porting code from other languages (e.g., some implementations of Scheme or Common Lisp) that use symbol-based macro systems.

Use of defmacro for modern Racket code is strongly discouraged. Instead, consider using syntax-parse or define-simple-macro.

Разработчикам The Last of Us виднее.

Разработчики GTA 5 рвут на себе волосы

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

кстати, раз уж ты так доверяешь разработчикам The Last of Us, которые выбрали racket, то иди до конца - defmacro не нужен же

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

ну напиши в спортлото авторам The Last of Us, узнай их мнение

То, что defmacro универсальное и мощнее костыльной гигиены, и без их мнения очевидно. Тем более у них уже был опыт создания компиляторов на Common Lisp.

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

То, что defmacro универсальное и мощнее костыльной гигиены, и без их мнения очевидно.

наверное потому разработчики The Last of Us сбежали с общелишпика на racket (c)

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

Какой нахрен тот же. Тебе же говорят - first class. А ты оборачиваешь. Сможешь сделать вот так?

(define sum (lambda(x y) (+ x y)))
(define-macro (sum2 x y) (+ x y))
(define lst '(1 2))

(define wrap
  (lambda(f1 f2 f3 data)
    (map apply (list f1 f2 f3) (list data data data))))

(print (wrap sum sum2 and lst))

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

наверное потому разработчики The Last of Us сбежали с общелишпика на racket (c)

Сбежали с коммерческого Allegro Common Lisp, «because it’s a good Lisp, is open source, and has quality libraries and implementation».

Что касается возможностей для написания компиляторов eDSL - там и там одинаковые.

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

И тогда еще упоротого Racket не было, была PLT Scheme. Вот, видно, и выбрали схемку с define-macro специально.

anonymous
()

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

«Скобочный» синтаксис - *одно* из основных преимуществ.

Если бы его преимущества заключались в динамической типизации, сборке мусора и интерактивном цикле разработки, то их в полной мере имели бы питон, javascript и даже php.

Полноценного интерактивного цикла разработки ни в одном из трёх нет. Есть только аналог REPL.

Первое мне видится ограничением, которое приводит к целому ряду неудобств

Потому что не надо пытаться применить макросы там, где надо просто написать функцию.

особено часто хочится сделать (apply and ...) или (reduce and ...)).

Функция every.

А второе может быть легко реализовано посредством функций высшего порядка

Только в случае с макросами вида WITH-*, которые получают кусок кода.

хоть в C и C++.

Я бы здесь первым примером Ruby назвал. :)

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

Что cl, что racket (да и r5rs тоже в плане семантики - калоши и пр) , переусложненное неповоротливое говно. Последний тру лисп - это маклисп. С рождением схемы лисп здох.

PS другой анон

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

Потому что не надо пытаться применить макросы там, где надо просто написать функцию.

Ну да, а в плюсах не надо пытаться применить один класс, там где нужен другой. А другой не надо где третий. А третий...

*одно* из основных преимуществ.

Было, пока лисп не скатился в мейнстримное говно.

anonymous
()

имели бы питон, javascript и даже php

javascript - это диалект лиспа.

anonymous
()

ну, за исключением странного скобочного синтаксиса

pretty printing и подсветка скобок

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

Почему? Чем препроцессор на Лиспе, выдающий код на Лиспе для использования в программе на Лиспе не угодил?

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

Не осилили. Тупые. Типичненько.

Вот пусть задроты Си-плющ-плющ и осиливают, пожирая мамкин борщ. А простые разработчики выбирают тупой Лисп, пишут тупой код и выпускают качественные продукты в срок.

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

Потому, что лишп не нужен. Это убожество пытаются впарить под тем предлогом, что якобы в нем проще чем где либо еще делать компиляторы eDSL. Тогда как на самом деле эти самые компиляторы eDSL, внешние препроцессоры, полноценные standalone DSL и тому подобное элементарно делаются и на вполне человеческих технологиях, без скобочек и тупого GC.

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

Ты тут чего-то явно напутал. Продуктов на лишпе никто не выпускает. Или ты свою лабу с первого курса «продуктом» назвал?

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

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

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.