Информация об изменениях

Сообщение Re[12]: понимание ООП Алана Кея от 27.03.2023 8:41

Изменено 27.03.2023 8:42 vdimas

Re[12]: понимание ООП Алана Кея
Здравствуйте, korvin_, Вы писали:

V>>Макросы Лиспа подстановочные (шаблонные), как макросы Си/С++ или макроассемблера.

_>С чего это вдруг?

Такой дизайн. ))

Заметь, я не говорил, что в Лиспе нельзя производить трансформацию AST явным образом, если ты напишешь свой некий такой код.
Я напоминаю, как устроены стандарные максросы Лиспа.


V>>А вот макросы Форта устроены иначе — они получают на входе поток лексем и манипулируют с ним именно явно, порождая то самое AST.

_>Макросы Лиспа получают на вход дерево объектов и манипулируют с ним явно.

Разве?

(defmacro foo2 (x)
(list 'exp x))


В момент компиляции во внутренее представления встречающихся foo2 с аргументом x, де-факто будет происходить подстановка (list 'exp x)) с указанным x.
Это обычный подстановочный макрос, который может быть реализован как текстуальный/шаблонный.
На практике в Лиспе он не текстуальный по соображениям эффективности — тело макроса хранится в предкомпилённом виде, т.е. что-то вроде шаблонов С++.

Никакого явного оперирования AST тут нет, как нет оперирования AST в шаблонах С++.


V>>Макросы Схемы живут только в момент компиляции, в отличии от макросов Лиспа

_>Что мешает в Схеме вызвать eval в рантайме?

Чаще всего никак.

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

Соответственно, у подключаемого движка-интерпретатора нет никакой связи с твоей программой.
В Схеме eval имеет сигнатуру (eval expression environment) где environment необходимо набить ручками перед вызовом eval.
Сравнить с Лиспом, в котом eval имеет сигнатуру (eval expression), а environment используется неявным образом текущий.


V>>Макросы Схемы имеют другое ключевое слово

_>Это существенное отличие, да.

Это из-за дополнительных ключевых слов в телах макросов.
Т.е. чуть другой стандарт, поэтому взяли другой идентификатор, ИМХО.


V>>и пару особенностей.

_>Каких?

Да просто открывает доку по макросам Лиспа и Схемы, сравниваешь.
Там очевидно.


V>>Но это по-прежнему подстановочные/позиционные макросы, без явного манипулирования AST.

_>Что мешает работать с syntax object в Схеме через syntax-case, например?

Это что-то вроде частичного определения шаблонов в С++ — описание неких частных случаев.
Механика та же, что и для общего случая.


V>>Причём, тут я противоречу самому себе, вроде бы, ведь можно представить себе такую реализацию Схемы, которая ведёт себя как Лисп?

V>>Представить такую реализацию можно, да вот только Схема — это именно компиллируемая версия Лиспа. ))
V>>А динамическая версия — это сам Лисп.
_>SBCL прекрасно компилирует Common Lisp.

Да пофик, динамичность никуда не девается.
Всё-равно под капотом работает Лисп-машинка.


_>А eval из Схемы никуда не делся.


Он прямо по стандарту делся в отдельную библиотеку, которая подключается при надобности.
Идея Схемы была дать возможность исполнять конечный код без Лисп-машинки, без ссылочной семантики в >90% случаев, без динамического построения текущего контекста и поиска в нём символов и т.д.
Соответственно, если широко используются ср-ва навроде eval (primitive-eval и т.д.), то Схема теряет заложенное в ней преимущество.
Профанация получается.


V>>Для целей эффективного конечного кода пришлось внести в Лисп несколько ограничений, наверно поэтому назвали другим языком и стандарты Схемы с тех пор развиваются независимо от Лиспа.

_>Схема не так и не для этого разрабатывалась.

Для этого. ))

Scheme стал первым диалектом Лиспа, применяющим исключительно статические (а не динамические) области видимости переменных

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

Но при надобности не стирать символы — вот вам рядышком прежний динамический eval.


V>>Вернее, впервые стандарты на семейство Лисп появились в Схеме — именно из-за вносимых ограничений.

_>С чего ты взял, что из-за них?

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


V>>В любом случае, это лишь эмуляция.

_>Чем здесь эмуляция отличается от «не-эмуляции»?

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

При этом теряются гарантии целостности, в сравнении с С++, т.е. в эмуляции есть возможность описать объект, имеющий некорректный "бинарный лейаут", на котором эмуляция споткнётся в рантайм (в зависимости от реализации такой эмуляции может быть проход по памяти, либо же динамически выкинута ошибка/сигнал). В ООП-языках нет возможность описать объект некорректно, компилятор не позволяет.


V>>Объекты в Лиспе не являются первоклассными сущностями языка, вот в чём косяк.

_>Являются.

В этом абзаце имелись ввиду не встроенные объекты-символы Лиспа (числа, ф-ии и CONS), а эмулируемые "объекты" в парадигме ООП.


V>>Первоклассной сущностью там являются числа/символы, функции и пара CONS(car, cdr), где каждый элемент пары может быть числом, функцией или опять парой (ссылочная семантика для всего перечисленного).

_>И много что ещё. Перечитай определение, что такое first-class citizen.

Сам почитай. ))

В C и C++ нельзя создавать функции во время выполнения программы, поэтому функции не являются объектами первого класса в этих языках. В то же время указатели на функцию можно передавать в качестве аргумента и возвращать из другой функции, поэтому иногда функции в C++ называют объектами второго класса (англ. second-class object). Тем не менее, в C++ есть понятие функционального объекта (англ. function object), который является объектом первого класса и реализует эквивалентную функциям семантику.

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


V>>Форт навязывает лишь способ мышления, не навязывая остального.

V>>И при этом чудовищно прост в реализации.
V>>И при этом чудовищно эффективен, в сравнении с Лиспом.
V>>Порой и в сравнении с нейтивом из-за особенностей манипулирования стеком — в Форте обычно меньше паразитных манипуляций данными в стеках/регистрах, меньше ненужных копирований/преобразований данных — состояние стека на выходе одного слова часто является готовым состоянием стека для вызова другого. Писать программы на Форте — это как играть в Тетрис, натурально, все фигуры должны стыковаться, и тогда прога просто летает. ))
_>А уж ассемблер-то как эффективен.

Мимо. Ассемблер низкоуровневый язык, а Форт высокоуровневый, хотя имеет ср-ва низкого уровня.
Это примерно как Си, только потенциально мощнее, т.к. позволяет описывать свой синтаксис (DSL) в программе.

Но это всё не бесплатно, конечно, т.к. простор для ошибок в Форте достаточно широк.
На технике той лет за эффективность приходилось платить. ))


_>И что из этого вытекает? Зачем все эти Лиспы, Си, Паскали и прочие Форты понапридумывали?


Да тут по классике инженерии — это решения в условиях противоречивых требований, где само решение представляет из себя системы принятых компромиссов.
Развитие ЯВУ диктовались развитием ресурсов железа, конечно, т.е. сугубо практическими соображениями.
Например, функциональные языки своего слова еще не сказали, т.е. тут как в науке — теория значительно опережает практику. ))

В одном из встреченных экспериментов проводили альфа/бета/эта-преобразования программы (эти преобразования выполняются итеративно, пока есть возможность таких преобразований) на ФП-языке "до самого дна" среднего размера программы.

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

Компиляция на современной технике заняла несколько дней.
Вот тебе ответ, зачем другие языки. ))
Re[12]: понимание ООП Алана Кея
Здравствуйте, korvin_, Вы писали:

V>>Макросы Лиспа подстановочные (шаблонные), как макросы Си/С++ или макроассемблера.

_>С чего это вдруг?

Такой дизайн. ))

Заметь, я не говорил, что в Лиспе нельзя производить трансформацию AST явным образом, если ты напишешь свой некий такой код.
Я напоминаю, как устроены стандарные максросы Лиспа.


V>>А вот макросы Форта устроены иначе — они получают на входе поток лексем и манипулируют с ним именно явно, порождая то самое AST.

_>Макросы Лиспа получают на вход дерево объектов и манипулируют с ним явно.

Разве?

(defmacro foo2 (x)
(list 'exp x))


В момент компиляции во внутренее представления встречающихся foo2 с аргументом x, де-факто будет происходить подстановка (list 'exp x)) с указанным x.
Это обычный подстановочный макрос, который может быть реализован как текстуальный/шаблонный.
На практике в Лиспе он не текстуальный по соображениям эффективности — тело макроса хранится в предкомпилённом виде, т.е. что-то вроде шаблонов С++.

Никакого явного оперирования AST тут нет, как нет оперирования AST в шаблонах С++.


V>>Макросы Схемы живут только в момент компиляции, в отличии от макросов Лиспа

_>Что мешает в Схеме вызвать eval в рантайме?

Чаще всего никак.

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

Соответственно, у подключаемого движка-интерпретатора нет никакой связи с твоей программой.
В Схеме eval имеет сигнатуру (eval expression environment) где environment необходимо набить ручками перед вызовом eval.
Сравнить с Лиспом, в котом eval имеет сигнатуру (eval expression), а environment используется неявным образом текущий.


V>>Макросы Схемы имеют другое ключевое слово

_>Это существенное отличие, да.

Это из-за дополнительных ключевых слов в телах макросов.
Т.е. чуть другой стандарт, поэтому взяли другой идентификатор, ИМХО.


V>>и пару особенностей.

_>Каких?

Да просто открываешь доку по макросам Лиспа и Схемы, сравниваешь.
Там очевидно.


V>>Но это по-прежнему подстановочные/позиционные макросы, без явного манипулирования AST.

_>Что мешает работать с syntax object в Схеме через syntax-case, например?

Это что-то вроде частичного определения шаблонов в С++ — описание неких частных случаев.
Механика та же, что и для общего случая.


V>>Причём, тут я противоречу самому себе, вроде бы, ведь можно представить себе такую реализацию Схемы, которая ведёт себя как Лисп?

V>>Представить такую реализацию можно, да вот только Схема — это именно компиллируемая версия Лиспа. ))
V>>А динамическая версия — это сам Лисп.
_>SBCL прекрасно компилирует Common Lisp.

Да пофик, динамичность никуда не девается.
Всё-равно под капотом работает Лисп-машинка.


_>А eval из Схемы никуда не делся.


Он прямо по стандарту делся в отдельную библиотеку, которая подключается при надобности.
Идея Схемы была дать возможность исполнять конечный код без Лисп-машинки, без ссылочной семантики в >90% случаев, без динамического построения текущего контекста и поиска в нём символов и т.д.
Соответственно, если широко используются ср-ва навроде eval (primitive-eval и т.д.), то Схема теряет заложенное в ней преимущество.
Профанация получается.


V>>Для целей эффективного конечного кода пришлось внести в Лисп несколько ограничений, наверно поэтому назвали другим языком и стандарты Схемы с тех пор развиваются независимо от Лиспа.

_>Схема не так и не для этого разрабатывалась.

Для этого. ))

Scheme стал первым диалектом Лиспа, применяющим исключительно статические (а не динамические) области видимости переменных

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

Но при надобности не стирать символы — вот вам рядышком прежний динамический eval.


V>>Вернее, впервые стандарты на семейство Лисп появились в Схеме — именно из-за вносимых ограничений.

_>С чего ты взял, что из-за них?

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


V>>В любом случае, это лишь эмуляция.

_>Чем здесь эмуляция отличается от «не-эмуляции»?

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

При этом теряются гарантии целостности, в сравнении с С++, т.е. в эмуляции есть возможность описать объект, имеющий некорректный "бинарный лейаут", на котором эмуляция споткнётся в рантайм (в зависимости от реализации такой эмуляции может быть проход по памяти, либо же динамически выкинута ошибка/сигнал). В ООП-языках нет возможность описать объект некорректно, компилятор не позволяет.


V>>Объекты в Лиспе не являются первоклассными сущностями языка, вот в чём косяк.

_>Являются.

В этом абзаце имелись ввиду не встроенные объекты-символы Лиспа (числа, ф-ии и CONS), а эмулируемые "объекты" в парадигме ООП.


V>>Первоклассной сущностью там являются числа/символы, функции и пара CONS(car, cdr), где каждый элемент пары может быть числом, функцией или опять парой (ссылочная семантика для всего перечисленного).

_>И много что ещё. Перечитай определение, что такое first-class citizen.

Сам почитай. ))

В C и C++ нельзя создавать функции во время выполнения программы, поэтому функции не являются объектами первого класса в этих языках. В то же время указатели на функцию можно передавать в качестве аргумента и возвращать из другой функции, поэтому иногда функции в C++ называют объектами второго класса (англ. second-class object). Тем не менее, в C++ есть понятие функционального объекта (англ. function object), который является объектом первого класса и реализует эквивалентную функциям семантику.

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


V>>Форт навязывает лишь способ мышления, не навязывая остального.

V>>И при этом чудовищно прост в реализации.
V>>И при этом чудовищно эффективен, в сравнении с Лиспом.
V>>Порой и в сравнении с нейтивом из-за особенностей манипулирования стеком — в Форте обычно меньше паразитных манипуляций данными в стеках/регистрах, меньше ненужных копирований/преобразований данных — состояние стека на выходе одного слова часто является готовым состоянием стека для вызова другого. Писать программы на Форте — это как играть в Тетрис, натурально, все фигуры должны стыковаться, и тогда прога просто летает. ))
_>А уж ассемблер-то как эффективен.

Мимо. Ассемблер низкоуровневый язык, а Форт высокоуровневый, хотя имеет ср-ва низкого уровня.
Это примерно как Си, только потенциально мощнее, т.к. позволяет описывать свой синтаксис (DSL) в программе.

Но это всё не бесплатно, конечно, т.к. простор для ошибок в Форте достаточно широк.
На технике той лет за эффективность приходилось платить. ))


_>И что из этого вытекает? Зачем все эти Лиспы, Си, Паскали и прочие Форты понапридумывали?


Да тут по классике инженерии — это решения в условиях противоречивых требований, где само решение представляет из себя системы принятых компромиссов.
Развитие ЯВУ диктовались развитием ресурсов железа, конечно, т.е. сугубо практическими соображениями.
Например, функциональные языки своего слова еще не сказали, т.е. тут как в науке — теория значительно опережает практику. ))

В одном из встреченных экспериментов проводили альфа/бета/эта-преобразования программы (эти преобразования выполняются итеративно, пока есть возможность таких преобразований) на ФП-языке "до самого дна" среднего размера программы.

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

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