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

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

Если не использовать встроенную систему модулей - можно. Если использовать - нельзя.

За блог спасибо. Из него ясно видно, что язык заточен под студентов, а не под профессионалов. А это - совершенно разные целевые аудитории. Я, например, уже давным-давно не студент и зачем мне нужны учебные языки? Мне нужны промышленные.

Что называется «смотрю в книгу - вижу фигу». Там написано, что подобный метод разработки не имеет никаких объективных преимуществ (разве что наоборот), и его выбор может быть обусловлен разве что привычкой и личными предпочтениями.

1. Язык - учебный, а не промышленный.

Не верно.

2. Исходя из этого, с точки зрения технологичности при профессиональной работе не даёт ничего по сравнению с cl.

А cl не дает ничего по сравнению с racket.

3. Макросы в cl, если уметь ими пользоваться, достаточно удобны и безопасны. Да, возможно,у racket есть какие-то плюсы, но наверняка макросистему racket можно воспроизвести в рамках CL.

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

Единственное реальное (а не академическое) преимущество, которое я увидел за макросистемой racket - это возможность отследить исходный текст, в лиспе это не предусмотрено.

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

Вопросы гигиены легко решаются с помощью конструкций типа with-gensyms, их просто не нужно забывать ставить.

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

Макросов не настолько много, чтобы это было обузой.

То есть стиль программирования на cl не предполагает использование большого количества макросов. Стиль программирования на racket, обратно, построен на использовании макросов, стимулирует к их написанию и вообще заточен под написание #lang any дсл-ей.

Второе (косвенное) преимущество - писать макросы в racket тяжелее, а значит, к ним будут реже прибегать и можно ожидать, что они будут более продуманы.

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

4. JIT компиляция - это вообще плохо

А полная непереносимость вещей, написанных на cl - это очень хорошо, надо полагать? Или образы размером 100 метров?

Даже с JIT, racket медленнее SBCL.

Безоснавательно. И не надо приводить в пример детские бенчмарки шутаута, я лично видел другие столь же детские бенчмарки, на которых SBCL с ТАКОЙ-ТО компиляцией в натив умудрялся отсасывать в 10 раз.

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

А зачем он нужен, извиняюсь? CL такое говно, что приходится разглядывать дизасемблированные листинги, чтобы разобраться в «а почему здесь сегфолт» или «с какого черта этот код тормозит»?

Что касается вообще типизированного программирования, то оно возможно и в лиспе. Да, нет такого сахара с встроенными декларациями типов, но это можно исправить в рамках CL тем же способом (макросами).

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

И ты не ответил на ещё один вопрос - каков максимальный размер проекта под Racket (в мегабайтах исходного текста)?

Откуда я могу это знать? Я гарантирую, что и ты не знаешь этого числа для cl. А меряться размером догадок не вижу смысла.

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

> Там написано, что подобный метод разработки
Там написано, что он пришёл к этому выводу, глядя на студентов, а не на профессионалов.

Откуда я могу это знать? Я гарантирую, что и ты не знаешь этого числа для cl. А меряться размером догадок не вижу смысла.


Я думал, ты догадаешься,что имелось в виду не «предельный с технической точки зрения», а «реально осуществлённый». Вот, например,
Acl или Stella (строго типизированный Common Lisp, который уже давно написан) http://www.isi.edu/isd/LOOM/Stella/ - имеют исходники порядка 7мб. Я не думаю, что это предел, но это - самые большие проекты,которые мне встретились. Есть ли проекты подобного масштаба на Racket?

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

Я думал, ты догадаешься,что имелось в виду не «предельный с технической точки зрения», а «реально осуществлённый».

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

Вот, например, Acl или Stella (строго типизированный Common Lisp, который уже давно написан)

Насколько Typed Racket выглядит недоделанным, но даже по сравнению с ним Stella смотрится кривой студенческой поделкой. No offence - просто констатация факта. Там написано, что проект занимает около 100к строк - ну вот 30к+ строк точно есть - например, новая гуйня для Racket, или тот же http://fprog.ru/2009/issue2/alex-ott-using-scheme-in-dozor-jet/ Кстати, а реализацию всего того, что входит в стандарт Racket можно считать? Там метров 40 свободно наберется.

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

Там написано, что он пришёл к этому выводу, глядя на студентов, а не на профессионалов.

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

In the presence of macros and higher-order functions and other beasts, it is difficult for masters of the universe with 30 years of experience to keep track of things.

Is it really such a deep principle of computing to create the objects incrementally in the repl as opposed to thinking systematically through the design of a program?

I decided not and asked Robby to make DrScheme's repl transparent. That is, it re-starts the repl and re-loads the buffer every time. I consider this behavior a suitable compromise: have a repl but don't confuse yourself with send-defs and send-exprs. This is especially true in an age when sending an entire buffer takes as much time as sending an individual expression or definition.

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

Even though I had used the incremental mode for more than a decade when I switched from Emacs to DrScheme in 1998, I have hardly ever looked back. I miss a few other things but the incremental repl is one of those rituals old Lispers acquired and never questioned ... but it isn't fundamental and critical to anything.

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

> Макросистема racket сложнее (ее труднее понять и изучить, как она работает), но писать макросы проще в racket

но писать макросы проще в racket

А можно для меня, слоупока, привести примеры

Да и проясните мне следующее:

1.Можно ли в макросах схемы делать variable capture?

2.Можно ли генерировать код циклически?

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

> Я не думаю, что каждый, кто решил начать разработку проприетарного проекта на Common Lisp, отчитывается лично перед тобой, не так ли? Я именно это имел в виду.

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

No offence - просто констатация факта

А по-моему, здесь как раз идёт offence, без всякого факта (факт не предъявлен, дана негативная оценка и даже никаких аргументов). Однако приятно, что и

Typed Racket выглядит недоделанным

Здорово, прямо бриллиант в потоке пропаганды :-)

Кстати, а реализацию всего того, что входит в стандарт Racket можно > считать? Там метров 40 свободно наберется.

Я думаю, там такие же 40 метров, как и сферические образы лиспа по 100 метров в вакууме и виртуальные (без пруфлинка) бенчмарки, где лисп якобы сливает. Реализации лиспа (ccl,sbcl) имеют размер исходников порядка 10 мб, включая, естественно, компилятор. Не думаю, что в racket что-то существенно большее, учитывая, что стандарт Scheme (не знаю про racket) намного компактнее, чем стандарт CL. Но как тебе будет угодно. Устал я от тебя. Мне racket не нужен, задавать тебе вопросы - это давать повод для очередной порции твоей гнилой пропаганды. Если бы вдруг racket мне понадобился - посчитать самому будет быстрее, чем разговаривать с тем, кто понимает вопросы с пятого раза.

Так что будь здоров и старайся не завираться.

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

А можно для меня, слоупока, привести примеры

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

1.Можно ли в макросах схемы делать variable capture?

Можно обойти гигиену. Но это будет плохим стилем, вообще специально для этих целей предназначены syntax parameters, я приводил тут пример с aif. Этим и отличается система racket - там для многих вещей есть удобные встроенные батарейки. Здесь польза даже не только в том, что упрощается написание - но проще понять во что будет раскрыт макрос, если это макрос вам незнаком. В CL макрос - это просто ф-я, которая берет список и делает другой список и нет никакого способа узнать, что в результате будет, не проследив за логикой работы этой функции. В racket за счет паттерн-матчинга и средств, предназначенных для решения частных проблем того или иного рода, макросы получаются намного более декларативными, что ли. Да, еще забыл упомянуть одну важную вещь, в Racket макросы - это first-class object. То есть макрос - это не что иное как обычная лямбда, и с этой лямбдой можно работать, как с обычной лямбдой. Например:

(define-syntax (macro stx) *код макроса*) ; объявляем макрос
(define-for-syntax (macro stx) *код функции*) ; объявляем функцию первой фазы - грубо говоря для компайл-тайма, биндинги в разных фазах раздельны, поэтому конфликта имен тут нет
(define-syntax macro1 (syntax-local-value #`macro)) ; поскольку биндинг макроса существует в 0 фазе, используем специальную ф-ю, для того, чтобы получить его значение в первой
(define-syntax macro2 macro) ; тут с биндингами все нормально и так
в CL есть что-то подобное?

2.Можно ли генерировать код циклически?

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

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

Знаешь, чтобы воспринимать вопросы таким образом

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

А по-моему, здесь как раз идёт offence, без всякого факта (факт не предъявлен, дана негативная оценка и даже никаких аргументов)

Конечно, трудно говорить, учитывая, что документации фактически нет, но, как я понял, статическая типизация там введена за счет введения статических классов? Может, технологически проект и хорош, да с такой-то кодогенерацией во что хочешь на выбор, но сам получившийся язык выходит каким-то хероватеньким тогда. Ни тебе алгебраических типов, ни паттерн-матчинга, ни параметрического полиморфизма. Кому нужна такая «статическая типизация»? Я считаю CLOS лучше, намного лучше Ну и что, что он статически не чекается? Зато возможности значительно шире, а с точки зрения надежности статическое ООП не особо то и дает выигрыша. Кроме того, не знаю чего там на счет лямбд, как они типизируются? Тоже, как я понял, крайне хило. Я, конечно, могу быть не прав (как уже сказал - документации там нихрена, немудрено ошибиться), если это так и перечисленные мной вещи там действительно есть - приношу свои извинения.

Здорово, прямо бриллиант в потоке пропаганды :-)

Как есть, так и говорю. Если Typed Racket недопилен, то он недопилен.

Я думаю, там такие же 40 метров

Это может проверить любой - достаточно скачать исходники и посмотреть размер папки «collects» - это и есть библиотеки racket со всеми описанными в доках #lang и так далее. Но, конечно, лучше безосновательно заявлять, что я вру, лол. Вот, например, размер папки «compiler» - 1.05 mb, drracket - 1.23 mb, racket - 1.64 mb, web-server - 0.743, это так, на что глаз упал из самого основного. Всего - 41,3 мб.

как и сферические образы лиспа по 100 метров в вакууме

Это я, вообще говоря, знаю с чужих слов. Но верю вполне.

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

Там вообще была элементарная однострочная задачка на построение обычного списка. Как я думаю, CL соснул на своей реализации длинной арифметеки (больше негде было просто). Получилось, что Racket работала где-то в ~3 раза медленнее сишной реализации с gmp, CL раз в 7 медленнее Racket. Могу, если угодно, таки вспомнить задачку и предоставить пруф. Это, вобщем-то, все было не к тому, что Racket быстрее CL (понятно, что медленнее, но для задач, в которых оно используется, незначительно), а к тому, все эти детские бенчмарки не показывают реальной производительности.

Так что будь здоров и старайся не завираться.

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

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

в CL есть что-то подобное?

;;; Объявляем макрос.
;;;
(defmacro aif (test then &optional else)
  `(let ((it ,test))
     (declare (ignorable it))
     (format t "I'am expand, macro.")
     (if it ,then ,else)))

;;; В CL макросы всегда compile-time. Объявляем макрос компиляции, он тут тоже для функций
;;; и конфликта имён нет.
;;;
(define-compiler-macro aif (test then else)
  `(let ((it ,test))
     (declare (ignorable it))
     (format t "I'am expand, compiler-macro.")
     (if it ,then ,else)))

;;; Но в данном случае не правильно давать одинаковые имена - макросы это макросы,
;;; а макросы компиляции помогают функциям, позволяя раскрывать что-то в них ещё на
;;; стадии compile-time.

(aif (numberp 1) it 'foo)
;=> I'am expand, macro.
;=> T

;;; Макрос это функция
;;;
(macro-function 'aif)
;=> #<FUNCTION (MACRO-FUNCTION AIF) {B754AA5}>

;;; Такая же как и lambda
;;;
(type-of (macro-function 'aif2))
;=> FUNCTION
(type-of (lambda ()))
;=> FUNCTION

;;; Присваиваем значение макроса другому макросу
;;;
(setf (macro-function 'aif2) (macro-function 'aif))

(aif2 (numberp 1) it 'foo)
;=> I'am expand, macro.
;=> T

Это всё есть в стандарте. В тоже время рэкет не стандарт. Если я начну говорить про нестандарт - про SBCL и про то сколько там стадий трансляции и сколько форм делающий каждую из стадий трансляции user-driven - рэкету станет плохо :)

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

(type-of (macro-function 'aif2))

тут `2' лишняя, всё интерактивность REPL-а, которая не нужна :)

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

К слову

(funcall
  (macro-function 'aif)       ;; обычная функция связанная с именем макроса
  '(aif (numberp 1) it 'foo)  ;; принимает код
  nil)                        ;; окружение

                              ;; возвращает код:

(LET ((IT (NUMBERP 1)))
  (DECLARE (IGNORABLE IT))
  (FORMAT T "I'am expand, macro.")
  (IF IT
      IT
      'FOO))
quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Это всё есть в стандарте. В тоже время рэкет не стандарт.

Не стандарт чего? Scheme? Так Racket - не Scheme, с какой же стати она должна быть ее стандартом?

Если я начну говорить про нестандарт - про SBCL и про то сколько там стадий трансляции и сколько форм делающий каждую из стадий трансляции user-driven - рэкету станет плохо :)

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

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

Можно обойти гигиену

Хм, значит те-же яйца вид сбоку.

Под этим можно понимать все, что угодно

Я имел в виду что-то вроде такого гипотетического кода

(defmacro n-times (n &body body) 
  (let ((r (list 'progn))) 
    (loop for i from 1 to n do (setf r (append r body))) 
    r))
no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Хм, значит те-же яйца вид сбоку.

В каком смысле?

Я имел в виду что-то вроде такого гипотетического кода

Можно, конечно.

(define-syntax (n-times stx)
  (syntax-parse stx
         [(_ n:nat body:expr) (with-syntax ([(bodies ...)
                                             (for/list ([i (in-range (syntax->datum #`n))])
                                               #`body)])
                                #`(begin bodies ...))]))

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

Можно, конечно

Welcome to Racket v5.0.2.
> (define-syntax (n-times stx)
  (syntax-parse stx
         [(_ n:nat body:expr) (with-syntax ([(bodies ...)
                                             (for/list ([i (in-range (syntax->datum #`n))])
                                               #`body)])
                                #`(begin bodies ...))]))
stdin::59: _: wildcard not allowed as an expression in: (_ n:nat body:expr)

 === context ===
/usr/lib/racket/collects/racket/private/misc.rkt:74:7

Макросы в схеме такие простые и понятные...

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

Потому что нефиг в репл совать то, что туда совать не надо, выражения в repl это топ-левел, а не module-context, по-этому они обрабатываются по-другому, и макросы тут совершенно не при делах. Если бы ты сделал вот так: [code] Добро пожаловать в DrRacket, версия 5.0.2 [3m]. Язык: racket [выбранный].

(module m racket

(define-syntax (n-times stx) (syntax-parse stx [(_ n:nat body:expr) (with-syntax ([(bodies ...) (for/list ([i (in-range (syntax->datum #`n))]) #`body)]) #`(begin bodies ...))])))

compile: unbound identifier in module (in phase 1, transformer environment) in: syntax-parse [/code] или вот так: [code] #lang racket (define-syntax (n-times stx) (syntax-parse stx [(_ n:nat body:expr) (with-syntax ([(bodies ...) (for/list ([i (in-range (syntax->datum #`n))]) #`body)]) #`(begin bodies ...))])) [/code] то сразу было бы понятно, в чем ошибка, и решается она так: [code] Добро пожаловать в DrRacket, версия 5.0.2 [3m]. Язык: racket [выбранный].

(require (for-syntax syntax/parse))
(define-syntax (n-times stx)

(syntax-parse stx [(_ n:nat body:expr) (with-syntax ([(bodies ...) (for/list ([i (in-range (syntax->datum #`n))]) #`body)]) #`(begin bodies ...))]))

(n-times 4 (display 1))

1111

(n-times -1 (display 1))

. . n-times: expected exact-nonnegative-integer in: -1 [/code]

[quote] Макросы в схеме такие простые и понятные... [/quote] Я не говорил, что они простые и понятные, я говорил, что их проще писать, если ты в них разобрался.

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

как же задолбала эта разметка

Потому что нефиг в репл совать то, что туда совать не надо, выражения в repl это топ-левел, а не module-context, по-этому они обрабатываются по-другому, и макросы тут совершенно не при делах. Если бы ты сделал вот так:

Добро пожаловать в DrRacket, версия 5.0.2 [3m].
Язык: racket [выбранный].
> (module m racket 
    (define-syntax (n-times stx) 
      (syntax-parse stx 
                    [(_ n:nat body:expr) (with-syntax ([(bodies ...) 
                                                        (for/list ([i (in-range (syntax->datum #`n))]) #`body)]) 
                                           #`(begin bodies ...))])))

. compile: unbound identifier in module (in phase 1, transformer environment) in: syntax-parse

или вот так:

#lang racket
(define-syntax (n-times stx)
  (syntax-parse stx
         [(_ n:nat body:expr) (with-syntax ([(bodies ...)
                                             (for/list ([i (in-range (syntax->datum #`n))])
                                               #`body)])
                                #`(begin bodies ...))]))

то сразу было бы понятно, в чем ошибка, и решается она так:

 
Добро пожаловать в DrRacket, версия 5.0.2 [3m].
Язык: racket [выбранный].
> (require (for-syntax syntax/parse))
> (define-syntax (n-times stx)
  (syntax-parse stx
         [(_ n:nat body:expr) (with-syntax ([(bodies ...)
                                             (for/list ([i (in-range (syntax->datum #`n))])
                                               #`body)])
                                #`(begin bodies ...))]))
> (n-times 4 (display 1))
1111
> (n-times -1 (display 1))
. . n-times: expected exact-nonnegative-integer in: -1

Макросы в схеме такие простые и понятные...

Я не говорил, что они простые и понятные, я говорил, что их проще писать, если ты в них разобрался.

anonymous
()
Ответ на: комментарий от anonymous
. n-times: expected exact-nonnegative-integer in: -1

Щито?

> (begin (n-times -1 (display 1)) (n-times 2 (display 2)))

. n-times: expected exact-nonnegative-integer in: -1

Не то что нужно, должно быть так

CL-USER> (progn (n-times -1 (print 1)) (n-times 2 (print 2)))

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

Щито?

Что «щито»? Все правильно - очевидно, что n должно быть неотрицательным целым, иначе эррор во время экспанда.

Не то что нужно, должно быть так

Если кому-то лень сделать обработку ошибок - это его проблемы, для любого нормального человека очевидно, что запись (n-times -1 body) - некорректна. Или что она значит, по-вашему? Выполнить форму body -1 раз? Это как?

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

> очевидно, что n должно быть неотрицательным целым иначе эррор во время экспанда

Где вы это нашли в моем примере? В моем примере на CL никаких эрроров отрицательный параметр не вызывает

Или что она значит, по-вашему? Выполнить форму body -1 раз? Это как?

В любом нормальном языке это значит, что цикл не выполняется ни разу. Молча. Без эрроров. А то вы не знали?

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

Где вы это нашли в моем примере? В моем примере на CL никаких эрроров отрицательный параметр не вызывает

Потому что кое-кому лень сделать обработку ошибок, да. Если туда засунуть переменную вместо функции или нецелое число, например, то тоже никаких ошибок не будет. Точнее будет что-нибудь невразумительное.

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

В любом нормальном языке будет ошибка, потому что нормальный язык предполагает предсказуемость работы функций. В данном случае семантика конструкции такова, что нету никаких причин выполнять при вводе отрицательного числа форму 0 раз. Почему не 100? или не 34348973895? или не 42, например? Почему 0? Если уж так хочется - ну поменяй n:nat на n:integer, только зачем делать кривой костыль из полноценного решения? Хотя я же забыл, это путь cl - писать вместо решения костыли.

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

Насколько Typed Racket выглядит недоделанным, но даже по сравнению с ним Stella смотрится кривой студенческой поделкой. No offence - просто констатация факта. Там написано, что проект занимает около 100к строк - ну вот 30к+ строк точно есть -

многовато будет, не? лучше взять и собрать схему в llvm (<1000 строк), после чего накрутить какие-то типизации поверх этого llvm

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

> сферические образы лиспа по 100 метров в вакууме и виртуальные (без пруфлинка) бенчмарки, где лисп якобы сливает. Реализации лиспа (ccl,sbcl) имеют размер исходников порядка 10 мб, включая, естественно, компилятор.

для статистики: cредний размер минимального рантайма с хелло-вордом.
ECL ~2.5M, не Common Lisp: ISLISP OpenLisp 800кб, вроде EuLisp https://github.com/Henry/EuLisp должен быть ещё меньше, ближе к схеме. goo ~200 Кб/800Кб, Схемы разные в среднем метров 5, хотя сильно зависит от конфигурации.
Особом ынтерпрайзом является jazz scheme с IDE Jedi — взяли Gambit, доработали язык в сторону большей модульности и ООП, сделали на нём IDE вроде Eclipse. Транслируется как и Gambit, через Си. Собранное с исходниками (то ли 20, то ли 70М), либами и бинарниками заняло у меня около 920М. Много, зато мы можем писать кроссплатформенные GUI хелловорды с юнит-тестами, проектами и репозиториями (так у jazzscheme обзываются обычные модули).
STELLA собранная тоже довольно много весит.
//другой anonymous

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

Спасибо. Размер образа (исходников - не знаю) Lispworks Personal вместе с гуем - около 20мб. Гуй включает в себя отладчик уровня исходного текста, естественно, компилятор, профайлер, инспектор, построитель графов ссылок и дерева вызовов в профайлере, текстовый редактор а-ля емакс. Также включает библиотеку видгетов, но не включает построитель интерфейсов (он - только в профи версии).

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

Конечно, трудно говорить, учитывая, что документации фактически нет, но, как я понял, статическая типизация там введена за счет введения статических классов? Может, технологически проект и хорош, да с такой-то кодогенерацией во что хочешь на выбор, но сам получившийся язык выходит каким-то хероватеньким тогда. Ни тебе алгебраических типов, ни паттерн-матчинга, ни параметрического полиморфизма. Кому нужна такая «статическая типизация»? Я считаю CLOS лучше, намного лучше Ну и что, что он статически не чекается? Зато возможности значительно шире, а с точки зрения надежности статическое ООП не особо то и дает выигрыша. Кроме того, не знаю чего там на счет лямбд, как они типизируются? Тоже, как я понял, крайне хило. Я, конечно, могу быть не прав (как уже сказал - документации там нихрена, немудрено ошибиться)

если ты сейчас про STELLA спросил — то в раёне STELLA или PowerLoom есть какая-то презентация с профитами. Коротко говоря, из динамического Common Lisp выкинули всю динамичность, которую трудно транслировать в Java или C++: СLOS и generic методы, множественное наследование, добавили виртуальных функций и статической типизации с выводом (там пример где-то был, когда для STELLA с её аннотациями пришлось писать меньше букв, чем для CL). То есть, выкинули из CL всё, что является CL, а не C++ или Явой.

Ни тебе алгебраических типов, ни паттерн-матчинга, ни параметрического полиморфизма.

да, в этом плане это не самый интересный проект. Например, тот же goo или какой-то недолисп вроде него или eulisp — более продвинут с точки зрения полиморфизма, есть обобщённые методы (ещё какой-то недолисп вроде goo был с презентацией «полиморфизм и фичи языка vs. паттернов GoF» — в goo добавили специальные фичи, упрощающие паттерны)

Проект в духе «как из лиспа сделать С++». Второй проект в таком духе — Dylan — «как из лиспа сделать алгол», но в нём есть по сути 2 достижения: макросы D-exprs — это примерно как синтаксические объекты в схеме, AST макросы, где параметрам можно указать тип: выражение, функция, константа, переменная и т.п. и «как сократить Common Lisp рантайм, разбив его на отдельные dll-ки». Сократить не сократили значительно (в одном компиляторе 20М рантайма и много батареек, в другом 1М и батареек мало), но хоть попытались.

Я считаю CLOS лучше, намного лучше Ну и что, что он статически не чекается? Зато возможности значительно шире, а с точки зрения надежности статическое ООП не особо то и дает выигрыша.

в какой-то презентации про STELLA делалось заявление: «применение ООП в духе С++, с VTable и без множественного наследования вместо мультиметодов, обобщённых методов, MOP и т.п. позволило ускорить объектную систему раз в 10». Бенчмарков с исходниками вроде бы не видел.

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

OpenDylan — 20 мб. Есть гуй, IDE без подсветки/раскраски кода (клон Emacs Deuce), отладчик с реплом (можно запущенную программу поставить на паузу и покомандовать реплом), профилёр, инспектор для отладчика, что-то типа проект-менеджера и системы контроля версий уровня SourceSafe, но убогое.
Компилирует, транслируя программу в Си, собирает Си компилятором. Модули-пакеты в смысле cl, cтроится набор библиотек с рантаймом (разделяемых библиотек в смысле си, одна или несколько библиотек — один или несколько лисп-пакетов). Рантаймы разные у компилятора и запущенной программы, плюс ещё дебаг/релиз версии. В батарейках есть ODBC, гуй DUIM вроде McCLIM (порт), являющийся родным Win32 GUI (бекпортирован назад на GTK2). Библиотека виджетов, билдера нет , но есть что-то убогое в примерах (не очень нужен, т.к. макросами сделаны лейауты, контейнеры, и его вполне можно писать руками). Макросистема менее гибкая, чем в CL/Scheme, но более гибкая, чем в том же M4 (шаблоны, где подставляемые места — типизированные объекты куска AST, т.е., для макросов работает проверка типов).

Кстати, визуально дока на OpenDylan/ Functional Objects Dylan/Harlequin Dylan и на LispWorks/Lucid Common Lisp похоже оформлена. Такое ощущение, что когда-то Harlequin был подразделением LispWorks.

Gwydion Dylan — 1 Мб собранные бинарники/3 Мб исходники. Компилирует, транслируя d2c из Dylan в Cи, собирает gcc. Минимальный рантайм минимальнее, около 1М. Батареек в среднем меньше, хотя что-то спортировано из OpenDylan, для чего-то написаны биндинги из Си.

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

Правда, в OpenDylan компилятор командной строки иногда на ровном месте падает. В остальном, IDE немного похоже на Дельфи, только панели готовых компонентов не хватает :)
в cvs что-то пилят про type inference (predicated dispatch) и llvm, но как-то не очень активно — сообщество исчисляется десятком человек.
не хватает раскраски кода, так что приходится в емаксе сидеть :) хотя в принципе не сложно прикрутить расцветку к Deuce

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

многовато будет, не? лучше взять и собрать схему в llvm (<1000 строк), после чего накрутить какие-то типизации поверх этого llvm

1. и зачем нужна такая игрушечная схема? 2. проще накрутить типизации поверх схемы.

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

> Почему 0

Именно столько итераций нужно сделать, чтобы прибавляя к нулю по 1 сделать переменную меньше нуля.

Если уж так хочется - ну поменяй n:nat на n:integer

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

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

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

Именно столько итераций нужно сделать, чтобы прибавляя к нулю по 1 сделать переменную меньше нуля.

Так ведь в том-то и дело, что там никто к нулю по 1 не прибавляет. Логично, что (n-times n ...) выполняет форму n раз, а что к чему прибавлять - это уже особенности реализации. Как будет работать макрос с отрицательным n сразу неясно (вдруг там наоборот итерация от n до нуля с проверкой zero?, тогда если отрицательное число апихнуть, то оно до бесконечности работать будет), значит надо эти особенности работы отражать в документации. А можно вместо этого просто кинуть ошибку и все.

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

> в какой-то презентации про STELLA делалось заявление: «применение ООП в духе С++, с VTable и без множественного наследования вместо мультиметодов, обобщённых методов, MOP и т.п. позволило ускорить объектную систему раз в 10». Бенчмарков с исходниками вроде бы не видел.

Объектные системы без MOP - жуткие, уродливые кастраты.

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

Не стандарт чего? Scheme? Так Racket - не Scheme, с какой же стати она должна быть ее стандартом?

Это реализация - она реализует R5RS/R6RS и SRFI библиотеками, также как SBCL реализует ANSI CL. Если вы говорите о том какой рэкет навороченный и спрашиваете «а в CL так можно?» то это не совсем правильно, резонно будет спросить «а в какой реализации CL так можно?».

Не станет - в Racket столько фаз, сколько захочешь, то есть заведомо больше любого конечного числа :)

Я про реальные фазы, а не про измышления. (Текст -> AST) это реальная фаза, можно в Racket привязать к произвольному знаку в тексте пользовательскую процедуру чтения которая будет читать текст начиная с этого знака и возвращать AST, передавая управление назад в процедуру чтения? (AST -> AST) для макросов это реальная фаза, (AST -> AST) для функций тоже, можно в Racket привязывать пользовательские процедуры трансляции независимо для этих двух фаз? (AST -> Flow Graph) тоже реальная фаза, могу я в Racket опираться на информацию о типах (которая есть в Flow Graph) при написании «макросов» (это другие процедуры трансляции, но аналогичны макросам) при чём на информацию о типах времени компиляции, конечно, не в runtime; или есть возможность определять трансляцию в Flow Graph? (Flow Graph -> VOPs), (VOPs -> Asm) и т.п. Опуская детали - есть ли возможность прямо в REPL регулировать интерактивно все стадии компиляции вплоть до того чтобы менять тот машинный код (ассемблерные инструкции) который генерируется; без всяких пересборок реализации. Насколько я помню, ядро Racket вообще реализовано на си, а не на Racket (или ошибаюсь?). Единственное что нельзя в SBCL делать без пересборок - вводить новые примитивы (объекты) и менять поведение GC (первое нельзя т.к. при введении новых примитивных объектов нужно менять GC).

Вот, но я конечно утрирую - если возникают такие вопросы лучше сразу смотреть первоисточники (мне про Racket, а вам про CL, а то у вас неверные и предвзятые о нём представления).

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

Это реализация - она реализует R5RS/R6RS и SRFI библиотеками

Еще раз, Racket - НЕ реализация scheme. Или, в крайнем случае, реализация НЕ ТОЛЬКО scheme. Да, можно запустить схемокод в #lang r6rs, например, но #lang racket, который основной - это НЕ scheme и core - это тоже НЕ scheme. Правильнее будет говорить, что Racket - это фреймворк. Как дотнет, например. Под ним хостится много языков, среди которых есть Scheme и, ВНЕЗАПНО, Racket. Есть такой язык Scheme, у него есть много реализаций, в том числе есть реализация на Racket. А есть такой язык Racket, у него только одна реализация :) Rогда идет речь о Racket то имеется в виду по умолчанию не реализация Scheme для Racket, а реализация Racket для Racket, стандарт которого увидеть вот тут: http://docs.racket-lang.org/reference/index.html

Если вы говорите о том какой рэкет навороченный и спрашиваете «а в CL так можно?» то это не совсем правильно, резонно будет спросить «а в какой реализации CL так можно?».

Нет смысла говорить о том, что «вот эта фича есть в той реализации, а та - в этой», что толку, если эти фичи в разных местах? Я говорю о конкретной реализации Racket (понятно какой) и конкретной реализации CL (SBCL).

Я про реальные фазы, а не про измышления.

Так и я тоже. Процесс экспанда в Racket идет по фазам, от старшей к младшей, пока старшая не раскроется к младшей не переходим. Сколько хотите таких фаз сделать - столько и будет.

(Текст -> AST) это реальная фаза, можно в Racket привязать к произвольному знаку в тексте пользовательскую процедуру чтения которая будет читать текст начиная с этого знака и возвращать AST, передавая управление назад в процедуру чтения?

Переопределить ридер? Конечно можно, именно для этого #lang и сделан, вобщем-то.

(AST -> AST) для макросов это реальная фаза, (AST -> AST) для функций тоже, можно в Racket привязывать пользовательские процедуры трансляции независимо для этих двух фаз?

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

(AST -> Flow Graph) тоже реальная фаза, могу я в Racket опираться на информацию о типах (которая есть в Flow Graph) при написании «макросов» (это другие процедуры трансляции, но аналогичны макросам) при чём на информацию о типах времени компиляции, конечно, не в runtime; или есть возможность определять трансляцию в Flow Graph? (Flow Graph -> VOPs), (VOPs -> Asm) и т.п.

compilation-handler и evaluation-handler вполне доступны. Информация о типах появляется, как я понимаю, только во время выполнения (хотя тут могу и ошибаться), ну а требовать настройки тех фаз, которых просто физически нет, по-моему, несколько глупо. Хотя, конечно, вам никто не мешает такие фазы ввести - например, ловите evaluation-handler, декомпилируете его байткод, парсите, выполняете преобразования, которые хотели над ним сделать, компилируете, суете в дефолтный хендлер.

Опуская детали - есть ли возможность прямо в REPL регулировать интерактивно все стадии компиляции вплоть до того чтобы менять тот машинный код (ассемблерные инструкции) который генерируется; без всяких пересборок реализации.

Можно. Но не нужно :)

Насколько я помню, ядро Racket вообще реализовано на си, а не на Racket (или ошибаюсь?).

На си написана vm (что логично - Racket компилируется в байткод, который должен крутиться в vm Racket), все остальное на Racket. Надеюсь, не будет претензий к тому, что нельзя интерактивно, из репла переписать vm? А то мне недолго предложить из cl-овского репла перепрошить цп. Интерактивно, в рантайме, без перезагрузки и пересборки :) Вообще, тут уже глупо что-то сравнивать, все-таки компиляция в натив и в байткод для вм вещи чересчур разные.

anonymous
()
Ответ на: комментарий от quasimoto
Единственное что нельзя в SBCL делать без пересборок - вводить новые примитивы (объекты) и менять поведение GC (первое нельзя т.к. при введении новых примитивных объектов нужно менять GC).

Насчет примитивов и gc - для этого надо переписать evaluation-handler, но что с ним реально сделать на практике, так это подхватить байткод который туда летит, что-то с ним сделать, а потом отправить результат обратно на стандартный хендлер. Если именно сам хендлер переписывать - (чтобы добавить примитивы, например), то это по сути будет переписыванием vm. Хотя, кстати, выключать репл будет необязательно даже в этом случае. Вообще сейчас проверил в репл это и все работает! Я даже неожидал, если честно :)

Добро пожаловать в DrRacket, версия 5.0.2 [3m].
Язык: racket [выбранный].
> (define c (current-compile))
> c
#<procedure:default-compile-handler>
> (c #`(+ 1 2))
default-compile-handler: expects 2 arguments, given 1: #<syntax:10:7 (+ 1 2)>
> (c #`(+ 1 2) #f)
*тут всякие неотображаемые кракозябры*
> (current-compile (λ (x y) (begin (display 1) (c x y))))
> (+ 1 2)
13
> 3423
13423
> (current-compile c)
1
> (+ 1 2)
3
> (define c (current-eval))
> (current-eval (λ (x) (begin (display 1) (c x))))
> (+ 1 2)
13
> (define (yoba) (+ 1 2))
1
> 
Пжалуйста!

Вот, но я конечно утрирую - если возникают такие вопросы лучше сразу смотреть первоисточники (мне про Racket, а вам про CL, а то у вас неверные и предвзятые о нём представления).

Да я ничего не имею против cl, речь шла изначально о макросах и только о них. А потом потянуло за собой остальное. Кстати, насчет интерактивной разработки - внимательнее почитав мануал, обнаружил интересную вещь:

Добро пожаловать в DrRacket, версия 5.0.2 [3m].
Язык: racket [выбранный].
> (compile-enforce-module-constants #f)
> (require racket/enter)
> (module m1 racket
    (provide x)
    (define x 5))
> (module m2 racket
    (require 'm1)
    (define (yoba) (display x)))
> (require 'm2)
> (module m2 racket
    (require 'm1)
    (provide yoba)
    (define (yoba) (display x)))
> (require 'm2)
> (yoba)
5
> (enter! 'm1)
> (define x 10)
> (enter! #f)
> (yoba)
10
> 
так что не нужно велосипедить cl-овские пекеджи. Правда этот (compile-enforce-module-constants #f) отключает ряд оптимизаций, но, я думаю, для процесса разработки это несущественно.

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

> Аналог iterate на основе макросистемы Racket есть?

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

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

> Она просто реализуется через рекурсию.

Рекурсия, как альтернатива iterate (а не банальному циклу) - совершенно не впечатляет.

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

лучше взять и собрать схему в llvm (<1000 строк)

Вопрос в том какого качество этих < 1000 строк, IMHO - там не очень красиво получилось.

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

Потому что нефиг в репл совать то, что туда совать не надо, выражения в repl это топ-левел, а не module-context

Это получается как в Haskell - файл имеет особую структуру, может содержать определённые особые выражения и определённые top-level выражения. В CL абсолютно любая форма является top-level формой (под формой подразумевается обычные аппликации и все специальные формы, в том числе и макросы, какие-то куски специальных форм и макросов могут, конечно, не быть формами (а.к.а. термами)), а файл - произвольный набор таких форм. Определение модуля (пакета) это просто форма которая создаёт структуру описывающую модуль и помещающая её в глобальный список модулей, переход в определённый модуль это просто присваивание глобальной переменной текущий-модуль одной из структур из списка модулей, т.е. можно как угодно комбинировать эти формы - нет фиксированного формата у файла с исходным кодом, зачем? И зачем вводить формы которые не могут быть top-level формами?

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

Rогда идет речь о Racket то имеется в виду по умолчанию не реализация Scheme для Racket, а реализация Racket для Racket, стандарт которого увидеть вот тут: http://docs.racket-lang.org/reference/index.html

Я говорю о конкретной реализации Racket (понятно какой) и конкретной реализации CL (SBCL).

Ну хорошо, если это считать стандартом, то да - говорим про конкретный Racket и конкретный SBCL.

Процесс экспанда в Racket идет по фазам, от старшей к младшей, пока старшая не раскроется к младшей не переходим. Сколько хотите таких фаз сделать - столько и будет.

Как я понял эти фазы (любого количества) все относятся к (AST -> AST), ну или (Syntax-Object -> Syntax-Object). Я говорю о фазах вообще (разных типов), в том числе про фазу чтения, фазу построения графа, его редукций, фазу эмита инструкций. (AST -> AST) это одна фаза, даже если она разделена на несколько подфаз, в SBCL нет такого разделения - экспанд осуществляется с оглядкой только на определённые макросы (одна фаза, точнее две - есть ещё compiler macros), я не понимаю сейчас зачем их делить относительно разных подфазы.

Переопределить ридер? Конечно можно, именно для этого #lang и сделан, вобщем-то.

#lang это немного другое, с помощью него вы говорите «тут у нас один язык, тут другой». А я про полностью управляемый ридер. Мы говорим про лиспы, тут ситуация немного отличается от других языков где есть лексер+парсер, тут есть ридер который строит AST и набор специальных парсеров которые он использует (например, разобрать запись числа, строки и т.п.). Когда ридер считывает character он смотрит не связана ли с ним процедура чтения, если связана - он передаёт ей управление, т.о. можно как угодно менять поведение ридера, вроде:

(defun foo (a b c)
  <a^2 + b + c>)

;=>

(defun foo (a b c)
  (+ (expt a 2) b c))

если связать с `<' процедуру разбора инфиксных выражений возвращающую управление по достижению `>', то она будет вызвана во время чтения. Это может быть удобно для расширения синтаксиса самого CL, в других случаях, когда речь идёт о сильно отличающихся языках, может оказаться лучше #lang-подход - он тут тоже возможен, называется named-readtables, т.е. есть пакеты (модули) в которые вы можете переключаться и есть синтаксические «пакеты» в которые можно точно также переключаться.

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

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

compilation-handler и evaluation-handler вполне доступны. Информация о типах появляется, как я понимаю, только во время выполнения (хотя тут могу и ошибаться), ну а требовать настройки тех фаз, которых просто физически нет, по-моему, несколько глупо.

В _настоящем_ компиляторе информация о типах доступна при компиляции :) SBCL, видимо, внутри более типизированный чем typed racket - иногда можно знать типы даже не декларированных на уровне исходного кода объектов, + есть некий вывод типов.

Хотя, конечно, вам никто не мешает такие фазы ввести - например, ловите evaluation-handler, декомпилируете его байткод, парсите, выполняете преобразования, которые хотели над ним сделать, компилируете, суете в дефолтный хендлер.

Речь о том, что компилятор осуществляет преобразования (аст -> граф, последовательность редукций на графе, свёртка графа в последовательность виртуальных операторов, assemble виртуальных операторов на данной архетектуре) то у вас либо есть возможность управлять этими преобразованиями, либо нет, т.е. это вопрос архитектуры компилятора. Ловить и декомпилировать - это не то, вот в CMUCL изначально основной мотивацией было желание создать user-driven нативный компилятор, если вам нужно определить инструкцию вы пишете define-instruction, если виртуальный оператор - define-vop, если способ преобразования AST во flow graph - define-ir1-translator; все подобные определения меняют поведение компилятора, т.е. самой этой последовательности преобразований (никаких «хендлеров» и декомпиляций, вы можете представить как это происходит - также как и в случае макрсов чтения и макросов, алгоритм чтения общий (с оглядкой на таблицу определённых пользовательских процедур чтения), алгоритм макро-экспансии тоже общий (с оглядкой на таблицу определённых макрсов), все остальные процедуры трансляции тоже завязаны на таблицы трансляторов, инструкций, оптимизаторов и т.п.). В SBCL сделать возможность ассемблерных вставок в intel синтаксисе в обычный исходный код - это тривиальное расширение о ста строках.

Можно. Но не нужно :)

Ну «не нужно» это отличный аргумент :) Действительно, «я сделал возможность ассемблерных вставок» это нафиг не нужно в большинстве случаев, но речь скорее о правильной архитектуре.

Надеюсь, не будет претензий к тому, что нельзя интерактивно, из репла переписать vm?

Нет, так все делают - и SBCL, и JVM, и GHC (видимо, это UNIX эпоха (а Windows это «обрезанный UNIX») и от си не отвязаться - в лисп машинах от него отвязались, но это были слишком right things (хотя сами эти машины такими не были - кто-то доказывал, что они проишрывают flat машинам по части некоторых алгоритмов)).

В racket я увидел compiler в collects, однако, судя по всему там протокод простокомпайлера, не driven компилятор :)

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

Пжалуйста!

Я вижу - вы взяли чёрный ящик, и вложили его в другой. Разница - печать единицы. А нужно чтобы на основном ящике было 100500 ручек вращение которых будет менять его поведение (компиляция).

так что не нужно велосипедить cl-овские пекеджи.

Ничего не понял :) Вы между модулями переключаетесь там? И что в этом велосипедного?

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

И главное - получится использовать такой подход при написании «реальных программ» (tm), потому что если нет, то это и будет велосипед.

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

Рекурсия, как альтернатива iterate (а не банальному циклу) - совершенно не впечатляет.

Утверждения «А реализуется через Б» и «Б - альтернатива А» имеют несколько разный смысл, не находите? Имеется ввиду, что можно сделать аналог итерейт, который будет раскрываться в рекурсивную функцию.

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

Это получается как в Haskell

Нет

В CL абсолютно любая форма ...

Да так же в Racket. Я немного поясню. Когда вы пишете:

#lang any
form1
form2
...
То ридер транслирует это в:
(module anonymous-module any
     form1
     form2
     form3)
По-этому, когда вы загружаете подобный файл, то тут только одна топ-левел форма - это форма (module ...), те формы, что внутри (module ...) уже не топ-левел (они и в CL будут не топ-левел, если их поместить внутрь другой формы, так же как тут они внутри (module ...), так что ваши слова о том, что в cl все формы top-level - это ложь). Никто вам не мешает писать только top-level формы и загружать файлы через (load ...) - но это deprecated. Зачем пользоваться неудобными инструментами, когда есть удобная система модулей? Можете считать, что форма (module ...) - это просто такая спецформа, которая улучшает обработку и компиляцию внутренних форм. Оно примерно так и есть на самом деле. То есть, предполагается, что вся ваша программа разложена по модулям, а топ-левел формы исполняются только в репле, во время разработки. Вы можете, конечно, делать иначе (как в CL), но никаких причин делать иначе нет.

 И зачем вводить формы которые не могут быть top-level формами?

А зачем вводить анафорические макросы? Определенные формы могут иметь смысл только внутри определенного контекста - это очевидно. Вообще, с тем define-syntax дело все на самом деле лишь в том, что «_» по дефолту используется для своих особых целей, если мы его не переопределим, то при использовании вне соответствующего контекста он матерится во время макроэкспанда. Было бы то же самое, если бы написали просто (+ 1 _). Если бы вместо _ было бы просто какое-нибудь имя без биндингов, то все было бы ок.

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

я не понимаю сейчас зачем их делить относительно разных подфазы.

Для прозрачности процесса макроэкспанда.

#lang это немного другое, с помощью него вы говорите «тут у нас один язык, тут другой». А я про полностью управляемый ридер.

Директива #lang задает и ридер в том числе (то есть для каждого lang есть своя функция read). А если вы вспомните, что #lang раскрывается в обычный (module ...) (кстати, вот #lang - это как раз ридер-макрос), то поймете, что в каждом модуле может быть свой ридер. Причем есть еще один ридер-макрос #reader reader-name, который заменяет текущий ридер новым, так что в 1 файле можно использовать сколько вам захочется ридеров и менять их туда-сюда, как вам угодно.

Мы говорим про лиспы, тут ситуация немного отличается от других языков где есть лексер+парсер, тут есть ридер который строит AST и набор специальных парсеров которые он использует (например, разобрать запись числа, строки и т.п.). Когда ридер считывает character он смотрит не связана ли с ним процедура чтения, если связана - он передаёт ей управление, т.о. можно как угодно менять поведение ридера, вроде:

Да, в Racket все так и есть, я уже об этом говорил. С ридером связана read-table, в которой хранятся процедуры чтения для каждого character. Вы эти процедуры можете поменять (или добавить, если их нет - как для большинство characters).

если связать с `<' процедуру разбора инфиксных выражений возвращающую управление по достижению `>'

Да, все можно.

Это может быть удобно для расширения синтаксиса самого CL, в других случаях, когда речь идёт о сильно отличающихся языках, может оказаться лучше #lang-подход

Эти подходы можно реализовать в рамках друг друга. Буквально в несколько десятков строк можно написать новый #lang, который будет отличаться тем, что в нем будет определен макрос для выполнения формы в read-time и макрос для подмены read-table в read-time. CL-овский (define-reader-macro ...) будет просто небольшим слоем сахара над этим делом. Дальше при использовании этого #lang можно делать все, что в CL способом аналогичным CL. Аналогично и наоборот. Тут, согласитесь, важен сам доступ к ридеру, возможность подменить функцию ридера на свою. Если эта возможность есть - все остальное просто вопрос наличия соответствующего сахарка, ну а потом уже просто вопрос того, кто какой сахар предпочитает. Выразить одно через другое - не проблема.

В _настоящем_ компиляторе информация о типах доступна при компиляции :) SBCL, видимо, внутри более типизированный чем typed racket

В Typed Racket тоже есть вывод типов, но выводятся только локальные. Можно было сделать и full inference, но этого не было сделано из соображений дизайна. Алсо, типы в Typed Racket определяются во второй фазе - то есть они существуют еще до начала макроэкспанда ваших макросов и уж задолго до компиляции. Что до наличия типов в обычной Racket - я, как уже сказал, не знаю, что представляет из себя байткод (знаю только, что сам байткод тоже lisp-based :)), но типов там, по-моему, нет, они определяются в рантайме. То есть это надо копаться в самой vm.

Речь о том, что компилятор осуществляет преобразования

Речь о том, что в Racket он, видимо, не осуществляет подобных преобразований :)

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

[quote] Ловить и декомпилировать - это не то [/quote] Это как раз то.

[quote] define-instruction, если виртуальный оператор - define-vop, если способ преобразования AST во flow graph - define-ir1-translator; все подобные определения меняют поведение компилятора [/quote] Все подобные определения - это, опять-таки, не более чем синтаксический сахар. Если вы можете поменять compilation/evaluation handler, то вы все описанное можете. Например, ввести новые инструкции байткода. Другое дело, что новые инструкции байткода надо будет транслировать в набор старых (иначе надо лезть в evaluation и vm), но задача написания какого-нибудь (define-bytecode-instruction ...) опять-таки тривиальна и дело нескольких десятков строк (если бы это не было тривиальным я бы и не стал говорить о «возможности»), для этого, конечно, надо знать, как устроен байткод. Но это уже другой разговор, ортогональный возможности доступа к той или иной фазе компиляции.

[quote] В SBCL сделать возможность ассемблерных вставок в intel синтаксисе в обычный исходный код - это тривиальное расширение о ста строках. [/quote] В Racket для этого надо переписать vm. Разница между компиляцией в байткод и натив - я уже говорил об этом.

[quote] Ну «не нужно» это отличный аргумент :) Действительно, «я сделал возможность ассемблерных вставок» это нафиг не нужно в большинстве случаев, но речь скорее о правильной архитектуре. [/quote] Ну вот не лезть туда - это как раз и есть правильная архитектура. Я именно это подразумевал под «не нужно», а не то, что такая возможность не может ВНЕЗАПНО пригодиться :)

[quote] В racket я увидел compiler в collects, однако, судя по всему там протокод простокомпайлера, не driven компилятор :) [/quote] Там, как я понимаю, сам компилятор (в байткод), а вот в eval, понятно, уже со всех сил дергается си.

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

Я вижу - вы взяли чёрный ящик, и вложили его в другой. Разница - печать единицы. А нужно чтобы на основном ящике было 100500 ручек вращение которых будет менять его поведение (компиляция).

Я поменял стандартные функции компиляции/евала на свои собственные - то есть то, что вы и просили. Кстати, безо всякой пересборки. Разница печать единицы - потому что это просто proof of concept, но понятно же, что разница может быть какой угодно. Можете вообще поменять compile на функцию, которая генерит сишный код, а евал - принимает сишный код и потом запускает его при помощи gcc, например. Что до ручек - их добавление задача не сложная.

Ничего не понял :) Вы между модулями переключаетесь там?

Да. И потом переопределяю х в зарекьюренном модуле.

И главное - получится использовать такой подход при написании «реальных программ» (tm), потому что если нет, то это и будет велосипед.

Ну я не знаю, тут вот некто вопил о том, что-де без подобного подхода лисп вообще не нужен и вообще. А некто другой говорил, что в Racket так нельзя. Я вот просто показал, что все можно - не более того :)

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

Наверное, for подойдет как замена iterate: http://download.plt-scheme.org/doc/html/reference/for.html. Плюс можно создавать свои последовательности через make-do-sequence. Любопытно.

Странно. Я почему-то думал, что там все принято делать через рекурсию :)

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

for раскрывается в named-let форму, то есть в рекурсивную функцию. Например

(for ([i (in-range n)]) (display i))
раскроется во что-то вроде:
(let loop ([i  10]) (when (positive? i) (begin (display i) (loop (sub1 i)))))

anonymous
()

Ещё один вопрос

Раз уж тут собрались все лисперы, то тоже спрошу про макросы и eval.

Есть макрос (defmacro run-func name &rest params)

Есть список параметров в переменной. Чем заменить (eval (append (list run-func my-name) params)))

?

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

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