Сообщение Re[9]: понимание ООП Алана Кея от 23.03.2023 2:18
Изменено 23.03.2023 2:21 Sinclair
Re[9]: понимание ООП Алана Кея
Здравствуйте, vdimas, Вы писали:
V>RTFM чего именно?
Истории Паскаля.
V>Сам же привёл пример конкретной системы gemstone, т.е. имелась ввиду соответствующая инфраструктура в этой системе.
Инфраструктура построена на каких-то основах.
S>>Ну, так как мне воспользоваться этим кодом изнутри VB?
V>Языками более низкого уровня.
V>Или расписать некий фреймворк оперирования памятью в некоей библиотеке и сделать импорт функций в VB (есть такая возможность), чтобы иметь требуемый инструментарий.
V>Например, можно взять готовую либу CRT.
Давайте пример кода на VB, который при помощи готовой либы CRT отправляет пользовательский предикат на сервер для исполнения.
V>Потому что в объектных файлах содержится код и данные со всей сопутствующей метаинформацией.
Это совершенно никак не связано с возможностями по манипуляции кодом как данными изнутри языка программирования.
Я не понимаю, почему эта очевидная вещь до вас не доходит. Компиляция в .obj была принята для практически 100% языков программирования; при этом возможностей мета-манипуляций в рантайме не было практически нигде.
V>Если требуется посылать код другому устройству, то требуется переносимость кода.
В первую очередь требуется возможность этот код описать в посылаемом виде.
V>Я уже начинаю подозревать, что ты видишь возможность переносимости кода только в виде его AST-представления.
Вы перескакиваете через ступеньку, пытаясь рассуждать об исполнении, как будто возможность породить такой код у вас есть.
Это во-первых.
Во-вторых, для систем типа GemStone возможность получить код в виде AST, а не байт-кода, является необходимостью.
Потому, что предикат не исполняется напрямую. СУБД на основе предиката строит план исполнения запроса, в котором одна часть предиката будет ключом поиска в индексе, другая будет фильтром по этому индексу, а третья будет вычисляться по связанной с индексом таблице. Это вам ликбез на пальцах.
Предикат, записанный в виде императивного байт-кода, таким преобразованиям поддаётся плохо либо никак.
V>В общем, твоя попытка рассуждать о банальностях как об откровениях малость сбивает меня с толку. ))
Ну так а что делать, если вы не понимаете элементарных вещей?
V>Провожу ликбез.
V>Хосподя, Алан Кей тот еще болтун, навроде тебя.
V>Причём, болтуном он стал гораздо позже... в попытках, не знаю, придать некую публичную окраску своей роли в этой работе.
А-а, ну вы-то, конечно, лучше Кея знаете, как там всё обстояло.
V>Такие монстры как Голдберг служили неплохим противовесом этому, в общем-то, нубу-самоучке.
А что, Голдберг где-то заявляла, что язык Smalltalk был вдохновлён Фортом?
V>Какой там в опу Лисп? ))
Да почитайте же уже The Early History of Smalltalk и перестаньте позориться.
V>Похоже, ты не трогал толком ни Смолтолк, ни Лисп, ни Форт, не понимаешь внутренней механики этих языков, но пытаешься фантазировать.
Вы всё время путаете дизайн языков с внутренней механикой компилятора и рантайма.
V>Рассуждать о Лиспе можно было лишь в последней версии Смолтолка-80, когда добавили метаклассы.
V>ООП в виде CLOS привнесли в 70-х в виде стандарта, напомню, и это всего лишь эмуляция ООП на библиотечном уровне.
V>Но концепции обкатывались, не без этого.
V>Т.е. в изначальном Лиспе никаких метаклассов не было, как и не было в Лиспе ООП, ес-но, были только ф-ии как первоклассные объекты (сам код) и данные в виде однонаправленно связанных встроенных в систему списков.
Конечно не было. Именно это Кей и реализовал — ему хотелось сделать систему, которая была построена на минимуме примитивов, как Лисп, но с ООП. Если бы в Лиспе было ООП, то Smalltalk бы и не понадобился.
V>Просто ссылаться на Лисп однажды стало модно, бгг, и этот болтун не преминул.
V>Курить, что есть шитый код.
Я знаю, что такое шитый код. Какое отношение он имеет к дизайну языка Smalltalk?
V>Пример развития Форта до переносимости м/у устройствами.
По-прежнему не понимаю, какое отношение Постскрипт с его переносимостью имеет к ООП Алана Кея.
V>Таки, самообразованием принято заниматься самостоятельно, не испытывая терпение коллег.
Ну так займитесь. Почему вы вместо того, чтобы почитать первоисточники, занимаетесь гаданием на МК-51?
S>>Попробуйте реализовать на Паскале, Си, и С++ функцию, которая бы передавала пользовательский предикат на сервер для исполнения.
V>Не вижу технических проблем передать чистую ф-ию или чистое замыкание при наличии соотв. инфраструктуры.
Пример кода в студию. Хотя бы на одном из языков.
V>Напомню, что MS компилятор С++ умеет компиллировать unamanaged C++ код (т.е. безо-всяких managed расширений) в чистый байт-код.
V>(тип генерируемого образа для режима С++/CLI задаётся опциями компилятора)
Ну ок, если вам будет проще — попробуйте изобразить требуемую функциональность на unmanaged C++, скомпилированном в чистый байт-код.
S>>А потом для сравнения попробуйте реализовать то же самое на Lisp или SmallTalk.
V>Ну вот попробуй на Лисп (или другом динамическом языке) без динамического исполнения текстовых исходников, а я поржу.
Посмотрите на то, как устроены макросы Лиспа. Там нет ничего про динамическое исполнение текстовых исходников.
Всё построено на манипуляциях кодом в виде AST. Можете начинать ржать.
V>Да не связана.
V>Такая возможность связана с представлением исполняемого кода, а это представление перпендикулярно языку.
V>Похоже, у тебя каша в голове из св-в языков и конкретных их компиляторов/интерпретаторов.
Всё ровно наоборот — это у вас каша.
V>Посмотри на Схему — вот тебе оптимизирующий компилятор Лиспа.
V>Были убраны возможности рантайм рефлексии кода.
V>Язык, по-сути, тот же.
V>Отличается представлением скомпиллированного кода, а значит, особенностями модели вычисления.
И самое замечательное, что эти особенности модели вычисления никак не влияют на то, о чём я говорю.
Макросы в Схеме всё ещё есть, и это означает, что я могу решить обозначенную задачу и на схеме тоже.
А всё потому, что решают тут свойства языка. Что там под капотом — дело десятое.
V>Не осенило еще? ))
Вижу, что вас не осенило. Причина, я думаю, именно в том, что вы не пробуете сделать то, о чём я говорю.
V>Например, MS Visual Basic for DOS компиллировал в неплохо оптимизированный код (я курочил дизассемблером что он там порождает — да там офигенно, на уровне их же компилятора Си и Паскаля).
Да видел конечно. Толку-то? Ни на одном из этих бейсиков я не могу отправить на сервер код пользовательского предиката.
V>В Лисп точно так же может сосуществовать сколько угодно много независимых ООП-подсистем библиотечного уровня.
Я в курсе. Синтаксис Лиспа настолько ужасен, что его ничем испортить нельзя. Поэтому всякие надстройки над ним выглядят не хуже оригинального лиспа.
И можно делать любое ООП, которое нравится — с наследованием интерфейсов и наследованием реализации, со множественным наследованием и без, с любыми наборами модификаторов "видимости" мемберов. Можно придумывать свойства и события; можно делать наследование экземпляров как в JS; можно обрабатывать неизвестные сообщения, как в Smalltalk.
Правда, всё это будет всё в том же ужасном стиле скобок поверх скобок. Но на фоне остального лисп-кода выделяться не будет.
В общем, Лисп считать ОО-языком бессмысленно.
V>Ты не понимаешь мотива принятия некоторых важных решений в IT в ретроспективе.
V>Проводимый ликбез тебя коробит, но это особенности твоего чванства и ничего более.
Скорее, это особенности вашего чванства.
V>В интересные места ведь тебя отсылаю. ))
Практически во всех этих местах я был. Вы что, всеръёз считаете, что я не в курсе кнутовского MIX-а, или никогда не слышал про Forth?
V>Ключевое тут то, что с помощью базовых примитивов можно расписать сколь угодно сложную систему управления динамической памятью, если по каким-то причинам не устраивает данная изкаробки. В том числе запросто пишется консервативный GC с любой из политик работы, например, какие были приняты в различных реализациях Лиспа — автоматический вызов при исчерпании пула ячеек vs явные вызовы GC.
Повторюсь: ООП-компилятор можно написать на не-ОО языке. Это ничего не говорит об ОО-свойствах этого языка.
V>Исходный GC связывал освобождаемые ячейки CONS в глобальный список свободных ячеек и не умел ничего более.
V>Перемещающи/копирующий GC должен уметь корректировать ссылки, а GC Лиспа той эпохи этого не делал.
V>(Не берусь утверждать за некие потенциальные новые реализации, бо за современным Лиспом не слежу, но десятилетиями оно работало именно так)
Наверняка вы что-то не так поняли. Весь смысл сборщика мусора — в возможности освободить занятую память.
Если сборщик ничего не перемещает, то откуда возьмётся освобождение? И вообще, зачем такой "сборщик" выполнять?
V>Не-а.
Может быть, я чего-то не понимаю. Вас не затруднит найти ссылку на описание того сборщика мусора, о котором вы рассуждаете?
V>По VB и VFoxPro литературы мильон.
Не по VB, а по ООП на VB. Примеры к "паттернам ООП" на каком языке даны?
V>Я говорю не про игнор, а про наложенные ограничения на реализацию парадигмы.
V>Джава наложила то ограничение, что экземпляры пользовательских типов будут всегда располагаться в динамической куче, а такого ограничения в исходной парадигме нет.
V>Это подробности конкретной реализации в неких принятых ограничениях.
Опять вы закапываетесь в подробности конкретной реализации. Зачем ?
В парадигме ООП нет ничего ни про кучу, ни про стек.
V>Сотый раз повторюсь — этого не требовалось.
V>Приличный "почти современный" ООП был реализован еще до Смолтолка, в Алгол-68, и там был функциональный тип и лямбды, т.е. лямбда-исчисление.
V>Я уже подкидывал тебе мысль, что ФП-подход — это подход системы ограничений к процедурному программированию (функциональный тип был известен еще в эпоху процедурного программирования), в то время как ООП-подход — это инструментарий расширения.
Это неудачная мысль.
V>ООП-подход включил известные на тот момент парадигмы целиком.
V>Дальнейшее развитие ООП-парадигмы и соотв. языков шло в области описания таких ограничений, коль они требуются, ср-вами языка.
V>Именно поэтому современные ООП-языки принято считать "мультипарадигменными", хотя это не так, разумеется.
Нет конечно.
S>>В чистом ООП никаких делегатов нет. Есть интерфейсы с определёнными сигнатурами.
V>Мда... Будем разгребать:
Вы просто написали то же самое, что и я, только более подробно.
V>А разгрести эту кашу можно лишь помня, что в ФП действует "утиная типизация", то бишь, такие функциональные объекты с одинаковой сигнатурой считаются одинаковым типом.
V>И оно не просто так, а потому что бинарная механика реализаций таких объектов идентична, т.е. бинарной ошибки реинтерпретации памяти быть не может.
Вы ставите телегу впереди лошади. В ФП совместимость между собой функций с одинаковой сигнатурой является частью дизайна языка. Бинарная механика просто построена так, чтобы удовлетворять этому требованию.
V>В дотнете, однако, делегаты с одинаковой сигнатурой неприводимы друг к другу, хотя обладают такой же бинарной совместимостью.
V>В этом месте ФП в дотнете заканчивается, толком не начавшись.
S>>А несовместимость делегатов с одинаковой сигнатурой в рамках ООП не является самостоятельной особенностью. Она скорее связана с выбором между утиной типизацией и номинативной типизацией.
V>Ага, т.е. ты хорошо понимаешь про утиную типизацию, просто Ваньку тут валяешь.
Конечно понимаю.
V>Номинативная типизация — это тоже левое ограничение, не требуемое для реализации концепции ООП.
V>Это просто такое решение, ограничиться именно ею.
V>Хотя, базовая машинка способна вызывать методы не того объекта через свою систему команд, а вся "загвоздка" была бы только в верификации сгенерированного компилятором кода, верно?
V>Т.е. всего-то надо было снабдить верификатор возможностью утиной типизации неких типов, помеченных атрибутом как "делегат" (их даже не обязательно было наследовать от базового Delegate, достаточно было наследовать прямо от Object, а остальные ср-ва можно было бы получать от метакласса).
V>Плюсом было бы то, что загрузчик-верификатор мог бы эффективно "склеивать" попадающие под утиную типизацию такие функциональные типы, т.е. не засорять память копиями одного и того же бинарного кода!
V>Типов-делегатов ведь овердохрена, но уникальных сигнатур потенциально меньше.
V>Все эти Action<> и Func<>, таки, выглядят крайне ущербно — как попытка залатать исходно присутствующую дыру во фреймворке.
Все эти Action<> и Func<> как раз и являются реализацией ровно вашей же идеи путём повторного использования уже существующих компонентов инфраструктуры. То самое склеивание функциональных типов и всё такое.
V>1. На шаблонном уровне совместимы.
Шаблоны тоже не являются частью ООП-парадигмы. Вы критикуете решение, принятое в системе, где шаблонов не было, при этом указывая на систему, где аналогичная дыра заклеивается при помощи шаблона
V>2. Компилятор склеивает идентичный бинарный код различных типов.
Это вообще деталь реализации.
V>А в дотнете ни ограничения в генериках на абстрактную сигатуру делегата не задать, ни описать эту абстрактную сигнатуру явно, скажем, где-то так:
V>
Не понял, а в чём тут проблема?
V>Об костыли Action<> и Func<> мы спотыкаемся сразу же, когда речь идёт о модификаторах, например ref/in/out, а недавно еще появились scope и readonly.
V>Плюс аргументами генериков не могут быть ref-структуры, поэтому, приходится описывать типы делегатов ручками как в старые добрые времена, когда генериков еще не было, бгг...
Это всё не относится к вопросам ООП парадигмы, а скорее о проектировании реальной платформы в условиях противоречивых ограничений.
V>Но утиная типизация или нет перпендикулярна статической типизации, прикинь? ))
Я в курсе.
V>Мы тут говорили о коде как о данных.
V>Это появилось только с метаклассами в Смолтолке-80.
V>До этого никакой рефлексии не было.
Рефлексия ортогональна коду-как-данным. В лиспе есть код-как-данные, но рефлексии может и не быть.
В раннем дотнете рефлексия была, а кода-как-данных не было.
Код-как-данные — это Expression<T>. Рефлексия — System.Runtime.Reflection.
V>Да какие проблемы (мать, мать, мать...) — опиши сам объект как неизменяемый.
V>Тебе показать, как описывать иммутабельные объекты в С++?
Ага, вижу, начинается понимание
V>Но тип аргумента-объекта уже должен уже быть? ))
Конечно.
S>>В качестве упражнения можете попробовать спроектировать на С++ пару абстрактных классов — изменяемый вектор и immutable вектор. В терминах дотнета это были бы IImmutableList<T> и IList<T>. И посмотрите, можно ли их отнаследовать друг от друга; можно ли получить один из другого при помощи модификатора const; понадобится ли вообще модификатор const при проектировании этих классов или при их использовании.
V>1. В дотнете я могу породить мутабельный тип из IImmutableList<T> аж бегом и со свистом.
Породить-то можете, а что вы с ним будете делать дальше?
Вы понимаете, что он будет нарушать спецификацию интерфейса?
V>2. В этой же технике в точности аналогично можно и в С++.
Ага. Осталось понять, при чём тут const, и устыдиться. Упражнение-то выполнить сможете?
V>Еще в прошлые разы я обращал твоё внимание на то, что более выгодно с практической точки зрения, чтобы не твой код требовал иммутабельности объекта, а чтобы внешний код требовал, чтобы твой код не мог модифицировать объект.
Пытаетесь подменить задачу. Знакомо.
V>Почему так? — по очевидной причине, ведь твой код тогда сможет работать как с тру-иммутабельными объектами, так и с неиммутабльными при рождении, но которые решили сделать иммутабельными "чуть позже". Например, некий достаточно сложно инициализируемый ветвистый словарь чего-то там.
Ну так для того, чтобы "мой" код корректно работал, мне нужны гарантии не только того, что я не изменяю объект, но и что никто другой его тоже не изменяет.
К примеру, когда этот развесистый словарь является ключом в другом словаре.
V>В целях эффективности в момент наполнения словарь может быть мутабельным, а потом в какой-то момент владение передаётся константной ссылке/указателю и ву а ля.
V>В отсутствии const в этом сценарии потребуется два типа — мутальный и иммутабельный, где готовый мутабельный словарь копируется в иммутабельный, с соответствующими накладными расходами и прессингом на память.
Фантазии, фантазии. Посмотрите, как устроен ImmutableDictionary в дотнете. Там нет const, и при этом "накладные расходы и прессинг на память" ровно такие же, как в вашем сценарии.
В С+ в этом сценарии достаточно скопировать ссылку/указатель.
Ну нет конечно, недостаточно. С++ не даст мне гарантии, что кто-то не поменяет этот словарь после того, как я заложился на его неизменность.
Перед тем, как рассуждать об эффективности кода, нужно обеспечить его корректность.
V>RTFM чего именно?
Истории Паскаля.
V>Сам же привёл пример конкретной системы gemstone, т.е. имелась ввиду соответствующая инфраструктура в этой системе.
Инфраструктура построена на каких-то основах.
S>>Ну, так как мне воспользоваться этим кодом изнутри VB?
V>Языками более низкого уровня.
V>Или расписать некий фреймворк оперирования памятью в некоей библиотеке и сделать импорт функций в VB (есть такая возможность), чтобы иметь требуемый инструментарий.
V>Например, можно взять готовую либу CRT.
Давайте пример кода на VB, который при помощи готовой либы CRT отправляет пользовательский предикат на сервер для исполнения.
V>Потому что в объектных файлах содержится код и данные со всей сопутствующей метаинформацией.
Это совершенно никак не связано с возможностями по манипуляции кодом как данными изнутри языка программирования.
Я не понимаю, почему эта очевидная вещь до вас не доходит. Компиляция в .obj была принята для практически 100% языков программирования; при этом возможностей мета-манипуляций в рантайме не было практически нигде.
V>Если требуется посылать код другому устройству, то требуется переносимость кода.
В первую очередь требуется возможность этот код описать в посылаемом виде.
V>Я уже начинаю подозревать, что ты видишь возможность переносимости кода только в виде его AST-представления.
Вы перескакиваете через ступеньку, пытаясь рассуждать об исполнении, как будто возможность породить такой код у вас есть.
Это во-первых.
Во-вторых, для систем типа GemStone возможность получить код в виде AST, а не байт-кода, является необходимостью.
Потому, что предикат не исполняется напрямую. СУБД на основе предиката строит план исполнения запроса, в котором одна часть предиката будет ключом поиска в индексе, другая будет фильтром по этому индексу, а третья будет вычисляться по связанной с индексом таблице. Это вам ликбез на пальцах.
Предикат, записанный в виде императивного байт-кода, таким преобразованиям поддаётся плохо либо никак.
V>В общем, твоя попытка рассуждать о банальностях как об откровениях малость сбивает меня с толку. ))
Ну так а что делать, если вы не понимаете элементарных вещей?
V>Провожу ликбез.
V>Хосподя, Алан Кей тот еще болтун, навроде тебя.
V>Причём, болтуном он стал гораздо позже... в попытках, не знаю, придать некую публичную окраску своей роли в этой работе.
А-а, ну вы-то, конечно, лучше Кея знаете, как там всё обстояло.
V>Такие монстры как Голдберг служили неплохим противовесом этому, в общем-то, нубу-самоучке.
А что, Голдберг где-то заявляла, что язык Smalltalk был вдохновлён Фортом?
V>Какой там в опу Лисп? ))
Да почитайте же уже The Early History of Smalltalk и перестаньте позориться.
V>Похоже, ты не трогал толком ни Смолтолк, ни Лисп, ни Форт, не понимаешь внутренней механики этих языков, но пытаешься фантазировать.
Вы всё время путаете дизайн языков с внутренней механикой компилятора и рантайма.
V>Рассуждать о Лиспе можно было лишь в последней версии Смолтолка-80, когда добавили метаклассы.
V>ООП в виде CLOS привнесли в 70-х в виде стандарта, напомню, и это всего лишь эмуляция ООП на библиотечном уровне.
V>Но концепции обкатывались, не без этого.
V>Т.е. в изначальном Лиспе никаких метаклассов не было, как и не было в Лиспе ООП, ес-но, были только ф-ии как первоклассные объекты (сам код) и данные в виде однонаправленно связанных встроенных в систему списков.
Конечно не было. Именно это Кей и реализовал — ему хотелось сделать систему, которая была построена на минимуме примитивов, как Лисп, но с ООП. Если бы в Лиспе было ООП, то Smalltalk бы и не понадобился.
V>Просто ссылаться на Лисп однажды стало модно, бгг, и этот болтун не преминул.
V>Курить, что есть шитый код.
Я знаю, что такое шитый код. Какое отношение он имеет к дизайну языка Smalltalk?
V>Пример развития Форта до переносимости м/у устройствами.
По-прежнему не понимаю, какое отношение Постскрипт с его переносимостью имеет к ООП Алана Кея.
V>Таки, самообразованием принято заниматься самостоятельно, не испытывая терпение коллег.
Ну так займитесь. Почему вы вместо того, чтобы почитать первоисточники, занимаетесь гаданием на МК-51?
S>>Попробуйте реализовать на Паскале, Си, и С++ функцию, которая бы передавала пользовательский предикат на сервер для исполнения.
V>Не вижу технических проблем передать чистую ф-ию или чистое замыкание при наличии соотв. инфраструктуры.
Пример кода в студию. Хотя бы на одном из языков.
V>Напомню, что MS компилятор С++ умеет компиллировать unamanaged C++ код (т.е. безо-всяких managed расширений) в чистый байт-код.
V>(тип генерируемого образа для режима С++/CLI задаётся опциями компилятора)
Ну ок, если вам будет проще — попробуйте изобразить требуемую функциональность на unmanaged C++, скомпилированном в чистый байт-код.
S>>А потом для сравнения попробуйте реализовать то же самое на Lisp или SmallTalk.
V>Ну вот попробуй на Лисп (или другом динамическом языке) без динамического исполнения текстовых исходников, а я поржу.
Посмотрите на то, как устроены макросы Лиспа. Там нет ничего про динамическое исполнение текстовых исходников.
Всё построено на манипуляциях кодом в виде AST. Можете начинать ржать.
V>Да не связана.
V>Такая возможность связана с представлением исполняемого кода, а это представление перпендикулярно языку.
V>Похоже, у тебя каша в голове из св-в языков и конкретных их компиляторов/интерпретаторов.
Всё ровно наоборот — это у вас каша.
V>Посмотри на Схему — вот тебе оптимизирующий компилятор Лиспа.
V>Были убраны возможности рантайм рефлексии кода.
V>Язык, по-сути, тот же.
V>Отличается представлением скомпиллированного кода, а значит, особенностями модели вычисления.
И самое замечательное, что эти особенности модели вычисления никак не влияют на то, о чём я говорю.
Макросы в Схеме всё ещё есть, и это означает, что я могу решить обозначенную задачу и на схеме тоже.
А всё потому, что решают тут свойства языка. Что там под капотом — дело десятое.
V>Не осенило еще? ))
Вижу, что вас не осенило. Причина, я думаю, именно в том, что вы не пробуете сделать то, о чём я говорю.
V>Например, MS Visual Basic for DOS компиллировал в неплохо оптимизированный код (я курочил дизассемблером что он там порождает — да там офигенно, на уровне их же компилятора Си и Паскаля).
Да видел конечно. Толку-то? Ни на одном из этих бейсиков я не могу отправить на сервер код пользовательского предиката.
V>В Лисп точно так же может сосуществовать сколько угодно много независимых ООП-подсистем библиотечного уровня.
Я в курсе. Синтаксис Лиспа настолько ужасен, что его ничем испортить нельзя. Поэтому всякие надстройки над ним выглядят не хуже оригинального лиспа.
И можно делать любое ООП, которое нравится — с наследованием интерфейсов и наследованием реализации, со множественным наследованием и без, с любыми наборами модификаторов "видимости" мемберов. Можно придумывать свойства и события; можно делать наследование экземпляров как в JS; можно обрабатывать неизвестные сообщения, как в Smalltalk.
Правда, всё это будет всё в том же ужасном стиле скобок поверх скобок. Но на фоне остального лисп-кода выделяться не будет.
В общем, Лисп считать ОО-языком бессмысленно.
V>Ты не понимаешь мотива принятия некоторых важных решений в IT в ретроспективе.
V>Проводимый ликбез тебя коробит, но это особенности твоего чванства и ничего более.
Скорее, это особенности вашего чванства.
V>В интересные места ведь тебя отсылаю. ))
Практически во всех этих местах я был. Вы что, всеръёз считаете, что я не в курсе кнутовского MIX-а, или никогда не слышал про Forth?
V>Ключевое тут то, что с помощью базовых примитивов можно расписать сколь угодно сложную систему управления динамической памятью, если по каким-то причинам не устраивает данная изкаробки. В том числе запросто пишется консервативный GC с любой из политик работы, например, какие были приняты в различных реализациях Лиспа — автоматический вызов при исчерпании пула ячеек vs явные вызовы GC.
Повторюсь: ООП-компилятор можно написать на не-ОО языке. Это ничего не говорит об ОО-свойствах этого языка.
V>Исходный GC связывал освобождаемые ячейки CONS в глобальный список свободных ячеек и не умел ничего более.
V>Перемещающи/копирующий GC должен уметь корректировать ссылки, а GC Лиспа той эпохи этого не делал.
V>(Не берусь утверждать за некие потенциальные новые реализации, бо за современным Лиспом не слежу, но десятилетиями оно работало именно так)
Наверняка вы что-то не так поняли. Весь смысл сборщика мусора — в возможности освободить занятую память.
Если сборщик ничего не перемещает, то откуда возьмётся освобождение? И вообще, зачем такой "сборщик" выполнять?
V>Не-а.
Может быть, я чего-то не понимаю. Вас не затруднит найти ссылку на описание того сборщика мусора, о котором вы рассуждаете?
V>По VB и VFoxPro литературы мильон.
Не по VB, а по ООП на VB. Примеры к "паттернам ООП" на каком языке даны?
V>Я говорю не про игнор, а про наложенные ограничения на реализацию парадигмы.
V>Джава наложила то ограничение, что экземпляры пользовательских типов будут всегда располагаться в динамической куче, а такого ограничения в исходной парадигме нет.
V>Это подробности конкретной реализации в неких принятых ограничениях.
Опять вы закапываетесь в подробности конкретной реализации. Зачем ?
В парадигме ООП нет ничего ни про кучу, ни про стек.
V>Сотый раз повторюсь — этого не требовалось.
V>Приличный "почти современный" ООП был реализован еще до Смолтолка, в Алгол-68, и там был функциональный тип и лямбды, т.е. лямбда-исчисление.
V>Я уже подкидывал тебе мысль, что ФП-подход — это подход системы ограничений к процедурному программированию (функциональный тип был известен еще в эпоху процедурного программирования), в то время как ООП-подход — это инструментарий расширения.
Это неудачная мысль.
V>ООП-подход включил известные на тот момент парадигмы целиком.
V>Дальнейшее развитие ООП-парадигмы и соотв. языков шло в области описания таких ограничений, коль они требуются, ср-вами языка.
V>Именно поэтому современные ООП-языки принято считать "мультипарадигменными", хотя это не так, разумеется.
Нет конечно.
S>>В чистом ООП никаких делегатов нет. Есть интерфейсы с определёнными сигнатурами.
V>Мда... Будем разгребать:
Вы просто написали то же самое, что и я, только более подробно.
V>А разгрести эту кашу можно лишь помня, что в ФП действует "утиная типизация", то бишь, такие функциональные объекты с одинаковой сигнатурой считаются одинаковым типом.
V>И оно не просто так, а потому что бинарная механика реализаций таких объектов идентична, т.е. бинарной ошибки реинтерпретации памяти быть не может.
Вы ставите телегу впереди лошади. В ФП совместимость между собой функций с одинаковой сигнатурой является частью дизайна языка. Бинарная механика просто построена так, чтобы удовлетворять этому требованию.
V>В дотнете, однако, делегаты с одинаковой сигнатурой неприводимы друг к другу, хотя обладают такой же бинарной совместимостью.
V>В этом месте ФП в дотнете заканчивается, толком не начавшись.
S>>А несовместимость делегатов с одинаковой сигнатурой в рамках ООП не является самостоятельной особенностью. Она скорее связана с выбором между утиной типизацией и номинативной типизацией.
V>Ага, т.е. ты хорошо понимаешь про утиную типизацию, просто Ваньку тут валяешь.
Конечно понимаю.
V>Номинативная типизация — это тоже левое ограничение, не требуемое для реализации концепции ООП.
V>Это просто такое решение, ограничиться именно ею.
V>Хотя, базовая машинка способна вызывать методы не того объекта через свою систему команд, а вся "загвоздка" была бы только в верификации сгенерированного компилятором кода, верно?
V>Т.е. всего-то надо было снабдить верификатор возможностью утиной типизации неких типов, помеченных атрибутом как "делегат" (их даже не обязательно было наследовать от базового Delegate, достаточно было наследовать прямо от Object, а остальные ср-ва можно было бы получать от метакласса).
V>Плюсом было бы то, что загрузчик-верификатор мог бы эффективно "склеивать" попадающие под утиную типизацию такие функциональные типы, т.е. не засорять память копиями одного и того же бинарного кода!
V>Типов-делегатов ведь овердохрена, но уникальных сигнатур потенциально меньше.
V>Все эти Action<> и Func<>, таки, выглядят крайне ущербно — как попытка залатать исходно присутствующую дыру во фреймворке.
Все эти Action<> и Func<> как раз и являются реализацией ровно вашей же идеи путём повторного использования уже существующих компонентов инфраструктуры. То самое склеивание функциональных типов и всё такое.
V>1. На шаблонном уровне совместимы.
Шаблоны тоже не являются частью ООП-парадигмы. Вы критикуете решение, принятое в системе, где шаблонов не было, при этом указывая на систему, где аналогичная дыра заклеивается при помощи шаблона
V>2. Компилятор склеивает идентичный бинарный код различных типов.
Это вообще деталь реализации.
V>А в дотнете ни ограничения в генериках на абстрактную сигатуру делегата не задать, ни описать эту абстрактную сигнатуру явно, скажем, где-то так:
V>
V>bool Find<T>(ICollection<T> collection, bool predicate(T arg)) {...}
V>
Не понял, а в чём тут проблема?
V>Об костыли Action<> и Func<> мы спотыкаемся сразу же, когда речь идёт о модификаторах, например ref/in/out, а недавно еще появились scope и readonly.
V>Плюс аргументами генериков не могут быть ref-структуры, поэтому, приходится описывать типы делегатов ручками как в старые добрые времена, когда генериков еще не было, бгг...
Это всё не относится к вопросам ООП парадигмы, а скорее о проектировании реальной платформы в условиях противоречивых ограничений.
V>Но утиная типизация или нет перпендикулярна статической типизации, прикинь? ))
Я в курсе.
V>Мы тут говорили о коде как о данных.
V>Это появилось только с метаклассами в Смолтолке-80.
V>До этого никакой рефлексии не было.
Рефлексия ортогональна коду-как-данным. В лиспе есть код-как-данные, но рефлексии может и не быть.
В раннем дотнете рефлексия была, а кода-как-данных не было.
Код-как-данные — это Expression<T>. Рефлексия — System.Runtime.Reflection.
V>Да какие проблемы (мать, мать, мать...) — опиши сам объект как неизменяемый.
V>Тебе показать, как описывать иммутабельные объекты в С++?
Ага, вижу, начинается понимание
V>Но тип аргумента-объекта уже должен уже быть? ))
Конечно.
S>>В качестве упражнения можете попробовать спроектировать на С++ пару абстрактных классов — изменяемый вектор и immutable вектор. В терминах дотнета это были бы IImmutableList<T> и IList<T>. И посмотрите, можно ли их отнаследовать друг от друга; можно ли получить один из другого при помощи модификатора const; понадобится ли вообще модификатор const при проектировании этих классов или при их использовании.
V>1. В дотнете я могу породить мутабельный тип из IImmutableList<T> аж бегом и со свистом.
Породить-то можете, а что вы с ним будете делать дальше?
Вы понимаете, что он будет нарушать спецификацию интерфейса?
V>2. В этой же технике в точности аналогично можно и в С++.
Ага. Осталось понять, при чём тут const, и устыдиться. Упражнение-то выполнить сможете?
V>Еще в прошлые разы я обращал твоё внимание на то, что более выгодно с практической точки зрения, чтобы не твой код требовал иммутабельности объекта, а чтобы внешний код требовал, чтобы твой код не мог модифицировать объект.
Пытаетесь подменить задачу. Знакомо.
V>Почему так? — по очевидной причине, ведь твой код тогда сможет работать как с тру-иммутабельными объектами, так и с неиммутабльными при рождении, но которые решили сделать иммутабельными "чуть позже". Например, некий достаточно сложно инициализируемый ветвистый словарь чего-то там.
Ну так для того, чтобы "мой" код корректно работал, мне нужны гарантии не только того, что я не изменяю объект, но и что никто другой его тоже не изменяет.
К примеру, когда этот развесистый словарь является ключом в другом словаре.
V>В целях эффективности в момент наполнения словарь может быть мутабельным, а потом в какой-то момент владение передаётся константной ссылке/указателю и ву а ля.
V>В отсутствии const в этом сценарии потребуется два типа — мутальный и иммутабельный, где готовый мутабельный словарь копируется в иммутабельный, с соответствующими накладными расходами и прессингом на память.
Фантазии, фантазии. Посмотрите, как устроен ImmutableDictionary в дотнете. Там нет const, и при этом "накладные расходы и прессинг на память" ровно такие же, как в вашем сценарии.
В С+ в этом сценарии достаточно скопировать ссылку/указатель.
Ну нет конечно, недостаточно. С++ не даст мне гарантии, что кто-то не поменяет этот словарь после того, как я заложился на его неизменность.
Перед тем, как рассуждать об эффективности кода, нужно обеспечить его корректность.
Re[9]: понимание ООП Алана Кея
Здравствуйте, vdimas, Вы писали:
V>RTFM чего именно?
Истории Паскаля.
V>Сам же привёл пример конкретной системы gemstone, т.е. имелась ввиду соответствующая инфраструктура в этой системе.
Инфраструктура построена на каких-то основах.
S>>Ну, так как мне воспользоваться этим кодом изнутри VB?
V>Языками более низкого уровня.
V>Или расписать некий фреймворк оперирования памятью в некоей библиотеке и сделать импорт функций в VB (есть такая возможность), чтобы иметь требуемый инструментарий.
V>Например, можно взять готовую либу CRT.
Давайте пример кода на VB, который при помощи готовой либы CRT отправляет пользовательский предикат на сервер для исполнения.
V>Потому что в объектных файлах содержится код и данные со всей сопутствующей метаинформацией.
Это совершенно никак не связано с возможностями по манипуляции кодом как данными изнутри языка программирования.
Я не понимаю, почему эта очевидная вещь до вас не доходит. Компиляция в .obj была принята для практически 100% языков программирования; при этом возможностей мета-манипуляций в рантайме не было практически нигде.
V>Если требуется посылать код другому устройству, то требуется переносимость кода.
В первую очередь требуется возможность этот код описать в посылаемом виде.
V>Я уже начинаю подозревать, что ты видишь возможность переносимости кода только в виде его AST-представления.
Вы перескакиваете через ступеньку, пытаясь рассуждать об исполнении, как будто возможность породить такой код у вас есть.
Это во-первых.
Во-вторых, для систем типа GemStone возможность получить код в виде AST, а не байт-кода, является необходимостью.
Потому, что предикат не исполняется напрямую. СУБД на основе предиката строит план исполнения запроса, в котором одна часть предиката будет ключом поиска в индексе, другая будет фильтром по этому индексу, а третья будет вычисляться по связанной с индексом таблице. Это вам ликбез на пальцах.
Предикат, записанный в виде императивного байт-кода, таким преобразованиям поддаётся плохо либо никак.
V>В общем, твоя попытка рассуждать о банальностях как об откровениях малость сбивает меня с толку. ))
Ну так а что делать, если вы не понимаете элементарных вещей?
V>Провожу ликбез.
V>Хосподя, Алан Кей тот еще болтун, навроде тебя.
V>Причём, болтуном он стал гораздо позже... в попытках, не знаю, придать некую публичную окраску своей роли в этой работе.
А-а, ну вы-то, конечно, лучше Кея знаете, как там всё обстояло.
V>Такие монстры как Голдберг служили неплохим противовесом этому, в общем-то, нубу-самоучке.
А что, Голдберг где-то заявляла, что язык Smalltalk был вдохновлён Фортом?
V>Какой там в опу Лисп? ))
Да почитайте же уже The Early History of Smalltalk и перестаньте позориться.
V>Похоже, ты не трогал толком ни Смолтолк, ни Лисп, ни Форт, не понимаешь внутренней механики этих языков, но пытаешься фантазировать.
Вы всё время путаете дизайн языков с внутренней механикой компилятора и рантайма.
V>Рассуждать о Лиспе можно было лишь в последней версии Смолтолка-80, когда добавили метаклассы.
V>ООП в виде CLOS привнесли в 70-х в виде стандарта, напомню, и это всего лишь эмуляция ООП на библиотечном уровне.
V>Но концепции обкатывались, не без этого.
V>Т.е. в изначальном Лиспе никаких метаклассов не было, как и не было в Лиспе ООП, ес-но, были только ф-ии как первоклассные объекты (сам код) и данные в виде однонаправленно связанных встроенных в систему списков.
Конечно не было. Именно это Кей и реализовал — ему хотелось сделать систему, которая была построена на минимуме примитивов, как Лисп, но с ООП. Если бы в Лиспе было ООП, то Smalltalk бы и не понадобился.
V>Просто ссылаться на Лисп однажды стало модно, бгг, и этот болтун не преминул.
V>Курить, что есть шитый код.
Я знаю, что такое шитый код. Какое отношение он имеет к дизайну языка Smalltalk?
V>Пример развития Форта до переносимости м/у устройствами.
По-прежнему не понимаю, какое отношение Постскрипт с его переносимостью имеет к ООП Алана Кея.
V>Таки, самообразованием принято заниматься самостоятельно, не испытывая терпение коллег.
Ну так займитесь. Почему вы вместо того, чтобы почитать первоисточники, занимаетесь гаданием на МК-51?
S>>Попробуйте реализовать на Паскале, Си, и С++ функцию, которая бы передавала пользовательский предикат на сервер для исполнения.
V>Не вижу технических проблем передать чистую ф-ию или чистое замыкание при наличии соотв. инфраструктуры.
Пример кода в студию. Хотя бы на одном из языков.
V>Напомню, что MS компилятор С++ умеет компиллировать unamanaged C++ код (т.е. безо-всяких managed расширений) в чистый байт-код.
V>(тип генерируемого образа для режима С++/CLI задаётся опциями компилятора)
Ну ок, если вам будет проще — попробуйте изобразить требуемую функциональность на unmanaged C++, скомпилированном в чистый байт-код.
S>>А потом для сравнения попробуйте реализовать то же самое на Lisp или SmallTalk.
V>Ну вот попробуй на Лисп (или другом динамическом языке) без динамического исполнения текстовых исходников, а я поржу.
Посмотрите на то, как устроены макросы Лиспа. Там нет ничего про динамическое исполнение текстовых исходников.
Всё построено на манипуляциях кодом в виде AST. Можете начинать ржать.
V>Да не связана.
V>Такая возможность связана с представлением исполняемого кода, а это представление перпендикулярно языку.
V>Похоже, у тебя каша в голове из св-в языков и конкретных их компиляторов/интерпретаторов.
Всё ровно наоборот — это у вас каша.
V>Посмотри на Схему — вот тебе оптимизирующий компилятор Лиспа.
V>Были убраны возможности рантайм рефлексии кода.
V>Язык, по-сути, тот же.
V>Отличается представлением скомпиллированного кода, а значит, особенностями модели вычисления.
И самое замечательное, что эти особенности модели вычисления никак не влияют на то, о чём я говорю.
Макросы в Схеме всё ещё есть, и это означает, что я могу решить обозначенную задачу и на схеме тоже.
А всё потому, что решают тут свойства языка. Что там под капотом — дело десятое.
V>Не осенило еще? ))
Вижу, что вас не осенило. Причина, я думаю, именно в том, что вы не пробуете сделать то, о чём я говорю.
V>Например, MS Visual Basic for DOS компиллировал в неплохо оптимизированный код (я курочил дизассемблером что он там порождает — да там офигенно, на уровне их же компилятора Си и Паскаля).
Да видел конечно. Толку-то? Ни на одном из этих бейсиков я не могу отправить на сервер код пользовательского предиката.
V>В Лисп точно так же может сосуществовать сколько угодно много независимых ООП-подсистем библиотечного уровня.
Я в курсе. Синтаксис Лиспа настолько ужасен, что его ничем испортить нельзя. Поэтому всякие надстройки над ним выглядят не хуже оригинального лиспа.
И можно делать любое ООП, которое нравится — с наследованием интерфейсов и наследованием реализации, со множественным наследованием и без, с любыми наборами модификаторов "видимости" мемберов. Можно придумывать свойства и события; можно делать наследование экземпляров как в JS; можно обрабатывать неизвестные сообщения, как в Smalltalk.
Правда, всё это будет всё в том же ужасном стиле скобок поверх скобок. Но на фоне остального лисп-кода выделяться не будет.
В общем, Лисп считать ОО-языком бессмысленно.
V>Ты не понимаешь мотива принятия некоторых важных решений в IT в ретроспективе.
V>Проводимый ликбез тебя коробит, но это особенности твоего чванства и ничего более.
Скорее, это особенности вашего чванства.
V>В интересные места ведь тебя отсылаю. ))
Практически во всех этих местах я был. Вы что, всеръёз считаете, что я не в курсе кнутовского MIX-а, или никогда не слышал про Forth?
V>Ключевое тут то, что с помощью базовых примитивов можно расписать сколь угодно сложную систему управления динамической памятью, если по каким-то причинам не устраивает данная изкаробки. В том числе запросто пишется консервативный GC с любой из политик работы, например, какие были приняты в различных реализациях Лиспа — автоматический вызов при исчерпании пула ячеек vs явные вызовы GC.
Повторюсь: ООП-компилятор можно написать на не-ОО языке. Это ничего не говорит об ОО-свойствах этого языка.
V>Исходный GC связывал освобождаемые ячейки CONS в глобальный список свободных ячеек и не умел ничего более.
V>Перемещающи/копирующий GC должен уметь корректировать ссылки, а GC Лиспа той эпохи этого не делал.
V>(Не берусь утверждать за некие потенциальные новые реализации, бо за современным Лиспом не слежу, но десятилетиями оно работало именно так)
Наверняка вы что-то не так поняли. Весь смысл сборщика мусора — в возможности освободить занятую память.
Если сборщик ничего не перемещает, то откуда возьмётся освобождение? И вообще, зачем такой "сборщик" выполнять?
V>Не-а.
Может быть, я чего-то не понимаю. Вас не затруднит найти ссылку на описание того сборщика мусора, о котором вы рассуждаете?
V>По VB и VFoxPro литературы мильон.
Не по VB, а по ООП на VB. Примеры к "паттернам ООП" на каком языке даны?
V>Я говорю не про игнор, а про наложенные ограничения на реализацию парадигмы.
V>Джава наложила то ограничение, что экземпляры пользовательских типов будут всегда располагаться в динамической куче, а такого ограничения в исходной парадигме нет.
V>Это подробности конкретной реализации в неких принятых ограничениях.
Опять вы закапываетесь в подробности конкретной реализации. Зачем ?
В парадигме ООП нет ничего ни про кучу, ни про стек.
V>Сотый раз повторюсь — этого не требовалось.
V>Приличный "почти современный" ООП был реализован еще до Смолтолка, в Алгол-68, и там был функциональный тип и лямбды, т.е. лямбда-исчисление.
V>Я уже подкидывал тебе мысль, что ФП-подход — это подход системы ограничений к процедурному программированию (функциональный тип был известен еще в эпоху процедурного программирования), в то время как ООП-подход — это инструментарий расширения.
Это неудачная мысль.
V>ООП-подход включил известные на тот момент парадигмы целиком.
V>Дальнейшее развитие ООП-парадигмы и соотв. языков шло в области описания таких ограничений, коль они требуются, ср-вами языка.
V>Именно поэтому современные ООП-языки принято считать "мультипарадигменными", хотя это не так, разумеется.
Нет конечно.
S>>В чистом ООП никаких делегатов нет. Есть интерфейсы с определёнными сигнатурами.
V>Мда... Будем разгребать:
Вы просто написали то же самое, что и я, только более подробно.
V>А разгрести эту кашу можно лишь помня, что в ФП действует "утиная типизация", то бишь, такие функциональные объекты с одинаковой сигнатурой считаются одинаковым типом.
V>И оно не просто так, а потому что бинарная механика реализаций таких объектов идентична, т.е. бинарной ошибки реинтерпретации памяти быть не может.
Вы ставите телегу впереди лошади. В ФП совместимость между собой функций с одинаковой сигнатурой является частью дизайна языка. Бинарная механика просто построена так, чтобы удовлетворять этому требованию.
V>В дотнете, однако, делегаты с одинаковой сигнатурой неприводимы друг к другу, хотя обладают такой же бинарной совместимостью.
V>В этом месте ФП в дотнете заканчивается, толком не начавшись.
S>>А несовместимость делегатов с одинаковой сигнатурой в рамках ООП не является самостоятельной особенностью. Она скорее связана с выбором между утиной типизацией и номинативной типизацией.
V>Ага, т.е. ты хорошо понимаешь про утиную типизацию, просто Ваньку тут валяешь.
Конечно понимаю.
V>Номинативная типизация — это тоже левое ограничение, не требуемое для реализации концепции ООП.
V>Это просто такое решение, ограничиться именно ею.
V>Хотя, базовая машинка способна вызывать методы не того объекта через свою систему команд, а вся "загвоздка" была бы только в верификации сгенерированного компилятором кода, верно?
V>Т.е. всего-то надо было снабдить верификатор возможностью утиной типизации неких типов, помеченных атрибутом как "делегат" (их даже не обязательно было наследовать от базового Delegate, достаточно было наследовать прямо от Object, а остальные ср-ва можно было бы получать от метакласса).
V>Плюсом было бы то, что загрузчик-верификатор мог бы эффективно "склеивать" попадающие под утиную типизацию такие функциональные типы, т.е. не засорять память копиями одного и того же бинарного кода!
V>Типов-делегатов ведь овердохрена, но уникальных сигнатур потенциально меньше.
V>Все эти Action<> и Func<>, таки, выглядят крайне ущербно — как попытка залатать исходно присутствующую дыру во фреймворке.
Все эти Action<> и Func<> как раз и являются реализацией ровно вашей же идеи путём повторного использования уже существующих компонентов инфраструктуры. То самое склеивание функциональных типов и всё такое.
V>1. На шаблонном уровне совместимы.
Шаблоны тоже не являются частью ООП-парадигмы. Вы критикуете решение, принятое в системе, где шаблонов не было, при этом указывая на систему, где аналогичная дыра заклеивается при помощи шаблона
V>2. Компилятор склеивает идентичный бинарный код различных типов.
Это вообще деталь реализации.
V>А в дотнете ни ограничения в генериках на абстрактную сигатуру делегата не задать, ни описать эту абстрактную сигнатуру явно, скажем, где-то так:
V>
Не понял, а в чём тут проблема?
V>Об костыли Action<> и Func<> мы спотыкаемся сразу же, когда речь идёт о модификаторах, например ref/in/out, а недавно еще появились scope и readonly.
V>Плюс аргументами генериков не могут быть ref-структуры, поэтому, приходится описывать типы делегатов ручками как в старые добрые времена, когда генериков еще не было, бгг...
Это всё не относится к вопросам ООП парадигмы, а скорее о проектировании реальной платформы в условиях противоречивых ограничений.
V>Но утиная типизация или нет перпендикулярна статической типизации, прикинь? ))
Я в курсе.
V>Мы тут говорили о коде как о данных.
V>Это появилось только с метаклассами в Смолтолке-80.
V>До этого никакой рефлексии не было.
Рефлексия ортогональна коду-как-данным. В лиспе есть код-как-данные, но рефлексии может и не быть.
В раннем дотнете рефлексия была, а кода-как-данных не было.
Код-как-данные — это Expression<T>. Рефлексия — System.Runtime.Reflection.
V>Да какие проблемы (мать, мать, мать...) — опиши сам объект как неизменяемый.
V>Тебе показать, как описывать иммутабельные объекты в С++?
Ага, вижу, начинается понимание
V>Но тип аргумента-объекта уже должен уже быть? ))
Конечно.
S>>В качестве упражнения можете попробовать спроектировать на С++ пару абстрактных классов — изменяемый вектор и immutable вектор. В терминах дотнета это были бы IImmutableList<T> и IList<T>. И посмотрите, можно ли их отнаследовать друг от друга; можно ли получить один из другого при помощи модификатора const; понадобится ли вообще модификатор const при проектировании этих классов или при их использовании.
V>1. В дотнете я могу породить мутабельный тип из IImmutableList<T> аж бегом и со свистом.
Породить-то можете, а что вы с ним будете делать дальше?
Вы понимаете, что он будет нарушать спецификацию интерфейса?
V>2. В этой же технике в точности аналогично можно и в С++.
Ага. Осталось понять, при чём тут const, и устыдиться. Упражнение-то выполнить сможете?
V>Еще в прошлые разы я обращал твоё внимание на то, что более выгодно с практической точки зрения, чтобы не твой код требовал иммутабельности объекта, а чтобы внешний код требовал, чтобы твой код не мог модифицировать объект.
Пытаетесь подменить задачу. Знакомо.
V>Почему так? — по очевидной причине, ведь твой код тогда сможет работать как с тру-иммутабельными объектами, так и с неиммутабльными при рождении, но которые решили сделать иммутабельными "чуть позже". Например, некий достаточно сложно инициализируемый ветвистый словарь чего-то там.
Ну так для того, чтобы "мой" код корректно работал, мне нужны гарантии не только того, что я не изменяю объект, но и что никто другой его тоже не изменяет.
К примеру, когда этот развесистый словарь является ключом в другом словаре.
V>В целях эффективности в момент наполнения словарь может быть мутабельным, а потом в какой-то момент владение передаётся константной ссылке/указателю и ву а ля.
V>В отсутствии const в этом сценарии потребуется два типа — мутальный и иммутабельный, где готовый мутабельный словарь копируется в иммутабельный, с соответствующими накладными расходами и прессингом на память.
Фантазии, фантазии. Посмотрите, как устроен ImmutableDictionary в дотнете. Там нет const, и при этом "накладные расходы и прессинг на память" ровно такие же, как в вашем сценарии.
V>В С+ в этом сценарии достаточно скопировать ссылку/указатель.
Ну нет конечно, недостаточно. С++ не даст мне гарантии, что кто-то не поменяет этот словарь после того, как я заложился на его неизменность.
Перед тем, как рассуждать об эффективности кода, нужно обеспечить его корректность.
V>RTFM чего именно?
Истории Паскаля.
V>Сам же привёл пример конкретной системы gemstone, т.е. имелась ввиду соответствующая инфраструктура в этой системе.
Инфраструктура построена на каких-то основах.
S>>Ну, так как мне воспользоваться этим кодом изнутри VB?
V>Языками более низкого уровня.
V>Или расписать некий фреймворк оперирования памятью в некоей библиотеке и сделать импорт функций в VB (есть такая возможность), чтобы иметь требуемый инструментарий.
V>Например, можно взять готовую либу CRT.
Давайте пример кода на VB, который при помощи готовой либы CRT отправляет пользовательский предикат на сервер для исполнения.
V>Потому что в объектных файлах содержится код и данные со всей сопутствующей метаинформацией.
Это совершенно никак не связано с возможностями по манипуляции кодом как данными изнутри языка программирования.
Я не понимаю, почему эта очевидная вещь до вас не доходит. Компиляция в .obj была принята для практически 100% языков программирования; при этом возможностей мета-манипуляций в рантайме не было практически нигде.
V>Если требуется посылать код другому устройству, то требуется переносимость кода.
В первую очередь требуется возможность этот код описать в посылаемом виде.
V>Я уже начинаю подозревать, что ты видишь возможность переносимости кода только в виде его AST-представления.
Вы перескакиваете через ступеньку, пытаясь рассуждать об исполнении, как будто возможность породить такой код у вас есть.
Это во-первых.
Во-вторых, для систем типа GemStone возможность получить код в виде AST, а не байт-кода, является необходимостью.
Потому, что предикат не исполняется напрямую. СУБД на основе предиката строит план исполнения запроса, в котором одна часть предиката будет ключом поиска в индексе, другая будет фильтром по этому индексу, а третья будет вычисляться по связанной с индексом таблице. Это вам ликбез на пальцах.
Предикат, записанный в виде императивного байт-кода, таким преобразованиям поддаётся плохо либо никак.
V>В общем, твоя попытка рассуждать о банальностях как об откровениях малость сбивает меня с толку. ))
Ну так а что делать, если вы не понимаете элементарных вещей?
V>Провожу ликбез.
V>Хосподя, Алан Кей тот еще болтун, навроде тебя.
V>Причём, болтуном он стал гораздо позже... в попытках, не знаю, придать некую публичную окраску своей роли в этой работе.
А-а, ну вы-то, конечно, лучше Кея знаете, как там всё обстояло.
V>Такие монстры как Голдберг служили неплохим противовесом этому, в общем-то, нубу-самоучке.
А что, Голдберг где-то заявляла, что язык Smalltalk был вдохновлён Фортом?
V>Какой там в опу Лисп? ))
Да почитайте же уже The Early History of Smalltalk и перестаньте позориться.
V>Похоже, ты не трогал толком ни Смолтолк, ни Лисп, ни Форт, не понимаешь внутренней механики этих языков, но пытаешься фантазировать.
Вы всё время путаете дизайн языков с внутренней механикой компилятора и рантайма.
V>Рассуждать о Лиспе можно было лишь в последней версии Смолтолка-80, когда добавили метаклассы.
V>ООП в виде CLOS привнесли в 70-х в виде стандарта, напомню, и это всего лишь эмуляция ООП на библиотечном уровне.
V>Но концепции обкатывались, не без этого.
V>Т.е. в изначальном Лиспе никаких метаклассов не было, как и не было в Лиспе ООП, ес-но, были только ф-ии как первоклассные объекты (сам код) и данные в виде однонаправленно связанных встроенных в систему списков.
Конечно не было. Именно это Кей и реализовал — ему хотелось сделать систему, которая была построена на минимуме примитивов, как Лисп, но с ООП. Если бы в Лиспе было ООП, то Smalltalk бы и не понадобился.
V>Просто ссылаться на Лисп однажды стало модно, бгг, и этот болтун не преминул.
V>Курить, что есть шитый код.
Я знаю, что такое шитый код. Какое отношение он имеет к дизайну языка Smalltalk?
V>Пример развития Форта до переносимости м/у устройствами.
По-прежнему не понимаю, какое отношение Постскрипт с его переносимостью имеет к ООП Алана Кея.
V>Таки, самообразованием принято заниматься самостоятельно, не испытывая терпение коллег.
Ну так займитесь. Почему вы вместо того, чтобы почитать первоисточники, занимаетесь гаданием на МК-51?
S>>Попробуйте реализовать на Паскале, Си, и С++ функцию, которая бы передавала пользовательский предикат на сервер для исполнения.
V>Не вижу технических проблем передать чистую ф-ию или чистое замыкание при наличии соотв. инфраструктуры.
Пример кода в студию. Хотя бы на одном из языков.
V>Напомню, что MS компилятор С++ умеет компиллировать unamanaged C++ код (т.е. безо-всяких managed расширений) в чистый байт-код.
V>(тип генерируемого образа для режима С++/CLI задаётся опциями компилятора)
Ну ок, если вам будет проще — попробуйте изобразить требуемую функциональность на unmanaged C++, скомпилированном в чистый байт-код.
S>>А потом для сравнения попробуйте реализовать то же самое на Lisp или SmallTalk.
V>Ну вот попробуй на Лисп (или другом динамическом языке) без динамического исполнения текстовых исходников, а я поржу.
Посмотрите на то, как устроены макросы Лиспа. Там нет ничего про динамическое исполнение текстовых исходников.
Всё построено на манипуляциях кодом в виде AST. Можете начинать ржать.
V>Да не связана.
V>Такая возможность связана с представлением исполняемого кода, а это представление перпендикулярно языку.
V>Похоже, у тебя каша в голове из св-в языков и конкретных их компиляторов/интерпретаторов.
Всё ровно наоборот — это у вас каша.
V>Посмотри на Схему — вот тебе оптимизирующий компилятор Лиспа.
V>Были убраны возможности рантайм рефлексии кода.
V>Язык, по-сути, тот же.
V>Отличается представлением скомпиллированного кода, а значит, особенностями модели вычисления.
И самое замечательное, что эти особенности модели вычисления никак не влияют на то, о чём я говорю.
Макросы в Схеме всё ещё есть, и это означает, что я могу решить обозначенную задачу и на схеме тоже.
А всё потому, что решают тут свойства языка. Что там под капотом — дело десятое.
V>Не осенило еще? ))
Вижу, что вас не осенило. Причина, я думаю, именно в том, что вы не пробуете сделать то, о чём я говорю.
V>Например, MS Visual Basic for DOS компиллировал в неплохо оптимизированный код (я курочил дизассемблером что он там порождает — да там офигенно, на уровне их же компилятора Си и Паскаля).
Да видел конечно. Толку-то? Ни на одном из этих бейсиков я не могу отправить на сервер код пользовательского предиката.
V>В Лисп точно так же может сосуществовать сколько угодно много независимых ООП-подсистем библиотечного уровня.
Я в курсе. Синтаксис Лиспа настолько ужасен, что его ничем испортить нельзя. Поэтому всякие надстройки над ним выглядят не хуже оригинального лиспа.
И можно делать любое ООП, которое нравится — с наследованием интерфейсов и наследованием реализации, со множественным наследованием и без, с любыми наборами модификаторов "видимости" мемберов. Можно придумывать свойства и события; можно делать наследование экземпляров как в JS; можно обрабатывать неизвестные сообщения, как в Smalltalk.
Правда, всё это будет всё в том же ужасном стиле скобок поверх скобок. Но на фоне остального лисп-кода выделяться не будет.
В общем, Лисп считать ОО-языком бессмысленно.
V>Ты не понимаешь мотива принятия некоторых важных решений в IT в ретроспективе.
V>Проводимый ликбез тебя коробит, но это особенности твоего чванства и ничего более.
Скорее, это особенности вашего чванства.
V>В интересные места ведь тебя отсылаю. ))
Практически во всех этих местах я был. Вы что, всеръёз считаете, что я не в курсе кнутовского MIX-а, или никогда не слышал про Forth?
V>Ключевое тут то, что с помощью базовых примитивов можно расписать сколь угодно сложную систему управления динамической памятью, если по каким-то причинам не устраивает данная изкаробки. В том числе запросто пишется консервативный GC с любой из политик работы, например, какие были приняты в различных реализациях Лиспа — автоматический вызов при исчерпании пула ячеек vs явные вызовы GC.
Повторюсь: ООП-компилятор можно написать на не-ОО языке. Это ничего не говорит об ОО-свойствах этого языка.
V>Исходный GC связывал освобождаемые ячейки CONS в глобальный список свободных ячеек и не умел ничего более.
V>Перемещающи/копирующий GC должен уметь корректировать ссылки, а GC Лиспа той эпохи этого не делал.
V>(Не берусь утверждать за некие потенциальные новые реализации, бо за современным Лиспом не слежу, но десятилетиями оно работало именно так)
Наверняка вы что-то не так поняли. Весь смысл сборщика мусора — в возможности освободить занятую память.
Если сборщик ничего не перемещает, то откуда возьмётся освобождение? И вообще, зачем такой "сборщик" выполнять?
V>Не-а.
Может быть, я чего-то не понимаю. Вас не затруднит найти ссылку на описание того сборщика мусора, о котором вы рассуждаете?
V>По VB и VFoxPro литературы мильон.
Не по VB, а по ООП на VB. Примеры к "паттернам ООП" на каком языке даны?
V>Я говорю не про игнор, а про наложенные ограничения на реализацию парадигмы.
V>Джава наложила то ограничение, что экземпляры пользовательских типов будут всегда располагаться в динамической куче, а такого ограничения в исходной парадигме нет.
V>Это подробности конкретной реализации в неких принятых ограничениях.
Опять вы закапываетесь в подробности конкретной реализации. Зачем ?
В парадигме ООП нет ничего ни про кучу, ни про стек.
V>Сотый раз повторюсь — этого не требовалось.
V>Приличный "почти современный" ООП был реализован еще до Смолтолка, в Алгол-68, и там был функциональный тип и лямбды, т.е. лямбда-исчисление.
V>Я уже подкидывал тебе мысль, что ФП-подход — это подход системы ограничений к процедурному программированию (функциональный тип был известен еще в эпоху процедурного программирования), в то время как ООП-подход — это инструментарий расширения.
Это неудачная мысль.
V>ООП-подход включил известные на тот момент парадигмы целиком.
V>Дальнейшее развитие ООП-парадигмы и соотв. языков шло в области описания таких ограничений, коль они требуются, ср-вами языка.
V>Именно поэтому современные ООП-языки принято считать "мультипарадигменными", хотя это не так, разумеется.
Нет конечно.
S>>В чистом ООП никаких делегатов нет. Есть интерфейсы с определёнными сигнатурами.
V>Мда... Будем разгребать:
Вы просто написали то же самое, что и я, только более подробно.
V>А разгрести эту кашу можно лишь помня, что в ФП действует "утиная типизация", то бишь, такие функциональные объекты с одинаковой сигнатурой считаются одинаковым типом.
V>И оно не просто так, а потому что бинарная механика реализаций таких объектов идентична, т.е. бинарной ошибки реинтерпретации памяти быть не может.
Вы ставите телегу впереди лошади. В ФП совместимость между собой функций с одинаковой сигнатурой является частью дизайна языка. Бинарная механика просто построена так, чтобы удовлетворять этому требованию.
V>В дотнете, однако, делегаты с одинаковой сигнатурой неприводимы друг к другу, хотя обладают такой же бинарной совместимостью.
V>В этом месте ФП в дотнете заканчивается, толком не начавшись.
S>>А несовместимость делегатов с одинаковой сигнатурой в рамках ООП не является самостоятельной особенностью. Она скорее связана с выбором между утиной типизацией и номинативной типизацией.
V>Ага, т.е. ты хорошо понимаешь про утиную типизацию, просто Ваньку тут валяешь.
Конечно понимаю.
V>Номинативная типизация — это тоже левое ограничение, не требуемое для реализации концепции ООП.
V>Это просто такое решение, ограничиться именно ею.
V>Хотя, базовая машинка способна вызывать методы не того объекта через свою систему команд, а вся "загвоздка" была бы только в верификации сгенерированного компилятором кода, верно?
V>Т.е. всего-то надо было снабдить верификатор возможностью утиной типизации неких типов, помеченных атрибутом как "делегат" (их даже не обязательно было наследовать от базового Delegate, достаточно было наследовать прямо от Object, а остальные ср-ва можно было бы получать от метакласса).
V>Плюсом было бы то, что загрузчик-верификатор мог бы эффективно "склеивать" попадающие под утиную типизацию такие функциональные типы, т.е. не засорять память копиями одного и того же бинарного кода!
V>Типов-делегатов ведь овердохрена, но уникальных сигнатур потенциально меньше.
V>Все эти Action<> и Func<>, таки, выглядят крайне ущербно — как попытка залатать исходно присутствующую дыру во фреймворке.
Все эти Action<> и Func<> как раз и являются реализацией ровно вашей же идеи путём повторного использования уже существующих компонентов инфраструктуры. То самое склеивание функциональных типов и всё такое.
V>1. На шаблонном уровне совместимы.
Шаблоны тоже не являются частью ООП-парадигмы. Вы критикуете решение, принятое в системе, где шаблонов не было, при этом указывая на систему, где аналогичная дыра заклеивается при помощи шаблона
V>2. Компилятор склеивает идентичный бинарный код различных типов.
Это вообще деталь реализации.
V>А в дотнете ни ограничения в генериках на абстрактную сигатуру делегата не задать, ни описать эту абстрактную сигнатуру явно, скажем, где-то так:
V>
V>bool Find<T>(ICollection<T> collection, bool predicate(T arg)) {...}
V>
Не понял, а в чём тут проблема?
V>Об костыли Action<> и Func<> мы спотыкаемся сразу же, когда речь идёт о модификаторах, например ref/in/out, а недавно еще появились scope и readonly.
V>Плюс аргументами генериков не могут быть ref-структуры, поэтому, приходится описывать типы делегатов ручками как в старые добрые времена, когда генериков еще не было, бгг...
Это всё не относится к вопросам ООП парадигмы, а скорее о проектировании реальной платформы в условиях противоречивых ограничений.
V>Но утиная типизация или нет перпендикулярна статической типизации, прикинь? ))
Я в курсе.
V>Мы тут говорили о коде как о данных.
V>Это появилось только с метаклассами в Смолтолке-80.
V>До этого никакой рефлексии не было.
Рефлексия ортогональна коду-как-данным. В лиспе есть код-как-данные, но рефлексии может и не быть.
В раннем дотнете рефлексия была, а кода-как-данных не было.
Код-как-данные — это Expression<T>. Рефлексия — System.Runtime.Reflection.
V>Да какие проблемы (мать, мать, мать...) — опиши сам объект как неизменяемый.
V>Тебе показать, как описывать иммутабельные объекты в С++?
Ага, вижу, начинается понимание
V>Но тип аргумента-объекта уже должен уже быть? ))
Конечно.
S>>В качестве упражнения можете попробовать спроектировать на С++ пару абстрактных классов — изменяемый вектор и immutable вектор. В терминах дотнета это были бы IImmutableList<T> и IList<T>. И посмотрите, можно ли их отнаследовать друг от друга; можно ли получить один из другого при помощи модификатора const; понадобится ли вообще модификатор const при проектировании этих классов или при их использовании.
V>1. В дотнете я могу породить мутабельный тип из IImmutableList<T> аж бегом и со свистом.
Породить-то можете, а что вы с ним будете делать дальше?
Вы понимаете, что он будет нарушать спецификацию интерфейса?
V>2. В этой же технике в точности аналогично можно и в С++.
Ага. Осталось понять, при чём тут const, и устыдиться. Упражнение-то выполнить сможете?
V>Еще в прошлые разы я обращал твоё внимание на то, что более выгодно с практической точки зрения, чтобы не твой код требовал иммутабельности объекта, а чтобы внешний код требовал, чтобы твой код не мог модифицировать объект.
Пытаетесь подменить задачу. Знакомо.
V>Почему так? — по очевидной причине, ведь твой код тогда сможет работать как с тру-иммутабельными объектами, так и с неиммутабльными при рождении, но которые решили сделать иммутабельными "чуть позже". Например, некий достаточно сложно инициализируемый ветвистый словарь чего-то там.
Ну так для того, чтобы "мой" код корректно работал, мне нужны гарантии не только того, что я не изменяю объект, но и что никто другой его тоже не изменяет.
К примеру, когда этот развесистый словарь является ключом в другом словаре.
V>В целях эффективности в момент наполнения словарь может быть мутабельным, а потом в какой-то момент владение передаётся константной ссылке/указателю и ву а ля.
V>В отсутствии const в этом сценарии потребуется два типа — мутальный и иммутабельный, где готовый мутабельный словарь копируется в иммутабельный, с соответствующими накладными расходами и прессингом на память.
Фантазии, фантазии. Посмотрите, как устроен ImmutableDictionary в дотнете. Там нет const, и при этом "накладные расходы и прессинг на память" ровно такие же, как в вашем сценарии.
V>В С+ в этом сценарии достаточно скопировать ссылку/указатель.
Ну нет конечно, недостаточно. С++ не даст мне гарантии, что кто-то не поменяет этот словарь после того, как я заложился на его неизменность.
Перед тем, как рассуждать об эффективности кода, нужно обеспечить его корректность.