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 молчит, вот туплю и мучаюсь.

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

ЁТМ, а где ты хочешь ошибку с eval? В compile-time? Ты совсем умом тронулся?!

Да при чем тут евал? Ошибка не в евале, а в определении макроса, который не должен вообще компилироваться. Я просто привел пример, как игнорирование этой ошибки компилятором приводит к ошибке в рантайме.

А чьи это слова:

Ты дурачок чтоли? Следи за разговором. Речь шла о чуши, которую ты начал нести про «вдруг я любитель коротких имен» (причем так и не объяснил, что имел ввиду). Мой пример про локальный скоп здесь совершенно не при делах.

Один в один пример на CL пишется элементарно стандартными средствами.

Да не запишется он на CL, никакими средствами. Разберись уже, как работает макросистема CL, а то ты, как я смотрю, весьма плохо ее понимаешь.

Вот только из примера нифига не понятно, что ты хочешь использовать не доступ к top-level переменной, а к [локальной] переменной из контекста объявления макроса.

Так нету никакой разницы. top-level переменная - это и есть переменная из контекста объявления макроса, если сам макрос объявлен в топ-левеле. У вас в CL по-другому?

а к локальной (внутри функции?)

Ну если ты определил макрос внутри функции, как-то так:

(define (fun x)
  (define-syntax-rule (macro) x)
  (macro))
то да, это будет локальная х, которая в функции. Но за пределами этой функции макроса не существует. В общем, макросы ведут себя точно так же, как функции.

перевожу пример на CL:

(let ([x 1])
  (defmacro yoba1() `x)
  (let ([x 2])
    (defmacro yoba2() `x)
    (let ([x 3])
      (defmacro yoba3() `x)
      (print (yoba1))
      (print (yoba2))
      (print (yoba3)))))

-> 1 2 3
и ни при чем тут ни топлевел ни пекеджи (которые, кстати, тоже весьма кривой костыль), так просто устроена в PLT гигиена - макрос замкнут на скоп своей области определения, функции замыкаются точно так же:
(let ([x 1])
  (defun yoba1() x)
  (let ([x 2])
    (defun yoba2() x)
    (let ([x 3])
      (defun yoba3() x)
      (print (yoba1))
      (print (yoba2))
      (print (yoba3)))))

-> 1 2 3
То есть, грубо говоря, гигиена в PLT это всего навсего лексический скоп макросов. Отсутствие гигиены (как в CL) - это динамический скоп. Логично скопить переменные по дефолту лексически (то есть дефолтная гигиена) и объявлять ее динамической, когда требуется (выводить из-под гигиены). Понятно так?

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

>Ошибка не в евале, а в определении макроса, который не должен вообще компилироваться.

с какой стати не должен? Отсутствие вызываемой функции в момент компиляции - не ошибка.

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


совсем воображения нет? имена у «внешней» и «внутренней» (загигиенизированной) переменных совпадают. Я при обращении к внешней забыл снять гигиену. Ошибка будет в логике работы программы, и ни одна собака мне про неё ни слова не скажет.

Да не запишется он на CL, никакими средствами.


На твоё незнание мне настрать

Так нету никакой разницы. top-level переменная - это и есть переменная из контекста объявления макроса, если сам макрос объявлен в топ-левеле.


ну меня смутило упоминание «локальный»... Ладно, разницы нет ибо

Но за пределами этой функции макроса не существует.


так это нах тогда не надо

Логично скопить переменные по дефолту лексически


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

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

с какой стати не должен? Отсутствие вызываемой функции в момент компиляции - не ошибка.

А что же это?

совсем воображения нет? имена у «внешней» и «внутренней» (загигиенизированной) переменных совпадают. Я при обращении к внешней забыл снять гигиену. Ошибка будет в логике работы программы, и ни одна собака мне про неё ни слова не скажет.

Ну то есть ты говоришь о том, что, если у нас есть:

(let ([x 1]) ; это внешняя переменная
  (let ([x 2]) ; это внутренняя переменная, получается после экспанда
    *здесь код*))
То если мы во внутреннем let-e просто обратимся к х («забыв», что нам нужна не внутренняя х = 2, а внешняя х = 1), то получим ошибку в логике работы программы? Но при чем тут макросы и гигиена? Это просто особенности работы лексической области видимости. Если уж перекрыл let-ом переменную - это твои проблемы. Или ты имел ввиду что-то другое? Приведи уже пример кода, пара строк же всего, а сразу будет все понятно.

На твоё незнание мне настрать

А при чем тут мое незнание? Ты же сам сказал, что нельзя.

так это нах тогда не надо

Это надо, чтобы макрос не ломался. В CL этого нет и приходится лепить кучу костылей.

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

Опять пальцем в небо. Никаких проблем со временем компиляции/выполнения тут нету и быть не может. Это не CL.

Примеры же твои высосаны из пальца.

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

anonymous
()

Что, опять срач? Если бы товарищи-антилисперы были действительно против Лиспа. они бы наверное не разводили таких срачей, которые могут привлечь внимание к Лиспу. Вывод - кому-то просто хочется посраться.

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

> Если бы товарищи-антилисперы

Ты не понял, это внутрилисповые разборка, типа CL vs Scheme - и то и другое лисп.

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

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

Обобщать, всё же, не стоит.

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

с какой стати не должен? Отсутствие вызываемой функции в момент компиляции - не ошибка.

А что же это?

Функция может быть определена после макроса, хоть вообще в рантайме. Но SBCL выдаст предупреждение. А в твоём первом примере с yobafun функция уже частично определена. defun --- это макрос, раскрывается он в два eval-when, один из которых отрабатывает во время раскрытия макроса, и компилятору становится известна сигнатура функции. Поэтому он может проверить твой код на валидность и в данном случае не выдаёт предупреждения.

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

(defmacro with-f=42 (&body body)
  `(let ((f 42))
     ,@body))

(with-f=42
  (let ((f 15))
    (format t "~a~%" f)))

и надеяться получить на выводе 42 просто глупо. Примерно так же, как и

(with-open-file (f filename :direction :output)
  (let ((f "a"))
    (format f "~a~%" f)))

разве что в последнем случае ошибка очевидна.

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

Функция может быть определена после макроса, хоть вообще в рантайме.

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

(defun yoba () (raise-exception ...))
И сразу все хорошо.

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

Во-первых, генсимом нельзя заменить гигиену. Суть гигиены состоит в том, что если ты пишешь макрос вроде:

(defmacro with-f=42 (&body body)
  `(let ((f 42))
     ,@body))
то с гигиеной можно не бояться, что кто-нибудь возьмет и переопределит let-форму, например, в результате чего макрос будет работать как угодно, но не как нужно. В некотором смысле гигиену позволяет заменить система пекеджей, но это весьма костылеобразно и все равно ломается, при желании. Во-вторых, важна даже не возможность введения/обхода гигиены, а то, что если гигиену забыл ввести - это хрен отловишь, а если забыл обойти - код не скомпилится.

и надеяться получить на выводе 42 просто глупо.

Это yyk - он по контексту вверх прыгать хочет.

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

Ну это вообще в макросах довольно странно. Однако, если очень хочется, то можно

Ну вот а в Template Haskell - нельзя, там эти средства настолько соответствуют своему назначению, что прежде чем что-то «намакросить» нужно долго вникать и много специфицировать. А в CL макросы это просто хук - возможность в стадии eval начать компиляцию и во время стадии compile начать произвольное вычисление, это интроспективность метациклического (по SICP) транслятора, но также получается что можно сделать *всё* что угодно, даже на стадии компиляции. Кстати, тоже касается и ридера - #.(reboot) будет перезагрузка времени чтения :) С одной стороны это очень высокая степень гибкости и расширяемости, с другой - границы языка очень размыты и часто можно видеть стрельбу по воробьям из пушки.

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

> А в CL макросы это просто хук

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

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

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

Ничто не помешает написать бредового кода. Насчёт локального скопа - в CL это классика, называется once-only (by Paul Graham):

(defparameter *x* 5)

(defmacro foo ()
  (once-only ((*x* *x*))
    `(print ,*x*)))

(foo)

(let ((*x* 10))
  (print *x*)
  (foo))

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

> Насчёт локального скопа - в CL это классика, называется once-only

(by Paul Graham):


Твой пример бессмыслен чуть более, чуть полностью. И очевидно работает не так и не потому, как ты вероятно ожидал. Разберись сам ))

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

> с гигиеной можно не бояться, что кто-нибудь возьмет и переопределит let-форму, например, в результате чего макрос будет работать как угодно, но не как нужно

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

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

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

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

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

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

Насчёт локального скопа - в CL это классика, называется once-only (by Paul Graham):

Это не то совершенно.

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

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

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

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

чувствуешь в себе силы написать правильно и не запутаться в логике --- бери общелисповый defmacro.

А чего уж так? Давай сразу ассемблер.

Макросы схемы, кстати, ещё не предел --- на дальнем конце шкалы маячит Template Haskell, который за тебя ещё и типы используемых переменных проверит.

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

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

спасибо, что перенял эстафету =)

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

Это не то совершенно.

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

;=> 5 10 5 5
(defparameter *x* 5)
(defmacro foo () (once-only (*x*) `(print ,*x*)))
(foo)
(let ((*x* 10)) (print *x*) (foo))
(foo)

;=> 5 10 5 5

А что нужно? Может так:

(defmacro defmacro/locally (name lambda-list locally-rebindings &rest body)
  `(defmacro ,name ,lambda-list
     (let ,(mapcar #'(lambda (binding)
                       (if (atom binding)
                           `(,binding ,(eval binding))
                           `(,(first binding) ,(eval (second binding)))))
                   locally-rebindings)
       ,@body)))

(defparameter *x* 5)
(defmacro/locally foo () (*x*) `(print ,*x*))
(foo)
(let ((*x* 10)) (print *x*) (foo))
(foo)
(setf *x* 10)
(print *x*)
(foo)

;=> 5 10 5 5 10 5
quasimoto ★★★★
()
Ответ на: комментарий от anonymous

>Но при чем тут макросы и гигиена?

пример был не о let, а о гигиене, и о том, что она не спасёт меня в случае наличия переменных с одинаковыми именами (ничего не скажет) и, соответственно, как «ручная гигиена» так и ручное её снятие - сущности одного порядка.

Ты же сам сказал, что нельзя.


с больной головы на здоровую... Ты бы хоть цитировал...

Никаких проблем со временем компиляции/выполнения тут нету и быть не может. Это не CL.


Я тебе давно о том-же: нехер со схемовскими порядками (а скорее с твоими собственными тараканами в голове размером с лошадь) лезть в CL =)

просто демонстрируют особенности работы гигиены.


ты мне ещё букварь почитай, КО ты наш несравненный...

Вобщем-то понятно...


Не льсти себе, подойди ближе...

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

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

...можно не бояться, что кто-нибудь возьмет и переопределит let-форму...


т.е. ты признаёшься, что совершенно не вкурил принципов работы CL, и наезжаешь на некоторые его «ключевые возможности» - ака «работа с именованными сущностями через символы» и возможностью в любой момент переопределить [почти] любой символ? Тогда тебе CL точно не нужен. Свободен...

Это yyk - он по контексту вверх прыгать хочет.


Это ты пытаешься меня заставить показать как это делается в макросах ;)

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

Твой пример бессмыслен чуть более, чуть полностью.

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

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

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

Ты опять совершенно не в теме: это «фича» лиспа - возможность переопределить «всё и вся» (утрирую). Но если ты очень хочешь «защищённости» - ты можешь её обеспечить, не вопрос (как - не ко мне, у меня дорого). Аналогично в схеме: а если я хочу _разрешить_ использовать именно модифицированный let (и перемодифицированный через один шаг тоже) - какой танец с бубном мне надо будет исполнить?

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


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

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


Это у тебя в ДНК недочёт по поводу использования инструмента без чтения документации по собственному весьма нетривиальному наитию...

Макросистема схемы же с одной стороны защищена, с другой - шире макросистемы CL по возможностям.


Не доказано. Обоснование типа «гигиена по умолчанию» не принимается - давай отсутствие принципиальных возможностей - то, что невозможно сделать в CL и что может схема. И касательно языка (стандарта), а не отдельных его реализаций. А то и так уже «можно писать на схеме, а можно на Racket».

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

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

А что нужно? Может так:

Вот это как раз характерный стиль программирования для многих общелисперов. Написать костыль для частного случая, который работает через раз хоть на каких-нибудь исходных данных, и выдавать его за общее решение отлаженное. Первый твой вариант работает только для топ-левел переменных и в репле. Второй вариант плох по нескольким причинам: 1. надо указывать биндинги явно 2. как мне указать биндинги для макросов или спец форм? Я же мог переопределить какой-нибудь if, например. А (eval `if) работать не будет. 3. та же проблема, что и в первом примере - работает только на топ-левеле и в репле. Стоит сделать (defparameter *x* (read)) - и уже нельзя будет (eval `*x*) во время макроэкспанда.

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

пример был не о let, а о гигиене, и о том, что она не спасёт меня в случае наличия переменных с одинаковыми именами (ничего не скажет) и, соответственно, как «ручная гигиена» так и ручное её снятие - сущности одного порядка.

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

Я тебе давно о том-же: нехер со схемовскими порядками (а скорее с твоими собственными тараканами в голове размером с лошадь) лезть в CL =)

То есть неизбежные проблемы с временем выполнения/компиляции - это порядки CL? Что называется - героически преодолеваем самими же собой созданные проблемы.

ты мне ещё букварь почитай, КО ты наш несравненный...

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

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

и выдавать его за общее решение отлаженное

Но где я об этом говорил? Может это был кто-то другой? :) Вообще костыль, да.

В остальном - это ж не попытка заставить CL плясать как Scheme, языки определённо разные (правда я видел исходный код scheme со схемными макросами, написано это было на CL причём не в виде интерпретатора а как расширение, далее на этой схеме был написан хаскель (!) и в основном всё это писала женщина (!!)).

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

Ты опять совершенно не в теме: это «фича» лиспа - возможность переопределить «всё и вся» (утрирую). [/quote Ну да, переопределять все и все - это замечательно я только за. Вот только CL этого делать не позволяет. Смотри, у меня есть некий набор макросов. Их не я написал, но я их использую в своем проекте. И тут я захотел у себя, в своем проекте поменять let. Ну удобно мне в силу каких-то причин, чтобы let работал не по стандарту, а как-то иначе. И ВНЕЗАПНО после этого переопределения все макросы перестают работать. Почему? Потому что в них есть let. И автором макросов предполагалось, что let работает строго определенным образом. Это дикость. Нормальная ситуация - это когда я переопределяю let, а макросы продолжают работать, как работали.

Но если ты очень хочешь «защищённости» - ты можешь её обеспечить, не вопрос

В том-то и дело, что нет. В CL есть два способа, которыми можно попытаться ее обеспечить - генсим и пекеджи. Генсим не универсален, пекеджи дырявы. Ну и еще неудобны в использовании, я уже об этом говорил. Чересчур большой оверхед с ними выходит. Проблема-то в том, что гигиена нужна практически везде. Проще снимать ее в 10% мест, чем навешивать в 90%.

а если я хочу _разрешить_ использовать именно модифицированный let (и перемодифицированный через один шаг тоже) - какой танец с бубном мне надо будет исполнить?

(local-syntax-introduce syntax-object) выводит syntax-object из-под гигиены. Вызов одной единственной функции - офигенный танец с бубном.

Ты опять попутал кислое с мягким. В CL отсутствие "защиты в твоём понимании" - фича, а не "недоработка". Но я начинаю подозревать, что тебе, как настоящему плотнику, все вокруг кажутся гвоздями...

Ты опять ничего не понял. В CL есть эта защита. Потому что люди, пишущие стандарт, в отличии от тебя понимают, что она необходима. Но эта защита дырява. Это значит не то, что разработчики оставили какой-то специальный путь для обхода защиты, а то, что при помощи некоторых нетривиальных и неочевидных методов можно сделать так, что она навернется. И никак лучше ее не сделаешь - для этого надо переписывать макросистему с нуля. Другими словами, был реализован максимум, который можно сделать, не перекраивая стандарт. Ясно тебе?

Не доказано. Обоснование типа «гигиена по умолчанию» не принимается - давай отсутствие принципиальных возможностей - то, что невозможно сделать в CL и что может схема. И касательно языка (стандарта), а не отдельных его реализаций. А то и так уже «можно писать на схеме, а можно на Racket».

Я привел пример с локальным скопом. Реализовать такое для общелиспера - задача примерно на год (может можно ускорить, если использовать какие-то SBCL-фишки, я их не знаю). Можешь начинать. Насчет стандарта - тут неоднозначная ситуация. Де-факто стандарт схемы на данный момент - это Racket, а не r6rs. Но де-юре Racket даже не scheme.

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

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

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

Но где я об этом говорил? Может это был кто-то другой? :) Вообще костыль, да.

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

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

> Генсим не универсален

Скорей неудобен.

пекеджи дырявы.


Можно раскрыть тему проблем с packages? А то вы часто это повторяете, а я не могу понять о чём, собственно, речь.

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

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

;; где-то в начале
(define x 10)
....
;; тонны кода спустя
(define-syntax foo
  (syntax-rules ()
    ((foo)
     (let ((x 15)) ;; или (define (bar x)...
       (some-bloated-code)
       (display x))))) ;; я забыл снять гигиену

какую ошибку я получу?

То есть неизбежные проблемы с временем выполнения/компиляции - это порядки CL?

читай документацию - не будет проблем

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

Перестань хотеть чтобы бабушка стала дедушкой

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

И тут я захотел у себя, в своем проекте поменять let.

Но вы ведь меняете my-package:let, а не cl:let - как ваш let может помешать стандартному? (не говоря уж о том, что ваш прилично назвать другим именем, не let, а my-let, например). Так что сторонние макросы будут продолжать работать как работали.

Это дикость.

В CL это естесвенно - там let это примитив (компиляции в компиляторах). Это всё равно что говорить «мой друг, сишный программист, дал мне свою динамическую библиотеку, а я хочу полность изменить сalling convention на нечто экзотичное (мне так удобно) - но его динамическая библиотека перестаёт работать, так как использует обычные соглашаения о вызове, это дикость!». Со стандартным let ничего особого проделать нельзя - это не макрос, не функция, следовательно вы не сможете его переопределить (только можете полезть и переопределить его ir1 трансляторы - то чем он является в SBCL). Можно только обворачивать его в разные макросы, что часто можно видеть.

Генсим не универсален

Что дырявого может быть в функции :: &optional string -> symbol & ?

пекеджи дырявы.

Тоже непонятно почему.

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

Неудобны - вам.

Я привел пример с локальным скопом. Реализовать такое для общелиспера - задача примерно на год

Макросистема CL такая, какя есть - зачем её переиначивать на манер Scheme? Это абсолютно бессмысленная затея, ИМХО. Я к примеру, из-за малого знакомства с современным Scheme вообще не понимаю зачем это нужно - а вы не говорите, только намекая на то что это общеизвестно.

Макросистема CL - это подмножество макросистемы Racket.

Вот кстати, такое утверждение я уже слышал. И даже верю ему. Но вижу это немного иначе - макросы СД не подмножество, точнее они «меньше» но позволяют выстроить поведение аналогичное макросам PLT. Т.е. они более элементарны (топорны? что может быть проще s-exp -> s-exp преобразований общего вида? а что может быть более общим?)

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

Ну взять хотя бы in-package и intern (не говоря о всяких специфических плясках с eval и string->symbol), применение которых ничем не ограничено, что позволяет кому угодно насрать в какой угодно пекедж в какой угодно момент. Я, конечно, не отрицаю, может быть ситуация, в которой такое динамическое вмешательство полезно - но в этом случае будет логичным, если автор сам предусмотрит определенный интерфейс для взаимодействия.

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

>И тут я захотел у себя, в своем проекте поменять let. Ну удобно мне в силу каких-то причин, чтобы let работал не по стандарту, а как-то иначе. И ВНЕЗАПНО после этого переопределения все макросы перестают работать. Почему? Потому что в них есть let. И автором макросов предполагалось, что let работает строго определенным образом.

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

Это дикость.


Ещё раз - тебе запрещено даже заходить на кухню - отчекрыжишь себе яйца как только дотянешься до ножа...

В том-то и дело, что нет. В CL есть два способа, которыми можно попытаться ее обеспечить - генсим и пекеджи.


В том-то и дело, что да. А ты продолжаешь демонстрировать полное незнание CL. Что-то меня задалбывает рассказывать тебе, что ты нихера не знаешь...

Проблема-то в том, что гигиена нужна практически везде. Проще снимать ее в 10% мест, чем навешивать в 90%.


Единственное, с чем я с тобой [почти] соглашусь, что гигиена нужна чаще, чем её снятие. Но это такая мелочь... Я надеюсь у тебя в кармане ещё что-то есть?

(local-syntax-introduce syntax-object) выводит syntax-object из-под гигиены. Вызов одной единственной функции - офигенный танец с бубном.


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

Ты опять ничего не понял. В CL есть эта защита.


Ты о «дефолтном» импорте стандартных пакаджей и «почти запрете» на их переопределение? Это скорее мера предупреждения для таких дебилов как ты.

Но эта защита дырява.


это твоё сраное ИМХО, Хотя ладно, думай как хочешь, чего это я...

Я привел пример с локальным скопом. Реализовать такое для общелиспера - задача примерно на год (может можно ускорить, если использовать какие-то SBCL-фишки, я их не знаю).


Воспроизвести тот твой пример точь в точь на CL - ровно столько-же кода. Но я тебя учить не собираюсь.


Насчет стандарта - тут неоднозначная ситуация. Де-факто стандарт схемы на данный момент - это Racket, а не r6rs.


а для CL - SBCL по твоей дебильной логике?

Но де-юре Racket даже не scheme.


херасе, я с фантомом тут CL сравнивал... =)

Макросистема CL - это подмножество макросистемы Racket. Когда я смотрю из последней на первую - то мне очевидно, в чем она шире CL.


Исчо один нихера не читающий а только пишущий КО. Отрицать то, что «макросистемы Racket шире CL» может только макаронный монстр в твоём воспалённом сознании. Но для меня эти дополнения - несколько приятных фич, не более

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


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

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

А, вы просто не понимаете что есть package. Ну тогда ладно.

что позволяет кому угодно насрать в какой угодно пекедж в какой

угодно момент.



Штангисты, а также различные ценители анонимных пространств имён, разных private и т.п. вас, конечно, поймут. Но это ведь вообще характерно для всех динамических языков. Об этом можно говорить сравнивая, скажем, CL и Haskell, но никак не макросистемы CL и Racket.

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

какую ошибку я получу?

То есть я тебя вполне правильно понял, ты пишешь

(define x 10)
(let ([x 15])
  (display x))
А потом удивляешься, что х - внутренний, а не внешний? Ну извини, дружок, здесь все дело в скопе, а наличие/отсутствие гигиены не при делах, как я тебе в самом начале и сказал. Глупо ожидать, что гигиена будет отлавливать обычные ошибки в обычном (не макро) коде, не находишь?

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

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

Глупо ожидать что гигиена отловит _все_ ошибки - да. Но ты же клялся что что если я забуду снять гигиену - я получу ошибку. Я забыл. Про скоп помнил, и гигиену снять забыл - где ошибка?

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


Глупо утверждать, что не снятая гигиена _всегда_ даст ошибку - не находишь? =)

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

1) можешь не засовывать при интерпретации 2) а может сначала документацию почитать? а то ты, того и гляди, и минет захочешь от CL-я...

Отжёг.

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

Де-факто стандарт схемы на данный момент - это Racket, а не r6rs. Но де-юре Racket даже не scheme.

Смотрим. http://shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=racket&...

Неплохо, заслуживает внимания. Можно пару вопросов? 1. Есть ли в Racket инкрементная разработка такая же, как в CL (переопределение функции, включая изменение сигнатуры, в рантайме)? 2. Объём кода (в Мб) самого большого на сегодня приложения на racket?

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

И как обстоят дела в racket с пекеджами?

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

И ещё один вопрос. keyword arguments. Без них вообще никак нельзя жить.

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

И ещё один вопрос - что ты писал на CL и что ты писал на Racket, объём кода и продолжительность проектов?

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

Но вы ведь меняете my-package:let, а не cl:let - как ваш let может помешать стандартному? (не говоря уж о том, что ваш прилично назвать другим именем, не let, а my-let, например). Так что сторонние макросы будут продолжать работать как работали.

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

В CL это естесвенно - там let это примитив (компиляции в компиляторах).

Не знал, что в CL нельзя переопределять спецформы. Ну в любом случае, это не слишком принципиально, вместо let можно использовать любой часто-применяемый макрос-функцию. Что до примера с сишным программистом: все же возможность переопределить что угодно и при этом обладать гарантиями того, что все сторонние библиотеки будут работать, как надо, - это, как минимум, очень удобно, не находите?

Что дырявого может быть в функции :: &optional string -> symbol & ?

Неуниверсален в том смысле, что все символы макроса ты им не загигеенишь.

Макросистема CL такая, какя есть - зачем её переиначивать на манер Scheme?

Незачем переиначивать. Просто пример того, что хрен сделаешь в CL, не более того.

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

А понять, зачем оно, можно, только используя. Для человека, всю жизнь писавшего на си, макросистема CL тоже особых преимуществ на сишной иметь не будет, пока он ей не попользуется. Здесь вобщем-то нет смысла приводить никаких примеров, потому что они будут мигом объявлены или ненужными, или «оно может и лучше но по невнятными критериям, которые для меня и не критерии», или будет приведен костыльный аналог на CL, причем товарищу, что это костыль - ни чуть не поверит. Вот простенький примерчик - анафорический if. Если писать его на ракете, то согласно правилам хорошего стиля оно будет как-то так (кстати, безо всякого нарушения гигиены):

#lang racket
(require racket/stxparam)

(define-syntax-parameter it 
  (λ (stx) 
    (raise-syntax-error #f (format "missed context at ~a:~a" (syntax-line stx) (syntax-column stx)) stx)))

(define-syntax-rule (aif test then else)
  (let ([tmp test])
    (syntax-parameterize ([it (make-rename-transformer #`tmp)])
                         (if test then else))))
Для меня совершенно очевидно, что этот вариант лучше дефолтного CL-овского. И у меня нет никакого понятия, как это объяснить общелисперу, который считает наоборот.

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

о никак не макросистемы CL и Racket

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

anonymous
()
Ответ на: комментарий от yyk
Глупо утверждать, что не снятая гигиена _всегда_ даст ошибку - не находишь? =)

Конечно, если ошибка заключается не в гигиене, то неснятая гигиена ошибки не даст :) В данном случае все же ошибка в скопе - если уж ты хочешь обращаться из let-a к внешней переменной, то изволь назвать ее по-другому. Да, в данном случае можно обратиться к внешней переменной, сняв гигиену. Но это равносильно как:

(define x 5)
(let ([x 10]) (#%top . x)) ;обратились к внешнему иксу
(let ([x 10]) x) ;а вот тут забыли
Короче говоря - совершенно другой класс ошибок, все же.

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

Неплохо, заслуживает внимания.

Просмотрел несколько примеров - странно, в тестах совсем не используют #lang typed/racket

Интересно, оно совсем ничего не даёт в этих тестах или «писатели» не знают о нём/не умеют пользоваться?

И ещё о typed/racket - а в макросистеме как-то возможно этим пользоваться (допустим, делать полиморфные макросы =))?

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

> Нельзя даже значение переменной за-setить.
Т.е., прощай *print-level* ?

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

1. Есть ли в Racket инкрементная разработка такая же, как в CL (переопределение функции, включая изменение сигнатуры, в рантайме)? 2. Объём кода (в Мб) самого большого на сегодня приложения на racket?

1. Точно так же нельзя. Объяснять, как можно не буду, просто на примере покажу:

(define x 5) ; чтоб много не писать будет обычная переменная, разницы с функциями никакой, арность не важна.

(define x any) ; тут ошибка, нельзя дважды объявлять одно и то же
(let ([x any]) ...) ; так понятное дело можно
(let ([y any]) (define x any2) ...) ; так тоже можно
(set! x any) ; и так
ну и можно при желании сделать вместо define другую форму, которая будет сетить, если переменная bound и дефайнить, если она unbound. 2. Это сам Racket. Если хочется мегабайтов - можно скачать репозиторий.

И как обстоят дела в racket с пекеджами?

Есть дефолтная система модулей и юниты, если ее вдруг не хватило.

И ещё один вопрос. keyword arguments. Без них вообще никак нельзя жить.

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

Т.е., прощай *print-level* ?

Для таких вещей есть специальная штука - параметры.

anonymous
()
Ответ на: комментарий от yyk
Просмотрел несколько примеров - странно, в тестах совсем не используют #lang typed/racket

Интересно, оно совсем ничего не даёт в этих тестах или "писатели" не знают о нём/не умеют пользоваться?

Примеры очень старые, а typed racket относительно недавно допилена до вменяемого состояния и продолжает пилиться. Что до выигрыша - ну в производительности его не будет, typed racket полностью на макросах, так что только аннотации коду добавить. Единственно что проще будет со всякими извращениями типа unsafe-fxop - в typed racket они не нужны, это она умеет оптимизировать с 5.0.2.

И ещё о typed/racket - а в макросистеме как-то возможно этим пользоваться (допустим, делать полиморфные макросы =))?

Можно типизировать функции, которые потом используются в макросах, но сама define-syntax форма (и производные) никак не типизируется, поэтому смысла в этом мало - один хрен функции принимают и возвращают синтаксический объект. Хотя если обвязать все это структурами, то да - можно будет делать полиморфные в некотором смысле макросы :) Вообще, у создателей Racket где-то завалялась пара-тройка papers по модели типизации макросов, так что, наверное, чего-то можно в будущем ожидать.

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

Спасибо за ответ.

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

Не понял, «не нужны» в смысле «есть другой путь» или это - идеологема?
У меня половина макросов на лиспе с keyword аргументами.

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

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

Точно так же нельзя.

В лиспе, когда я вижу в теле функции ошибку, я иду в исходник, меняю это тело и нажимаю C-c C-c. После этого (не перезагружая программы) у меня уже новая функция (если не особо жёстко работает оптимизатор). Есть ли подобная возможность в racket, технологически столь же простая? Пусть за сценой происходит set!, мне по барабану. Главное, чтобы такая возможность была, т.к. только это и есть динамическая разработка, а всё остальное - это пародия на неё. (Только не надо объяснять, что это плохо и ненужно, т.к. я этим уже не первый год интенсивно пользуюсь. Кроме лиспа, есть ещё, как минимум, MS SQL Server, который тоже так может. Со строгой типизацией, можно менять сигнатуру функции, несмотря на наличие ссылок на неё, всё это безопасно. И этим я тоже интенсивно пользовался в своё время).

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

>Что до выигрыша - ну в производительности его не будет, typed racket полностью на макросах, так что только аннотации коду добавить.

Не вкурил: что к чему и когда добавлять. Посему [возможно дурацкие] вопросы:

- обычный код аннотировать типами нельзя?

- почему указание типа не увеличит скорость - проверки на соответствие типов всё равно будут на каждом шагу?

- я не именно про define-syntax, я про возможность достать (откуда?) тип для синтаксического объекта (ну да, внутри define-syntax =)).

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

>Есть ли подобная возможность в racket, технологически столь же простая?

Щас те скажут «Не надо делать из нашей уютненькой чистенькой гигиеничненькой схемы ваш страшный грязный толстый CL!» =)

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