LINUX.ORG.RU

Ворос по раскрытию макросов

 


2

5

Допустим, я пишу новый, «более лучший лисп»(ТМ). Хочу реализовать простенькую макросистему. Мой экспандер анализирует исходник, находит макровызовы, раскрывает их, а дальше возникает вопрос. Что если код, в который раскрывается макрос, содержит еще один макровызов? Его нельзя раскрыть на данном этапе, поскольку еще нет разрешения имен. Значит, мне надо заэвалить все, а раскрывать когда? Скорей всего, когда данный вызов потребуется. Заэвалил, запускаю снова экспандер. вроде норм. Но что если результат 2 вызова требуется уже при первом выполнении? Заставлять пользователя явно указывать, в какой фазе раскрывать каждый макрос, или как?

Ответ на: комментарий от terminator-101

И эти 2 сущности живут независимыми жизнями.

Все верно! Бритва Оккама!

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

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

Не надо гнать на Маккарти. Я не встречал ни одного источника, где он говорил бы об этом как об ошибке дизайна. Кроме того, лисп не имел никакого отношения к LC кроме лямбда-нотации. Никакой ошибки там не было.

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

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

terminator-101
() автор топика
Ответ на: комментарий от komputikisto

Pico Lisp - не рефлективный Лисп.

PicoLisp — рефлективен на все 100.

terminator-101
() автор топика
Ответ на: комментарий от terminator-101

Ну, вот... А я уж думал ты готов слушать, что квалифицированные в этой области говорят. Тогда я умываю руки. Я написал 100 и одну реализацию Лиспа на различных схемах - SECD, CPS, фреймы, реификаторы, dynmaic/lexical scope и т.п. Я тут не понтуюсь, а хочу помочь, но раз никто не собирается слушать, то я не на том форуме, очевидно.

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

Ты что же, думаешь, что удары кулачечками в грудь с криками Я — это весомый аргумент? LOL.

terminator-101
() автор топика
Ответ на: комментарий от komputikisto

Что ж ты за эгоист такой! Пара сраных регистрантов тебя не дослушала и ты уже руки опускаешь. А как же ридонли анонимусы?

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

не увидел различия между его схемой и лямбда-исчислением

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

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

Первый лисп, с дефолтным лексическим скопом - это алголоподобная схема

Вообще-то бета-редукция как раз реализует lexical scope. Ты хочешь сказать, что Маккарти ничего не знал про лямбда-исчисление и бета-редукцию?

То, что разработчики Алгола и лямбда-исчисления пришли к одной идее о lexical scope говорит о том, что это правильная модель.

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

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

Я хочу сказать, что он плевал на нее.

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

Лексический скоп серьезно ограничивает выразительные возможности языка, делая его более «безопасным». В конечном итоге, он ведет к «классической» (в плохом смысле) модели ООП, принятой в таких языках как JAVA. С жестко структурированной иерархией. BDSM. И с небольшим сахарком, становиться достоянием быдло-энтерпрайза, где он, действительно, более чем уместен. ИЧСХ, Стилл, как раз таки, один из дезигнеров этого говна.

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

Я хочу сказать, что он плевал на нее.

Вот именно! История была скорее всего такая. Алонзо Чёрчь попробовал правило подстановки, но понял что иногда оно приводит к ошибкам. Усложнил правила и получились альфа и бета редукции. Далее Маккатри соединил то, что он знал про обработку списков с той частью лямбда-исчисления, которую он осознавал. У него получился Lisp с dynamic scope (т.е. с той моделью подстановки, которая по всей видимости была отвергнута Чёрчем). Далее ряд исследователей (Питер Ландин и другие) публикует статьи о правильной реализации lexical scope, но не на основе редукционных правил, а посредством замыканий. После долгого обсуждения проблемы funarg в Лисп пристраивают замыкания, которые реализуют промежуточную форму между dynamic и lexical scope. Они назывались funarg device. И только потом в Scheme реализовали все правильно. После фейла Лиспа, который не смог стать распространенным языком, некоторые программисты (я не только о Гвидо говорю) недовольные языками того времени разрабатывают скриптовые языки и натыкаются на проблему funarg. И вот уже наши дни. Некто terminator-101 не понимает проблему funarg, говорит, что Pico Lisp - самый тру Лисп. Такая же история со многими другими крутыми концепциями программирования, например, продолжениями.

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

Фунарг-проблема — это детская проблема. Достаточно конвенций именований, чтобы ее «решить»

После фейла Лиспа, который не смог стать распространенным языком, некоторые программисты

Breaking news!!! Язык элиты, внезапно, не стал языком быдла.

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

В конечном итоге, он ведет к «классической» (в плохом смысле) модели ООП

Не смотря на то, что некоторые считают, что замыкания и ООП - суть одно, это не так. ООП о полиморфизме. Исторически так сложилось, что ad hoc полиморфизм смогли открыть впервые в модели посылки сообщений (Smalltalk). Сейчас уже известен лучший механизм ad hoc полиморфизма - generic функции CLOS. Хотя Лисперы уже как 20 лет знают решение проблемы с ООП, это опять наталкивается на ту же историю, что с проблемой funarg. Забавно наблюдать, как мультиметоды адаптируются вне Лиспа. Например, в Perl 6 сочетает одновременно ООП в стиле Smallatk и в стиле CLOS.

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

Не смотря на то, что некоторые считают, что замыкания и ООП - суть одно, это не так.

Во первых, не ООП, а быдляцкое недоООП. Во вторых — кто этого не понимает, или спорит с этим — тот дебил. И в третих, к сведению, основа смолтоковских объектов — это лисповские fexpr'ы

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

lol

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

Breaking news!!! Язык элиты, внезапно, не стал языком быдла.

Этому «быдлу», как ты их называешь, все равно на чем программировать. Скажут на Лиспе - будут на Лиспе. Лисп одновременно прост и сложен. Прост тем, что сочетая в правильных пропорциях довольно красивые и простые идеи дает максимальную выразительность. Сложен тем, что понимать всю глубину этих простых правил способен не каждый. Любой программист способен писать на Лиспе, но не всякий поймет его истинную природу. Изучить любой современный ЯП с его нагромождением ad hoc правил куда как сложнее, чем Лисп.

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

основа смолтоковских объектов — это лисповские fexpr'ы

Что? Пример кода в студию! Фэкспры были в Smalltalk-72, но в Smalltalk-80 ничего подобного нет. Сейчас именно последний понимается под Смолтоком.

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

Изучить любой современный ЯП с его нагромождением ad hoc правил куда как сложнее, чем Лисп.

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

к слову, современные лиспы, за пределами схемы R5RS — это и есть нагромождение ad hoc правил.

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

за пределами схемы R5RS — это и есть нагромождение ad hoc правил.

syntax parsing из racket тоже нагромождение костылей?

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

Что?

Не позорься, почитай Кея. Тебе говорят не о фекспрах в языке, а о том, что фекспры — семантическая основа объектной системы. Ты лекции по лиспу читаешь, а лисп не знаешь них*я

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

В Smalltak я разбираюсь. Так вот, нет там фекспров. Изначально были, и про это, видимо, ты прочитал у Alan Kay. В Smalltak-80 управляющие конструкции реализованы в виде методов, которым передаются замыкания (блоки - в терминологии Smalltalk).

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

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

фекспры — семантическая основа объектной системы

Это как это? Похоже на бред. Семантическая основа Smalltak - диспетчеризация по одному аргументу.

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

к слову, современные лиспы, за пределами схемы R5RS — это и есть нагромождение ad hoc правил.

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

komputikisto
()
Ответ на: комментарий от terminator-101

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

Как это не видит? Видит.

В рефлективных лиспах все есть текст.

С чего вдруг? Почему именно текст?

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

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

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

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

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

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

Не смотря на то, что некоторые считают, что замыкания и ООП - суть одно, это не так. ООП о полиморфизме.

ООП об инкапсуляции.

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

только глобальный контекст


(set 'context1 (quote(f) (let(a 1) (f))))
(set 'context2 (quote(f) (let(a 2) (f))))
(set 'context3 (quote(f) (let(a 3) (f))))
(set 'f (quote() (prinl a)))

(context1 f) #  1
(context2 f) #  2
(context3 f) #  3

Эти контексты динамические, но никто не мешает реализовать любые. собственно, лексические замыкания тоже есть и в picolisp и в newlisp.

terminator-101
() автор топика
Ответ на: комментарий от terminator-101

Не глобальный, а динамический.

Именно глобальный.

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

Эти контексты динамические, но никто не мешает реализовать любые. собственно, лексические замыкания тоже есть и в picolisp и в newlisp.

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

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

Лексический скоп серьезно ограничивает выразительные возможности языка

А как в вашем «всё есть текст»-лиспе без лексического скопа написать такую функцию:

(define (generator x)
  (let ((i x))
    (lambda ()
      (set i (+ i 1))
      i)))

(define a (generator 1))
(define b (generator 1))

> (a)
2
> (a)
3
> (b)
2
> (a)
4

?

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

лексические замыкания тоже есть и в picolisp

Да ну? Напиши на picolisp аналог

(define (generator x)
  (let ((i x))
    (lambda ()
      (set! i (+ i 1))
      i)))
monk ★★★★★
()
Ответ на: комментарий от monk

Вот тут написано как newLISP эмулирует замыкания. «Замыкания» в newLISP определяют глобальные переменные, которые не видны остальному коду за счет пакетов. Т.е. «замыкания» в newLISP - это хак с пакетами. Пакеты являются концепцией времени чтения. Смешение областей видимости (концепция времени исполнения) и пакетов является плохой идеей. Это уже в макросах приводит к некоторым проблемам, а тут все через пакеты...

Одна из моих идей, которые прорабатываю состоит в том, чтобы использовать пакеты как на этапе чтения (в лекции я пользуюсь термином интернализация) так и на этапе печати (экстернализации). Т.е. пакеты в моем Лиспе это список пар (текст . символ). Символы не тащат за собой текстовое представление, как в других Лиспах, а печатаются по-разному в зависимости от текущего пакета. В таком Лиспе с одним и тем же интерпретатором можно взаимодействовать на различных языках. Т.е. возникает гетерогенность репрезентации символов. Символ в моем Лиспе - структура без полей. Но для того чтобы это все заработало, нужно снять с пакетов дополнительную нагрузку разделения пространств имен. Этого можно достигнуть реализовав first-class окружения как в MIT Scheme, T или OakLisp.

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

«Замыкания» в newLISP определяют глобальные переменные, которые не видны остальному коду за счет пакетов.

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

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

как сделать, чтобы generator создавал новый пакет при каждом выполнении

Как я понял, пример это не делает. Я не разбираюсь в newLISP, поэтому не буду додумывать. Да и неважно это, т.к. пакеты не должны использоваться в подобном контексте.

komputikisto
()
Ответ на: комментарий от terminator-101

И оно будет так работать?

(define a (gen:gen 1))
(define b (gen:gen 1))

> (a)
2
> (a)
3
> (b)
2
> (a)
4

?

По-моему, нет. У тебя gen:gen возвращает число, а должен возвращать замыкание-генератор.

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

Да и неважно это, т.к. пакеты не должны использоваться в подобном контексте.

Тогда что должно? Или такую функцию написать на picolisp/newlisp невозможно? Тогда они даже менее выразительны, чем C++.

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

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

Я уже цитировал статью Steele & Sussman, она не зря имеет подзаголовок «трудности модульности». Там выделяются следующие схемы видимости переменных:

- локальный scope - dynamic scope - lexical scope - lexical scope + опционально dynamic

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

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

Guy Lewis Steele, Jr. and Gerald Jay Sussman. «The Art of the Interpreter or, the Modularity Complex (Parts Zero, One, and Two)»

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

Символы не тащат за собой текстовое представление, как в других Лиспах

А в каких это лиспах символы тащат за собой текстовое представление? Я ни одного такого не знаю.

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

В CLHS можно прочитать, что символы имеют следующие атрибуты: Name, Package, Property list, Value, Function.

Как видно, имя является атрибутом символа. При смене пакета печатное имя символа не изменяется. Кроме того, CL хранит глобальные переменные в виде атрибутов символа, что является наследием совсем уж старых Лиспов. Атрибуты Value и Function не нужны при правильной организации глобального окружения. Атрибут Name не нужен, если Лисп будет хранить текстовое представления в пакетах, а не в атрибуте символа. Остается только Property list. Вот примерный диалог с моей реализацией:

> standard-package
> <package standard-package>
> (setq x '(python ruby lisp))
> (setq p (copy-package standard-package))
> (change-representation 'lisp "шепелявить" p)
> (change-representation 'python "пистон" p)
> (repl :package p)
>> x
>> (пистон ruby шепелявить)
>> (exit)
> x
> (python ruby lisp)
komputikisto
()
Ответ на: комментарий от anonymous

Я готов к диалогу, но я не могу одно и то же объяснять по кругу. Лисп работает следующим образом. Есть какое-то внешнее представление (обычно это текст, но не обязательно). Вычислитель не обязан ничего знать про это внешнее представление, он вычисляет внутренние объекты Лиспа - формы. Внешнее представление преобразуется во внутреннее в результате процесса интернализации. Внешние имена (текст) преобразуются в символы (внутреннее представление имен) при помощи пакетов. Экстернализация (обратное преобразование) производится при помощи атрибута Name. Моя Лисп система при экстернализации символов ищет их в текущем пакете, который и определяет их текстовое представление. В рамках одного пакета должно соблюдаться условие: экстернализация(интернализация(x))=x.

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

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

> standard-package   ; предопределенный пакет
<package standard-package>
> (setq x '(python ruby lisp))
(python ruby lisp)   ; печать относительно пакета standart-package
> (setq p (copy-package standard-package 'new-package))
<package new-package>
> (change-representation 'lisp "шепелявить" p)
... ; значение не имеет значения
> (change-representation 'python "пистон" p)
...
> (repl :package p) ; новый REPL со сменой пакета
>> x                ; приглашение к вводу отображает уровень REPL
(пистон ruby шепелявить) ; печать относительно пакета new-package
>> (exit)
> x
(python ruby lisp) ; печать относительно пакета standart-package
komputikisto
()
Ответ на: комментарий от komputikisto

Я готов к диалогу, но я не могу одно и то же объяснять по кругу.

Так ты ничего и не обяяснял ни разу. Только воду льешь.

Экстернализация (обратное преобразование) производится при помощи атрибута Name.

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

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