LINUX.ORG.RU

[Lisp] туплю с defmacro

 


0

2

Есть код:


(defun get-action-name (list)
	(mapcar #'(lambda (def-list) (list (first def-list) (second def-list))) list))

(defmacro with-new-actions (parent actions-list
							&body body)
  (get-action-name actions-list))

(defparameter *workspace-actions-list* 
  '((aNew "&New" #'new-page-handler "tab- new" "Ctrl+N")
	(aOpen "&Open" #'open-page-handler "open" "Ctrl+O")))

(with-new-actions 'parent *workspace-actions-list*) ;; 1

(with-new-actions 'parent ((aNew "&New" #'new-page-handler "tab- new" "Ctrl+N")
	(aOpen "&Open" #'open-page-handler "open" "Ctrl+O"))) ;; 2

Проблема в том что строка (2) раскрывается, а строка (1) - нет.

Вопрос как раскрыть *workspace-actions-list* внутри defmacro ? Google молчит, вот туплю и мучаюсь.

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

> Например, для интерактивной разработки. Сгенерил-запустил-поправил.

Угу.

Или сгенерить какое-то меню в рантайме.

Любое меню/тоолбар плагинами должны уметь расширяться в runtime. Но до этогоместа я пока не добрался.

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

> Любое меню/тоолбар плагинами должны уметь расширяться в runtime.

Но там я уже eval, разумеется, наружу не выставлю. А вот свой код вполне может колбасить меню в runtime в зависимости от кучи условий.

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

>Во-первых, пока что автор темы ещё не сказал, что это - константа?

докажет обратное - сам отсоветую ему использовать #. =)

Например, для интерактивной разработки. Сгенерил-запустил-поправил.


defconstant на defconstant работает, разве что предупреждение выплюнет

Почему плох #. , я не помню.


я помню - фактически работает через `print - read`, но в compile-time. По сравнению с eval во время применения макроса действительно ничего не даёт, разве что короче выглядит.

...что ведёт к усложнению анализа процесса загрузки... <и прочее>


ещё раз: я исходил из предположения, что работаем с константой.

Если всё это - только ради избавления от eval, то я не вижу смысла.


`это всё` (а как много то этого!) для того, чтобы не «вычислять константу», которая прекрасно «читается по написанному»

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

>И потом поимей проблемы с переопределением константы.

какие проблемы? только предупреждение

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

goto в Си и eval в CL - совершенно разного порядка вещи.
eval - плохо и нельзя. Просто запомни это.

В крайнем случае можно symbol-value использовать, но никак не eval.

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

Ну, в общем, по большому-то счёту, можно и так, и этак. Может я и зря придрался. В любом случае, предпочтительнее вынести определение этой *workspace-actions-list* в отдельный файл.

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

>Только непонятно чем вот это... отличается от eval в макре - и там и там время компиляции

по результату - ничем. Фактически в первом случае ридер считывает напечатанное значение константы, во втором работает «полноправный интерпретатор»

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

> goto в Си и eval в CL - совершенно разного порядка вещи. eval - плохо и нельзя. Просто запомни это.

eval и в перловке тоже плохо, но когда нужно - используют.

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

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

>Любое меню/тоолбар плагинами должны уметь расширяться в runtime. Но до этогоместа я пока не добрался.

после отработки твоего кода простое изменение *workspace-actions-list* нифига не даст, сколько не танцуй ни с eval, ни без него

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

>eval и в перловке тоже плохо, но когда нужно - используют.

Перловка - говно, и там часто без eval никак.

CL - не перловка.

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


А как еще объяснять банальности, которые должен знать каждый?

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

>Вот все придрались к несчастному eval'у.

мы не придрались =) Если ты отдаешь себе отчёт в своих действиях, при этом _зная_ что именно ты делаешь - никаких проблем. А мы так - описываем варианты и чем они отличаются =)

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

Ну вот еще подтверждение, что ты нифига не понимаешь.

Все макросы отрабатывают в compile-time, еще раз.

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

> Ну вот еще подтверждение, что ты нифига не понимаешь.

Все макросы отрабатывают в compile-time, еще раз.

Мммать. А если мне macroexpand-1 понадобится ? В runtime. И на другой environment ? И macroexpand-all нет ?

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

> eval - плохо и нельзя

Наверно опять разработчики стандарта фигню спороли, а нам теперь расхлёбывать?

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

> Может, тогда уж глобальный define-symbol-macro. Только на самом деле, я не вижу преимуществ по сравнению с предложенным eval-ом.

на первый взгляд, я его тоже не вижу: вариант с eval проще, легче читается и разница в производительности несущественна. На второй взгляд, макросом DSL-нее: тут пришлось обносить eval-when + eval список. Этот eval список — вычисление оп. семантики «макроса». Если бы вычисление было похитрее, например rule engine, оптимизации и т.п. можно было бы такой eval скрыть за этой абстракцией.
Выглядеть это будет громоздко, скорее всего.

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

> после отработки твоего кода простое изменение *workspace-actions-list* нифига не даст, сколько не танцуй ни с eval, ни без него

А то я не понимаю. :)

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

eval - плохо и нельзя. Просто запомни это.

Недоказуемая идеологема, к тому же ложная. Вот поискал eval в своём коде. Примеры:

1. В определении asdf - системы,

(load-op :after (op c) (eval (read-from-string "(budden::connect-database)")
по-другому нельзя, т.к. в момент загрузки файла .asd пакета ещё не существует.

2. Используется в самой asdf, например,

defun sysdef-central-registry-search ...
(dolist (dir *central-registry*)
        (let* ((defaults (eval dir)) 

3. В программе управления печкой - потоки исполнения кладут друг другу кусочки кода в mailbox.

4. Своего рода map макроса по списку.

(loop for (a b) in 
   *lexer-list*
   do (eval `(define-parse-tree-synonym ,a ,b)))

5. У меня, конечно же, своя система тестов. Вот она:

;; Written by Denis Budyak. 
;; #.+BSD-license+

(in-package :trivial-deftest)

;;; Trivial test suite. Define tests in the
;;; source file, the would run at load-time. Tests 
;;; can be viewed as examples
(defvar *run-tests* t)

;; ! is a shorthand for deftest
(defmacro ! (name expr1 expr2 &rest keyargs &key (test ''equalp)) "defines a test"
    (let ((function-name (intern (concatenate 'string (string '#:test-fun-) (princ-to-string name)))))
      (unintern function-name)
      (when *run-tests*
        `(eval-when (:load-toplevel :execute) 
           (eval ; macros would expand at compile time otherwise
            `(progn
               (defun ,',function-name ()
                 (unless (funcall ,',test ,',expr1 ,',expr2)
                   (warn "deftest failed: ~S" '(,',function-name ,',expr1 ,',expr2 ,@',keyargs))))
               (,',function-name)))))))
(чего-то я смотрю в этот код и не пойму, зачем здесь intern с последующим unintern, ну да ладно).

И т.п. Надоело искать.

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

eval это такая фича для разработчиков, максимум. Т.е. что-то там потестировать, попробовать, интерактивно.

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

> (eval (read-from-string "(budden::connect-database)")
(funcall (intern (string :connect-database) (find-package :budden)))

2. Используется в самой asdf, например,

Аналогично можно. Просто разработчики ленивые.

В программе управления печкой - потоки исполнения кладут друг другу кусочки кода в mailbox.

не понял.

4. Своего рода map макроса по списку.

А контекст этого кода какой?
Если toplevel, можно делать (macrolet ((frob ...)) ...)
ну как в сорцах sbcl повсюду

5. У меня, конечно же, своя система тестов. Вот она:

лучше compile+funcall
У eval с раскрытием макросов туго, не гарантируется.

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

>eval это такая фича для разработчиков, максимум. Т.е. что-то там потестировать, попробовать, интерактивно.

ну вот разработчик применяет eval во время отработки макроса к своим собственным данным. После сохранения image этот eval «выполнится никогда» =)

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

>У eval с раскрытием макросов туго, не гарантируется.

Вернее, не гарантируется, что один раз раскроются и т.п.

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

>ну вот разработчик применяет eval во время отработки макроса к своим собственным данным.

Нет, в коде это зло, если не 100% константное выражение(а в код автора, как видно, можно что угодно пропихнуть). Я имел ввиду REPL и т.д.

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

У eval с раскрытием макросов туго, не гарантируется.

Расскажи про это моим файлам, в которых эта тестилка применена :-) Вот пример, который, безусловно, работает:

(defmacro let-with-conc-type (var type value &body body)
  "type must be an atom"
  (assert (typep type 'symbol) () "let-with-conc-type: ожидали имя типа, получили ~S" type)
  `(let ((,var ,value))
     (declare (type ,type ,var))
     (with-conc-name ,var ,(symbol+ type '-)
       ,@body)))


#+see-packages
(trivial-deftest::! #:let-with-conc-type.1
                    (let-with-conc-type x string "asdf"
                      `(,(x.equal "asdf") ,(x.upcase) ,(x.equal x.upcase))
                      )
                    '(T "ASDF" T))

Мейлбоксы - посмотрел ещё раз, там можно было обойтись symbol-value, но зато в слове eval меньше букв :P

Остальное даже не вижу смысла комментировать. Да, eval в этих случаях можно попытаться заменить, но для этого нужно принять твою идеологему, а ты ведь даже не удосуживаешься привести доводы в её пользу. Кроме того, я не сторонник идеологем.

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

> Вернее, не гарантируется, что один раз раскроются и т.п.
Пруф в студию.

den73 ★★★★★
()

А почему нельзя сделать это функцией и использовать

(with-new-actions 'parent *workspace-actions-list*)

(with-new-actions 'parent '((aNew "&New" #'new-page-handler "tab- new" "Ctrl+N")
    (aOpen "&Open" #'open-page-handler "open" "Ctrl+O")))
no-such-file ★★★★★
()
Ответ на: комментарий от den73

>а ты ведь даже не удосуживаешься привести доводы в её пользу.

EVAL это, как минимум:
1) Тормоза - задействуется весь жирный коммонлисповый интерпретатор, а в ряде случаев(в SBCL по дефолту, например) и целый компилятор.
2) Небезопасность - третьи лица могут выполнить свой вредоносный код.
3) Размытость семантики раскрытия макросов - в стандарте по поводу макросов в интерпретируемом коде не говорится практически ничего, реализации делают кто во что горазд.

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

> Тормоза - задействуется весь жирный коммонлисповый интерпретатор,
Ты колдун, что ли? Как раз сегодня ночью пытался бороться с тормозами в своём препроцессоре SQL. Кое-что получилось, и параллельно я пришёл к твёрдому выводу, что (load ) быстрее, чем (load (compile)). Вот сейчас хотел специально для тебя выложить результаты time и - надо же, вариант через compile оказался быстрее. Чертовщина какая-то. Вообще говоря, существует одноразово исполняемый генерируемый в рантайме код, который быстрее проинтерпретировать, чем скомпилировать и потом выполнить. И который проще породить как дерево, чем с помощью лямбд. То, что SBCL всё компилирует - это личная проблема SBCL (тем более, как я понял, это можно отключить).

Небезопасность - третьи лица могут выполнить свой вредоносный код.

Ну если у них есть REPL или просто обычный бинарный отладчик, или если при ошибке программа падает в REPL отладчика, они и так это смогут. Или если в образе есть compile или eval, то можно с помощью бинарного отладчика это сделать. Да и ещё сотней способов. А если твоё приложение крутится на сервере и ты даёшь пользователю только определённые точки входа, где он может что-то написать, а сама программа ему для правки недоступна, то наличие или отсутствие eval тут не будет условием безопасности/небезопасности. Есть ещё программы, где нет никаких левых пользователей.

Размытость семантики раскрытия макросов - в стандарте по поводу макросов в интерпретируемом коде не говорится практически ничего, реализации делают кто во что горазд

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

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

> А почему нельзя сделать это функцией?
Потому что это ещё не дописанный макрос. Читай тему.

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

> Наверное вы с самого начала хотели написать

Там надо разворачивать глобальную переменную - уже разобрались.

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

[qoute] Макросы: 1) раскрываются при компиляции и Ну зачем же нести такую чушь? В CL фактически нету никакого разделения между временем компиляции и рантаймом, по-этому макрос может быть раскрыт когда угодно. Никто не гарантирует, что он будет раскрыт в компайлтайме.

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

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

> Там надо разворачивать глобальную переменную - уже разобрались

Если вы про решение с eval, то это костыль. Зачем что-то разворачивать если можно просто подставить? Вам нужно переписать вашу макру так, чтобы она генерировала нужный вам код, а вы вместо этого пытаетесь выполнять код приложения на стадии компиляции.

Впрочем, дело ваше...

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

> Ну зачем же нести такую чушь?

Ну откуда же столько категоричности?

В CL фактически нету никакого разделения между временем компиляции

и рантаймом



Hyperspec считает иначе.

Никто не гарантирует, что он будет раскрыт в компайлтайме.


Стандарт гарантирует. Точнее, с точки зрения CL компиляция это и есть процесс раскрытие макросов, а не то, что некоторые думают. Читаем и просвещаемся: http://www.lispworks.com/documentation/HyperSpec/Body/03_bbb.htm

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

Стандарт гарантирует.

Ну вот например здесь:

(defparameter x (read))
(defun yobafun () (progn (setq x (+ 1 x)) x))
(defmacro yobamacro () (yobafun))
(print x)
(print (yobamacro))
(print x)
(setq x (read))
(print (yobamacro))
(print x)
когда макросы раскрываются, и что имеем на выводе?

напиши пример

Пример чего?

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

Нет, именно так, как написано у меня. Херь полная, конечно, но таков CL. Я же говорил - отсталая макросистема.

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

>Нет, именно так, как написано у меня.

тогда defparameter и defun должны объявляться и в compile-time, иначе неопределённость полная...

Я же говорил - отсталая макросистема.


знаешь лучшую? Пока твои претензии больше похожи на твоё-же «ниасиляторство»

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

> знаешь лучшую? Пока твои претензии больше похожи на твоё-же
> «ниасиляторство»

Fare знает (уж его то сложно упрекнуть в «ниасиляторстве»):

Если вам нравится использовать нетривиальное метапрограммирование, то рекомендую избегать такого примитивного в этом отношении языка, как Common Lisp, и использовать современный язык с многостадийной компиляцией и четко определенной семантикой (например, язык модулей PLT Scheme, camlp4 для OCaml, и другие системы, обрабатывающие файлы детерминированным образом). У макросов PLT есть ряд преимуществ: гигеничность, совместимость с отладчиками и другими инструментами и т.п.

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

>Fare знает (уж его то сложно упрекнуть в «ниасиляторстве»)

зато можно упрекать в предвзятости, ангажированности и демагогии

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

И вообще, за любые попытки манипулирования эмоциями в [около]технических «публикациях» засовывал бы голым задом в муравейник/термитник/пчелиное дупло 8-Е

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

> зато можно упрекать в предвзятости, ангажированности и демагогии

Считаете, что разбираетесь в вопросе лучше него?

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

тогда defparameter и defun должны объявляться и в compile-time, иначе неопределённость полная...

Ну а вот в CL они не должны объявляться в compile-time (то есть согласно здравому смыслу, может, и должны - но с точки зрения дизайна языка это никак не регламентируется), потому что функции одновременно можно вызывать и в компайл-тайме из макросов и в рантайме. А семантика какой-нибудь хрени типа:

(defmacro yobamacro () ... *гдето тут вызываем yobafun*)
(defun yobafun () ... *гдето тут вызываем yobamacro*)
это вообще туши свет. Вобщем-то в моем предыдущем примере, если я правильно понимаю принцип экспанда в CL, мы получим эрор в рантайме в момент попытки выполнения (yobamacro) (в общем он не может раскрыться в компайлтайме и не раскроется вообще никогда), но если пример еще немножко модифицировать, то ошибка вообще будет отложена до хз когда и может вылететь в любой момент, когда мы вызовем функцию, в которой есть это (yobamacro).

знаешь лучшую?

Да тот же Racket. Там по крайней мере нельзя написать откровенно бредового кода, а уж как сделать в CL локальный скоп макросов, вроде такого:

(define x 5)
(define-syntax-rule (yoba) (display x))
(yoba)
(let ([x 10]) (display x) (yoba))
(yoba)

-> 5 10 5 5
это я даже не представляю.

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

>Считаете, что разбираетесь в вопросе лучше него?

МЛЯТЬ! как соотношение «владений вопросом» коррелирует с перегруженностью его статьи сраными эмоциями и апелляциями к его собственным ожиданиям?

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