Сообщение Re[8]: понимание ООП Алана Кея от 22.03.2023 17:32
Изменено 22.03.2023 18:23 vdimas
V>>Такой Паскаль лично я в руках не держал, держал Турбо-Паскаль, потом объектный, потом Дельфи.
S>RTFM: https://ru.wikipedia.org/wiki/P-%D0%BA%D0%BE%D0%B4
RTFM чего именно?
Что такое P-код? ))
Это тоже перпендикулярно языку.
Собсно, по твоей ссылке и говорится, что концепция была впервые реализована в другом языке.
Сам я впервые "вживую" курочил P-код в Бейсике ZX Spectrum.
Паскаль под ZX Spectrum компилял в нейтив.
Паскали под IBM PC ходили борландовый и от MS, в P-код не компиллировали.
V>>В любом случае, если даже такой Паскаль существовал (с возможностью сохранять и оперировать этим P-представлением), тогда и возможность соответствующая была.
V>>Аргумент из разряда "было или нет реализовано" опять немного перпендикулярный.
S>Простите, но это бред.
Медитировать до просветления, как грится.
Сам же привёл пример конкретной системы gemstone, т.е. имелась ввиду соответствующая инфраструктура в этой системе.
V>>Аналогично в VB — компиляция происходит в нейтив, но для отладки используется P-код в памяти, без сброса образа на диск.
S>Ну, так как мне воспользоваться этим кодом изнутри VB?
Языками более низкого уровня.
Или расписать некий фреймворк оперирования памятью в некоей библиотеке и сделать импорт функций в VB (есть такая возможность), чтобы иметь требуемый инструментарий.
Например, можно взять готовую либу CRT.
V>>Что не требуется уже на уровне переносимых объектных файлов.
S>При чём тут "не требуется"?
Потому что в объектных файлах содержится код и данные со всей сопутствующей метаинформацией.
S>Речь о том, как что устроено, и с чем связаны какие возможности.
Если требуется посылать код другому устройству, то требуется переносимость кода.
Я уже начинаю подозревать, что ты видишь возможность переносимости кода только в виде его AST-представления.
В то время как исторически переносимость обеспечивалась P-кодом, хотя он изначально и не назывался так, а назывался кодом для абстрактной машины.
Кнут тоже использовал абстрактную машину в своих "Искусствах программирования" (если ты читал эти труды).
У нас был коллективный курсовик на 3-м курсе на тему реализации абстрактной машины и минимальной операционки+компилятора для него.
В общем, твоя попытка рассуждать о банальностях как об откровениях малость сбивает меня с толку. ))
V>>Я уже говорил — из Форта, скорее, который являлся чем-то вроде бинарной версии Лиспа.
S>Продолжаете безудержно фантазировать.
Провожу ликбез.
V>>Натяжка Смолтолка на Лисп, таки, слишком смелая.
S>При чём тут натяжка? Это прямая речь Алана Кея.
Хосподя, Алан Кей тот еще болтун, навроде тебя.
Причём, болтуном он стал гораздо позже... в попытках, не знаю, придать некую публичную окраску своей роли в этой работе.
Он работал над этой системой не один, слава богу.
Такие монстры как Голдберг служили неплохим противовесом этому, в общем-то, нубу-самоучке.
Изначально Смолтолк был попыткой описать схематику Симулы на основе того ограничения, что "объекты" обмениваются лишь "сообщениями".
Какой там в опу Лисп? ))
Система получилась убогой по быстродействию, поэтому была переработана унутре на манер Форта.
Просто ты не знаешь (или не понимаешь) о чём речь.
Похоже, ты не трогал толком ни Смолтолк, ни Лисп, ни Форт, не понимаешь внутренней механики этих языков, но пытаешься фантазировать.
Рассуждать о Лиспе можно было лишь в последней версии Смолтолка-80, когда добавили метаклассы.
ООП в виде CLOS привнесли в 70-х в виде стандарта, напомню, и это всего лишь эмуляция ООП на библиотечном уровне.
Но концепции обкатывались, не без этого.
Т.е. в изначальном Лиспе никаких метаклассов не было, как и не было в Лиспе ООП, ес-но, были только ф-ии как первоклассные объекты (сам код) и данные в виде однонаправленно связанных встроенных в систему списков.
Очередное твоё "слышал звон".
Просто ссылаться на Лисп однажды стало модно, бгг, и этот болтун не преминул.
V>>Зато на Форт прямая — под капотом была стековая VM с двумя стеками из Форта и такой же шитый код по результату работы JIT.
S>Какой "такой же"?
Курить, что есть шитый код.
V>>Это почему к эффективности Смолтолка были вопросы, из-за недостаточно умного JIT.
S>Опять пошёл какой-то бред.
Ликбез.
V>>Зато PostScript, который является развитием Форта, вполне себе ездит с устройства на устройство и прекрасно исполняется.
S>А пост-скрипт то тут при чём?
Пример развития Форта до переносимости м/у устройствами.
Наглядно показывает, что "переносимость" не берется из воздуха.
Что требует для своей реализации как некие св-ва вычислительной модели, так и заточенную под переносимость обвязку (инфраструктуру).
Это, блин, просто пример, хотя бы немного воткнув в который, ты бы избавился от многих своих дурацких вопросов и еще более дурацких попыток спора по элементарным вещам.
Таки, самообразованием принято заниматься самостоятельно, не испытывая терпение коллег.
V>>Повторюсь, любой объектный файл (или архив их в объектной библиотеке) над переносимой платформой (например, llvm) представляет из себя то же самое.
V>>Даже в Паскале, Си и С++.
V>>К языку это перпендикулярно.
S>Сколько бы раз вы ни повторяли, это не перестанет быть заблуждением.
Но доказать не сможешь? ))
S>Попробуйте реализовать на Паскале, Си, и С++ функцию, которая бы передавала пользовательский предикат на сервер для исполнения.
Не вижу технических проблем передать чистую ф-ию или чистое замыкание при наличии соотв. инфраструктуры.
В вопросе удалённого исполнения кода есть всего два требования:
— переносимость кода;
— переносимость данных.
S>А потом для сравнения попробуйте реализовать то же самое на Lisp или SmallTalk.
Ну вот попробуй на Лисп (или другом динамическом языке) без динамического исполнения текстовых исходников, а я поржу.
S>Может быть, тогда до вас дойдёт, как такая возможность связана с языком.
Да не связана.
Такая возможность связана с представлением исполняемого кода, а это представление перпендикулярно языку.
Разве ты не видел интерпретаторов Си или Паскаля?
Похоже, у тебя каша в голове из св-в языков и конкретных их компиляторов/интерпретаторов.
Посмотри на Схему — вот тебе оптимизирующий компилятор Лиспа.
Были убраны возможности рантайм рефлексии кода.
Язык, по-сути, тот же.
Отличается представлением скомпиллированного кода, а значит, особенностями модели вычисления.
V>>Сходство с Лиспом там в общей схеме работы интерпретатора, который двухфазный:
V>>1. компиляция во внутренее представление;
V>>2. исполнение этого внутреннего представления.
S>С этой точки зрения более-менее похожи все языки, включая basic.
Все реализации языков, работающие на основе дополнительной исполняющей машинки.
Но не сами языки.
Не осенило еще? ))
Разве ты не видел компиллирующих Бейсиков?
Например, MS Visual Basic for DOS компиллировал в неплохо оптимизированный код (я курочил дизассемблером что он там порождает — да там офигенно, на уровне их же компилятора Си и Паскаля).
V>>Еще отличие Форта в том, что в коде хранится адрес исполняемой процедуры слова, в то время как в Лиспе хранился адрес метаинформации символа, т.е. Чарльз Мур убрал лишний уровень косвенности в процессе работы программы. Однако же, обратным поиском адреса в Форте запросто памятся на "слова". Т.е. Мур сделал размен эффективности оперирования метаинформацией на эффективность исполнения.
S>Короче, общего примерно столько же, сколько у вороны с роялем.
Общей была идея двухфазного интерпретатора и представления кода как данных.
И это были первые в мире интерпретаторы, если что.Большинство реализаций Форта позволяют сделать декомпиляцию программы. Полученный текст мало отличается от исходного.
Просто Мур допилил идею Лиспа до более эффективной, открыв тем самым эпоху достаточно эффективных (в сравнении с Лиспом) интерпретаторов.
Стековая машинка и фаза компиляции интерпретатором в шитый код стали стандартом для реализации интерпретаторов де-факто.
И источником тут является Форт.
Лисп является источником лишь транзитивно, т.к. необходимость борьбы с недостатками Лиспа и породила Форт.
Даже VM джавы и дотнета используют стековую машинку как абстрактную, хотя не используют шитый код...
Но это уже следствие выросших вычислительных мощностей, где у JIT появились ресурсы на генерацию нейтивного кода на лету.
V>>Да и, до первых виденных ЭВМ прилично упражнялся на программируемом калькуляторе MK-61, это тот же Форт, вид сборку.
V>>Поэтому, программирование на Форте чуть позже зашло как к себе домой. ))
S>Простите, но рассуждения про Форт не очень интересны в контексте ООП.
А зря. ))
ООП в Форте реализуется так же как в Лиспе — через макросистему.
Форт не поддерживает никакую парадигму программирования и поддерживает их все одновременно. Написать набор слов для организации ООП в программе на Форте (а их может быть одновременно несколько и они будут отлично уживаться вместе) гораздо проще, чем решить, какие возможности от этого набора слов требуются.
Напомню, что CLOS — это не более чем попытка стандартизировать библиотечные ООП-возможности.
В Лисп точно так же может сосуществовать сколько угодно много независимых ООП-подсистем библиотечного уровня.
Но! Могут возникнуть вопросы конфликтов идентификаторов макросов и ф-ий таких подсистем.
Именно поэтому потребовалась стандартизация ООП-подсистем в Лисп.
В Форте конфликты имён решаются более чем элегантно через словари, поэтому вопрос стандартизации ООП-расширений никогда не стоял.
Бери любое на вкус.
Исходники любого из этих расширений смехотворны по объёму, т.е. могут быть включены в целевую систему без переживаний по поводу "утяжеления" готового образа — ведь львиная доля этих расширений нужны лишь для первой стадии интерпретации (макросы), а при сохрании оптимального образа ненужный код может быть выкинут.
И насчёт "самолюбования" — я просто вижу любопытствующего чела, который, однако, не обладает необходимой инфрмацией, чтобы понять — почему оно происходило именно так.
Ты не понимаешь мотива принятия некоторых важных решений в IT в ретроспективе.
Проводимый ликбез тебя коробит, но это особенности твоего чванства и ничего более.
В интересные места ведь тебя отсылаю. ))
Было бы любопытно...
V>>Оперирование динамической памятью явное.
V>>Это самые базовые слова:
V>>- "," — выделить ячейку в динамической памяти и скопировать туда значение с вершины стека;
V>>- "@" — читать из памяти число адресу на вершине стека;
V>>- "!" — писать число из второй ячейки стека по адресу на вершине стека.
S>Это, мягко говоря, совсем не то, что применяется в Лиспе или Смоллтоке.
Ты спрашивал про динамическую память — тебе ответили.
Ключевое тут то, что с помощью базовых примитивов можно расписать сколь угодно сложную систему управления динамической памятью, если по каким-то причинам не устраивает данная изкаробки. В том числе запросто пишется консервативный GC с любой из политик работы, например, какие были приняты в различных реализациях Лиспа — автоматический вызов при исчерпании пула ячеек vs явные вызовы GC.
V>>В Форте можно описать произвольные структуры данных в памяти.
S>В ассемблере тоже. Это не делает ассемблер лиспоподобным языком.
Разумеется, речь идёт о выразительности такого описания.
Выразительность Форта при описании объектов выше выразительности Лиспа.
Это из-за особенностей макросистемы Форта — макросы полностью перехватывают выход первой фазы интерпретатора, т.е. в Форт изкаробки включены ср-ва оперирования кишками своей машинки, плюс доступны все слова, из которых состоит "компилятор" первой фазы. Поэтому ООП-расширения Форта смешны по объёму, т.к. вся инфраструктура по парсингу уже есть.
Это и сила, и слабость одновременно, ес-но, как и оно есть у любого достаточно "широкого" инструментария, т.к. больше простора для ошибок.
V>>Сборка мусора в Лиспе, опять же, реализована по-разному.
S>Это подробности. Она является неотъемлемой частью языка.
Дудки! ))
Если GC в некоторой реализации требует явного вызова и никогда не вызывается автоматически — это уже подробности не только языка, но принятых решений в этой конкретной реализации.
А каждое настолько важное принимаемое инженерами решение происходит не просто так, разумеется...
Просто ты склонен упрощать из-за недостатка информации по темам, в которых пытаешься расуждать.
Ты мог бы самостоятельно покормить свою эрудицию перед заходом на новый раунд, мне было интересней.
А так уподобляешься шимже, который цинично провоцирует коллег на дележку информацией на блюдечке, разменивая свою лень на своё лицо. ))
V>>В некоторых реализация сборка мусора вызывается каждый раз при исчерпании текущих свободных ячеек в предвыделенном пуле их, что может провоцировать (и провоцировала) сборку мусора даже когда потребности в этом не было, т.е. не было "забытых" объектов.
V>>В других реализациях сборка мусора вызывается явно в рамках борьбы такими с холостыми срабатываниями.
S>Чегось?
ЧТД. ))
А ведь это были важные отличия реализаций Лиспа.
V>>В любом случае, с многопоточной сборкой мусора в современных VM этот подход имел мало общего.
S>Это вообще иррелевантно к данному обсуждению. Речь о языке, а не о реализации.
Бгг...
Если GC не вызывается автоматом, то твои рассуждения "о языке" идут сразу в dev/nul.
V>>Да и сборщик мусора консервативный, а не точный, как в Java или .Net.
V>>Т.е. не перемещает данные (не уплотняет память).
S>Чегось? Продолжается безудержный бред, да?
Очередное ЧТД.
Исходный и десятки лет реализованный GC не уплотнял память на уровне ячеек.
Он уплотнял только страницы, если/когда эти страницы освобождались полностью.
Но оно же происходит и в обычном менеджере памяти в том же Си безо всякого GC.
Исходный GC связывал освобождаемые ячейки CONS в глобальный список свободных ячеек и не умел ничего более.
Перемещающи/копирующий GC должен уметь корректировать ссылки, а GC Лиспа той эпохи этого не делал.
(Не берусь утверждать за некие потенциальные новые реализации, бо за современным Лиспом не слежу, но десятилетиями оно работало именно так)
Да и, сам вопрос уплотнения памяти в Лисп может стоять только из соображений локальности, т.е. из-за наличия кеш-памяти в современных процессорах.
В эпоху засилья Лиспа было фиолетово для эффективности работы, располагаются ли ячейки подряд или в разных участках памяти.
V>>Т.е., не помогла особо метаинформация.
S>Прекрасно помогла.
Не-а.
V>>Второй вариант без проблем может быть реализован в Форте на библиотечном уровне.
V>>В Лиспе на библиотечном уровне никак, т.к. отсутствуют встроенные примитивы прямой работы с памятью и соотв. доступ ко внутренним кишкам интерпретатора.
V>>(в кое-каких реализациях присутствуют, но это уже намного более поздние эксперименты в попытках привнести в Лисп свойства полее поздних ЯВУ общего назначения)
S>В лиспе сборка мусора является частью языка. Отсутствие примитивов прямой работы с памятью — это скорее плюс, чем минус.
Про плюсы/минусы можно рассуждать только в контексте конкретной решаемой задачи.
Если бы у Лиспа были одни плюсы, не нужны были бы другие языки.
V>>С выходом объектного Паскаля и первых версий VB от MS, скорее.
S>Это в (бывшем) СССР. А в мире объектные версии Паскаля прошли практически незамеченными массовой публикой.
VB зато широко использовался.
Visual FoxPro тоже, кстате.
S>Попробуйте найти литературу по ООП с примерами на Паскале или VB.
По VB и VFoxPro литературы мильон.
V>>Java изначально ограничила саму ООП-парадигму и .Net совершила ту же ошибку в первоначальном дизайне.
S>Мне даже интересно, какие именно аспекты ООП-парадигмы проигнорировала Java.
Я говорю не про игнор, а про наложенные ограничения на реализацию парадигмы.
Джава наложила то ограничение, что экземпляры пользовательских типов будут всегда располагаться в динамической куче, а такого ограничения в исходной парадигме нет.
Это подробности конкретной реализации в неких принятых ограничениях.
V>>Помнишь обсуждение хотя бы новых управляемых указателей на методы в .Net?
V>>А обсуждение типов-делегатов все нулевые года?
V>>Надо ж было умудриться описать делегаты как объекты, что делегаты с одинаковой сигнатурой стали несовместимы. ))
S> Это, собственно, и есть попытки выйти за пределы ООП-парадигмы.
Сотый раз повторюсь — этого не требовалось.
Приличный "почти современный" ООП был реализован еще до Смолтолка, в Алгол-68, и там был функциональный тип и лямбды, т.е. лямбда-исчисление.
Я уже подкидывал тебе мысль, что ФП-подход — это подход системы ограничений к процедурному программированию (функциональный тип был известен еще в эпоху процедурного программирования), в то время как ООП-подход — это инструментарий расширения.
ООП-подход включил известные на тот момент парадигмы целиком.
Дальнейшее развитие ООП-парадигмы и соотв. языков шло в области описания таких ограничений, коль они требуются, ср-вами языка.
Именно поэтому современные ООП-языки принято считать "мультипарадигменными", хотя это не так, разумеется.
S>В чистом ООП никаких делегатов нет. Есть интерфейсы с определёнными сигнатурами.
Мда... Будем разгребать:
Берём абстрактный функциональный ФП-тип.
Этот тип задан своей сигнатурой.
Для целей реализации лямбда-исчисления функциональный тип представлен кортежом { функция, данные }, где абстрактный тип лямбды определён сигнатурой функции.
В .Net это можно было бы описать так:
interface IFunc<TArg, TRet> {
TRet Invoke(TArg arg);
}
Где данные передаются через неявно подаваемый this.
В ООП само понятие interface описано как кортеж таких функций.
Это прямо изначально по Барбаре Лисков, если что (главный теоретик ООП, а не этот ботун Алан Кей).
При сокращении кортежа до одной ф-ии получаем абстрактный функциональный тип.
Собсно, делегаты так и реализованы, т.е. никакого выхода за ООП-парадигму в них нет.
"Выход" за ООП-парадигму там можно усмотреть только в выразительности описания таких объектов, бо требуется описать всего лишь сигнатуру единственной ф-ии, и это будет сигнатура метода Invoke.
А разгрести эту кашу можно лишь помня, что в ФП действует "утиная типизация", то бишь, такие функциональные объекты с одинаковой сигнатурой считаются одинаковым типом.
И оно не просто так, а потому что бинарная механика реализаций таких объектов идентична, т.е. бинарной ошибки реинтерпретации памяти быть не может.
В дотнете, однако, делегаты с одинаковой сигнатурой неприводимы друг к другу, хотя обладают такой же бинарной совместимостью.
В этом месте ФП в дотнете заканчивается, толком не начавшись.
S>А несовместимость делегатов с одинаковой сигнатурой в рамках ООП не является самостоятельной особенностью. Она скорее связана с выбором между утиной типизацией и номинативной типизацией.
Ага, т.е. ты хорошо понимаешь про утиную типизацию, просто Ваньку тут валяешь.
Номинативная типизация — это тоже левое ограничение, не требуемое для реализации концепции ООП.
Это просто такое решение, ограничиться именно ею.
Хотя, базовая машинка способна вызывать методы не того объекта через свою систему команд, а вся "загвоздка" была бы только в верификации сгенерированного компилятором кода, верно?
Т.е. всего-то надо было снабдить верификатор возможностью утиной типизации неких типов, помеченных атрибутом как "делегат" (их даже не обязательно было наследовать от базового Delegate, достаточно было наследовать прямо от Object, а остальные ср-ва можно было бы получать от метакласса).
Плюсом было бы то, что загрузчик-верификатор мог бы эффективно "склеивать" попадающие под утиную типизацию такие функциональные типы, т.е. не засорять память копиями одного и того же бинарного кода!
Типов-делегатов ведь овердохрена, но уникальных сигнатур потенциально меньше.
Все эти Action<> и Func<>, таки, выглядят крайне ущербно — как попытка залатать исходно присутствующую дыру во фреймворке.
V>>Это серьёзный косяк, как ни крути.
V>>C++ эту ошибку не совершал.
S>Скажем, в С++ два одноимённых типа с "одинаковой сигнатурой" ничуть не более совместимы между собой, чем в дотнете.
1. На шаблонном уровне совместимы.
2. Компилятор склеивает идентичный бинарный код различных типов.
А в дотнете ни ограничения в генериках на абстрактную сигатуру делегата не задать, ни описать эту абстрактную сигнатуру явно, скажем, где-то так:
bool Find<T>(ICollection collection, bool predicate(T arg)) {...}
Об костыли Action<> и Func<> мы спотыкаемся сразу же, когда речь идёт о модификаторах, например ref/in/out, а недавно еще появились scope и readonly.
Плюс аргументами генериков не могут быть ref-структуры, поэтому, приходится описывать типы делегатов ручками как в старые добрые времена, когда генериков еще не было, бгг...
S>Это в ФП подобные проблемы возникнуть не могут, т.к. там совместимость любых функций с одинаковой сигнатурой ставится во главу угла.
Разумеется, но это же без проблем.
Просто считается, что любой функциональный тип с одной сигнатурой — он ровно один на всех.
Тут проблем нет.
И не зря в функциональных типах часто развита система алиасинга типов (аналог typedef в Си/С++).
Слава богу, в 2022-м появился global using в C#. ))
Пара десятилетий потребовалось для вменяемого алиасинга.
Но это должно было быть сразу, и под функицональынй тип должна была быть механика неявного алиасинга, как в ФП.
В общем, придирки к дотнету в этом вопросе не в том, что они чего не придумали сами (за это их никто бы не полоскал), а за то, что не взяли уже давно придуманное и отлаженное.
Причём, это не какая-то мелочь, а самая базовая весч, которую они же взялись реализовать сразу же — функциональынй тип!
Ну так и делайте его ровно так, как он уже давно отшлифован как в концепции, так и в реализации этой концепции что на уровне компилятора, что в бинарном виде.
Там же, хосподя, примитивщина всё.
Например, по выходу дотнета за несколько вечеров я накатал компилятор ограниченной реализации Схемы.
Ты бы знал, как я матерился, выбирая представление функционального типа в конечном образе, ууу...
Зато освоил способы описания типов-делегатов "на лету", в процессе работы компилятора.
Разумеется, у меня был глобальный словарь всех уникальных сигнатур, встреченных программой.
Т.е., в наколенном варианте за несколько вечеров это решить можно, а на уровне инфраструктуры "забыли", угу...
При том, что F# был одним из первых реализованных языков.
Я не зря потом купил книгу одного из авторов дотнета, как они описывали процесс создания фреймворка, мне просто было интересно, как ои до такого дошли.
Книга написано вполне читабельно, но вызывает сожаление — они "тренировались на кошках" в основном в деле реализации C# и Питона.
При том, что Питон под дотнет так и остался невостребован.
Лучше бы разрабатывали его из F# и C#.
S>А в лиспе так и вообще, одинаковость сигнатуры не является критерием.
Ес-но, тут динамическая типизация vs статическая.
Но утиная типизация или нет перпендикулярна статической типизации, прикинь? ))
Порвал шаблончик или нет?
(опять и снова отсылаю к статически компиллируемым ФП-языкам)
V>>Повторяю в сотый раз — "чисто ФП-шные" штуки были в ООП чуть не в первых ООП-языках.
V>>Например, в том же Алголе-68.
V>>Т.е. функциональный тип как первоклассный в языке, лямбды.
S>Мне кажется, вы слишком смело называете Алгол ООП-языком. Никакого ООП в нём не было.
Я называю Алгол-68.
Это первый полноценный ООП-язык.
V>>Симула, кстате, была тоже лишь попыткой объектного расширения Алгола.
S>Как так — взяли ООП-шный алгол и попробовали привинтить к нему "объектное расширения"?
Ну да.
Берется язык со всем его синтаксисом и стандартной библиотекой.
Берутся исходники всего этого.
Допиливается.
Получается Симула, которая практически тот же Алгол.
Но работы была проведена недостаточно качественно в 67-м, поэтому появился Алгол-68.
На Симуле обкатали концепции, но недоделки стали видны сразу же — этот язык не мог претендовать на роль языка общего назначения.
Поэтому Алгол 68 уже мог.
Симулу могли бы назвать "Объектным Алголом".
Похоже, тебя чуть сбивает с толку совсем левое название языка. ))
Тот же Алгол, хосподя...
V>>Да и Смолтолк приобрёл свои характерные черты не в первой версии 72, и даже не в 76, а только в 80.
S>:sigh:
S>http://wiki.c2.com/?AlanKaysDefinitionOfObjectOriented
Мы тут говорили о коде как о данных.
Это появилось только с метаклассами в Смолтолке-80.
До этого никакой рефлексии не было.
Что касается ООП-парадигмы — она была уже давно обкатана до первых версий Смолтолка.
Смолтолк — это попытка выделить ООП-парадигму "в чистом виде".
Никакой практической пользы "чистота ООП" Смолтолка не имела до введения метаклассов аж в 80-м году, когда стало возможным проводить инспекцию кода, рефакторить и всё это было обёрнуто в IDE.
Поэтому, заслуга Смолтолка не в ООП-парадигме, а в развитой системе метаинформации об ООП-like объектах.
В возможности динамического создания типов и экземпляров этих типов.
А ты тут в очередной раз показываешь непонимание, откуда у чего растут ноги в IT. ))
Причина-следствие, причина-следствие... Это же важно, не?
V>>Для этого значение должно быть const в контексте внешнего кода.
S>Не-не-не, Дэвид Блейн. Вот я пишу свой код. И в нём мне бы хотелось полагаться на то, что его вызывают не с чем попало, а с неизменяемым объектом.
Да какие проблемы (мать, мать, мать...) — опиши сам объект как неизменяемый.
Тебе показать, как описывать иммутабельные объекты в С++?
Модификатор const к ссылке/указателю на объект разрешает вызывать только const-методы этого объекта.
То бишь, предполагается, что у этого объекта могут быть и не const-методы.
Я что в тот раз фигел от твоего непонимания сей элементарщины, что сейчас...
Заметь, в дотнете для структур ввели аналогичную семантику readonly для методов.
Поэтому, если структура передаётся по readonly ref-ссылке (кратно in), то можно вызывать только readonly методы.
В точности как в С++.
Неужели все вокруг идиоты и не понимают, ЗАЧЕМ это делается именно так? ))
Похоже, не понимаешь только ты, считая всех вокруг заранее идиотами.
V>>Т.е. значение может быть const по всей иерархии "внешности", вплоть до объявления.
S>Внешний код ещё не написан — каким образом я смогу потребовать от него const на этом значении "по всей иерархии внешности"?
Но тип аргумента-объекта уже должен уже быть? ))
V>>Это на выбор программиста.
S>
Ну да.
Хочешь полной иммутабельности — не запрещено.
Хочешь частичной — тоже велкам.
Выбирай наиболее эффективную стратегию решения конкретной задачи.
V>>Что будет лишь малой частью всех возможных сценариев по слишком очевидной причине (тут уже сложно не материться) — ввиду наиболее сильного наложенного на сценарий ограничения. S>Ок, как видим, вы так и не поняли, что такое иммутабельность, и почему она ортогональна const.
Мы видим несколько иное — чванливость Синклера, который иногда не в состоянии воспринимать информацию по причине ЧСВ и более ни по какой. ))
V>>Странно, что мы вообще спорили тогда и сейчас вокруг подобной элементарщины...
S>Да ничего странного — у вас отсутствует обучаемость. Годы идут, а вы так и не вышли за пределы своих заблуждений.
И даже сейчас ты не понял, как описывать иммутабельные типы в С++?
Признайся, уже понял и тебе дико стыдно.
S>В качестве упражнения можете попробовать спроектировать на С++ пару абстрактных классов — изменяемый вектор и immutable вектор. В терминах дотнета это были бы IImmutableList<T> и IList<T>. И посмотрите, можно ли их отнаследовать друг от друга; можно ли получить один из другого при помощи модификатора const; понадобится ли вообще модификатор const при проектировании этих классов или при их использовании.
1. В дотнете я могу породить мутабельный тип из IImmutableList<T> аж бегом и со свистом.
2. В этой же технике в точности аналогично можно и в С++.
Ты не там гарантии иммутабельности ищещь.
Гарантии иммутабельности может иметь только сам тип.
Еще в прошлые разы я обращал твоё внимание на то, что более выгодно с практической точки зрения, чтобы не твой код требовал иммутабельности объекта, а чтобы внешний код требовал, чтобы твой код не мог модифицировать объект.
Почему так? — по очевидной причине, ведь твой код тогда сможет работать как с тру-иммутабельными объектами, так и с неиммутабльными при рождении, но которые решили сделать иммутабельными "чуть позже". Например, некий достаточно сложно инициализируемый ветвистый словарь чего-то там.
В целях эффективности в момент наполнения словарь может быть мутабельным, а потом в какой-то момент владение передаётся константной ссылке/указателю и ву а ля.
В отсутствии const в этом сценарии потребуется два типа — мутальный и иммутабельный, где готовый мутабельный словарь копируется в иммутабельный, с соответствующими накладными расходами и прессингом на память. В С+ в этом сценарии достаточно скопировать ссылку/указатель.
Мне казалось, что ты понял эту особеность уже тогда, коль закончил пререкаться...
Но сейчас, смотрю, тебя всё еще распирает от врождённого упрямства "это ВЫ чего-то не понимаете, а не Я!!!111"
Значит, ни хрена ты не понял.
Походу, и не пытался, наивно надеясь, что всё понимаешь и так.
Не всё.
V>>Такой Паскаль лично я в руках не держал, держал Турбо-Паскаль, потом объектный, потом Дельфи.
S>RTFM: https://ru.wikipedia.org/wiki/P-%D0%BA%D0%BE%D0%B4
RTFM чего именно?
Что такое P-код? ))
Это тоже перпендикулярно языку.
Собсно, по твоей ссылке и говорится, что концепция была впервые реализована в другом языке.
Сам я впервые "вживую" курочил P-код в Бейсике ZX Spectrum.
Паскаль под ZX Spectrum компилял в нейтив.
Паскали под IBM PC ходили борландовый и от MS, в P-код не компиллировали.
V>>В любом случае, если даже такой Паскаль существовал (с возможностью сохранять и оперировать этим P-представлением), тогда и возможность соответствующая была.
V>>Аргумент из разряда "было или нет реализовано" опять немного перпендикулярный.
S>Простите, но это бред.
Медитировать до просветления, как грится.
Сам же привёл пример конкретной системы gemstone, т.е. имелась ввиду соответствующая инфраструктура в этой системе.
V>>Аналогично в VB — компиляция происходит в нейтив, но для отладки используется P-код в памяти, без сброса образа на диск.
S>Ну, так как мне воспользоваться этим кодом изнутри VB?
Языками более низкого уровня.
Или расписать некий фреймворк оперирования памятью в некоей библиотеке и сделать импорт функций в VB (есть такая возможность), чтобы иметь требуемый инструментарий.
Например, можно взять готовую либу CRT.
V>>Что не требуется уже на уровне переносимых объектных файлов.
S>При чём тут "не требуется"?
Потому что в объектных файлах содержится код и данные со всей сопутствующей метаинформацией.
S>Речь о том, как что устроено, и с чем связаны какие возможности.
Если требуется посылать код другому устройству, то требуется переносимость кода.
Я уже начинаю подозревать, что ты видишь возможность переносимости кода только в виде его AST-представления.
В то время как исторически переносимость обеспечивалась P-кодом, хотя он изначально и не назывался так, а назывался кодом для абстрактной машины.
Кнут тоже использовал абстрактную машину в своих "Искусствах программирования" (если ты читал эти труды).
У нас был коллективный курсовик на 3-м курсе на тему реализации абстрактной машины и минимальной операционки+компилятора для него.
В общем, твоя попытка рассуждать о банальностях как об откровениях малость сбивает меня с толку. ))
V>>Я уже говорил — из Форта, скорее, который являлся чем-то вроде бинарной версии Лиспа.
S>Продолжаете безудержно фантазировать.
Провожу ликбез.
V>>Натяжка Смолтолка на Лисп, таки, слишком смелая.
S>При чём тут натяжка? Это прямая речь Алана Кея.
Хосподя, Алан Кей тот еще болтун, навроде тебя.
Причём, болтуном он стал гораздо позже... в попытках, не знаю, придать некую публичную окраску своей роли в этой работе.
Он работал над этой системой не один, слава богу.
Такие монстры как Голдберг служили неплохим противовесом этому, в общем-то, нубу-самоучке.
Изначально Смолтолк был попыткой описать схематику Симулы на основе того ограничения, что "объекты" обмениваются лишь "сообщениями".
Какой там в опу Лисп? ))
Система получилась убогой по быстродействию, поэтому была переработана унутре на манер Форта.
Просто ты не знаешь (или не понимаешь) о чём речь.
Похоже, ты не трогал толком ни Смолтолк, ни Лисп, ни Форт, не понимаешь внутренней механики этих языков, но пытаешься фантазировать.
Рассуждать о Лиспе можно было лишь в последней версии Смолтолка-80, когда добавили метаклассы.
ООП в виде CLOS привнесли в 70-х в виде стандарта, напомню, и это всего лишь эмуляция ООП на библиотечном уровне.
Но концепции обкатывались, не без этого.
Т.е. в изначальном Лиспе никаких метаклассов не было, как и не было в Лиспе ООП, ес-но, были только ф-ии как первоклассные объекты (сам код) и данные в виде однонаправленно связанных встроенных в систему списков.
Очередное твоё "слышал звон".
Просто ссылаться на Лисп однажды стало модно, бгг, и этот болтун не преминул.
V>>Зато на Форт прямая — под капотом была стековая VM с двумя стеками из Форта и такой же шитый код по результату работы JIT.
S>Какой "такой же"?
Курить, что есть шитый код.
V>>Это почему к эффективности Смолтолка были вопросы, из-за недостаточно умного JIT.
S>Опять пошёл какой-то бред.
Ликбез.
V>>Зато PostScript, который является развитием Форта, вполне себе ездит с устройства на устройство и прекрасно исполняется.
S>А пост-скрипт то тут при чём?
Пример развития Форта до переносимости м/у устройствами.
Наглядно показывает, что "переносимость" не берется из воздуха.
Что требует для своей реализации как некие св-ва вычислительной модели, так и заточенную под переносимость обвязку (инфраструктуру).
Это, блин, просто пример, хотя бы немного воткнув в который, ты бы избавился от многих своих дурацких вопросов и еще более дурацких попыток спора по элементарным вещам.
Таки, самообразованием принято заниматься самостоятельно, не испытывая терпение коллег.
V>>Повторюсь, любой объектный файл (или архив их в объектной библиотеке) над переносимой платформой (например, llvm) представляет из себя то же самое.
V>>Даже в Паскале, Си и С++.
V>>К языку это перпендикулярно.
S>Сколько бы раз вы ни повторяли, это не перестанет быть заблуждением.
Но доказать не сможешь? ))
S>Попробуйте реализовать на Паскале, Си, и С++ функцию, которая бы передавала пользовательский предикат на сервер для исполнения.
Не вижу технических проблем передать чистую ф-ию или чистое замыкание при наличии соотв. инфраструктуры.
В вопросе удалённого исполнения кода есть всего два требования:
— переносимость кода;
— переносимость данных.
Переносимость данных была реализована в рамках COM/OLE и CORBA.
Переносимость кода и вообще отправка на удалённое исполнение в рамках ActiveX-апплетов (но только в рамках одной архитектуры).
Более широкая переносимость кода была реализована в тех же Java-апплетах и .Net-апплетах.
Напомню, что MS компилятор С++ умеет компиллировать unamanaged C++ код (т.е. безо-всяких managed расширений) в чистый байт-код.
(тип генерируемого образа для режима С++/CLI задаётся опциями компилятора)
S>А потом для сравнения попробуйте реализовать то же самое на Lisp или SmallTalk.
Ну вот попробуй на Лисп (или другом динамическом языке) без динамического исполнения текстовых исходников, а я поржу.
S>Может быть, тогда до вас дойдёт, как такая возможность связана с языком.
Да не связана.
Такая возможность связана с представлением исполняемого кода, а это представление перпендикулярно языку.
Разве ты не видел интерпретаторов Си или Паскаля?
Похоже, у тебя каша в голове из св-в языков и конкретных их компиляторов/интерпретаторов.
Посмотри на Схему — вот тебе оптимизирующий компилятор Лиспа.
Были убраны возможности рантайм рефлексии кода.
Язык, по-сути, тот же.
Отличается представлением скомпиллированного кода, а значит, особенностями модели вычисления.
V>>Сходство с Лиспом там в общей схеме работы интерпретатора, который двухфазный:
V>>1. компиляция во внутренее представление;
V>>2. исполнение этого внутреннего представления.
S>С этой точки зрения более-менее похожи все языки, включая basic.
Все реализации языков, работающие на основе дополнительной исполняющей машинки.
Но не сами языки.
Не осенило еще? ))
Разве ты не видел компиллирующих Бейсиков?
Например, MS Visual Basic for DOS компиллировал в неплохо оптимизированный код (я курочил дизассемблером что он там порождает — да там офигенно, на уровне их же компилятора Си и Паскаля).
V>>Еще отличие Форта в том, что в коде хранится адрес исполняемой процедуры слова, в то время как в Лиспе хранился адрес метаинформации символа, т.е. Чарльз Мур убрал лишний уровень косвенности в процессе работы программы. Однако же, обратным поиском адреса в Форте запросто памятся на "слова". Т.е. Мур сделал размен эффективности оперирования метаинформацией на эффективность исполнения.
S>Короче, общего примерно столько же, сколько у вороны с роялем.
Общей была идея двухфазного интерпретатора и представления кода как данных.
И это были первые в мире интерпретаторы, если что.Большинство реализаций Форта позволяют сделать декомпиляцию программы. Полученный текст мало отличается от исходного.
Просто Мур допилил идею Лиспа до более эффективной, открыв тем самым эпоху достаточно эффективных (в сравнении с Лиспом) интерпретаторов.
Стековая машинка и фаза компиляции интерпретатором в шитый код стали стандартом для реализации интерпретаторов де-факто.
И источником тут является Форт.
Лисп является источником лишь транзитивно, т.к. необходимость борьбы с недостатками Лиспа и породила Форт.
Даже VM джавы и дотнета используют стековую машинку как абстрактную, хотя не используют шитый код...
Но это уже следствие выросших вычислительных мощностей, где у JIT появились ресурсы на генерацию нейтивного кода на лету.
V>>Да и, до первых виденных ЭВМ прилично упражнялся на программируемом калькуляторе MK-61, это тот же Форт, вид сборку.
V>>Поэтому, программирование на Форте чуть позже зашло как к себе домой. ))
S>Простите, но рассуждения про Форт не очень интересны в контексте ООП.
А зря. ))
ООП в Форте реализуется так же как в Лиспе — через макросистему.
Форт не поддерживает никакую парадигму программирования и поддерживает их все одновременно. Написать набор слов для организации ООП в программе на Форте (а их может быть одновременно несколько и они будут отлично уживаться вместе) гораздо проще, чем решить, какие возможности от этого набора слов требуются.
Напомню, что CLOS — это не более чем попытка стандартизировать библиотечные ООП-возможности.
В Лисп точно так же может сосуществовать сколько угодно много независимых ООП-подсистем библиотечного уровня.
Но! Могут возникнуть вопросы конфликтов идентификаторов макросов и ф-ий таких подсистем.
Именно поэтому потребовалась стандартизация ООП-подсистем в Лисп.
В Форте конфликты имён решаются более чем элегантно через словари, поэтому вопрос стандартизации ООП-расширений никогда не стоял.
Бери любое на вкус.
Исходники любого из этих расширений смехотворны по объёму, т.е. могут быть включены в целевую систему без переживаний по поводу "утяжеления" готового образа — ведь львиная доля этих расширений нужны лишь для первой стадии интерпретации (макросы), а при сохрании оптимального образа ненужный код может быть выкинут.
И насчёт "самолюбования" — я просто вижу любопытствующего чела, который, однако, не обладает необходимой инфрмацией, чтобы понять — почему оно происходило именно так.
Ты не понимаешь мотива принятия некоторых важных решений в IT в ретроспективе.
Проводимый ликбез тебя коробит, но это особенности твоего чванства и ничего более.
В интересные места ведь тебя отсылаю. ))
Было бы любопытно...
V>>Оперирование динамической памятью явное.
V>>Это самые базовые слова:
V>>- "," — выделить ячейку в динамической памяти и скопировать туда значение с вершины стека;
V>>- "@" — читать из памяти число адресу на вершине стека;
V>>- "!" — писать число из второй ячейки стека по адресу на вершине стека.
S>Это, мягко говоря, совсем не то, что применяется в Лиспе или Смоллтоке.
Ты спрашивал про динамическую память — тебе ответили.
Ключевое тут то, что с помощью базовых примитивов можно расписать сколь угодно сложную систему управления динамической памятью, если по каким-то причинам не устраивает данная изкаробки. В том числе запросто пишется консервативный GC с любой из политик работы, например, какие были приняты в различных реализациях Лиспа — автоматический вызов при исчерпании пула ячеек vs явные вызовы GC.
V>>В Форте можно описать произвольные структуры данных в памяти.
S>В ассемблере тоже. Это не делает ассемблер лиспоподобным языком.
Разумеется, речь идёт о выразительности такого описания.
Выразительность Форта при описании объектов выше выразительности Лиспа.
Это из-за особенностей макросистемы Форта — макросы полностью перехватывают выход первой фазы интерпретатора, т.е. в Форт изкаробки включены ср-ва оперирования кишками своей машинки, плюс доступны все слова, из которых состоит "компилятор" первой фазы. Поэтому ООП-расширения Форта смешны по объёму, т.к. вся инфраструктура по парсингу уже есть.
Это и сила, и слабость одновременно, ес-но, как и оно есть у любого достаточно "широкого" инструментария, т.к. больше простора для ошибок.
V>>Сборка мусора в Лиспе, опять же, реализована по-разному.
S>Это подробности. Она является неотъемлемой частью языка.
Дудки! ))
Если GC в некоторой реализации требует явного вызова и никогда не вызывается автоматически — это уже подробности не только языка, но принятых решений в этой конкретной реализации.
А каждое настолько важное принимаемое инженерами решение происходит не просто так, разумеется...
Просто ты склонен упрощать из-за недостатка информации по темам, в которых пытаешься расуждать.
Ты мог бы самостоятельно покормить свою эрудицию перед заходом на новый раунд, мне было интересней.
А так уподобляешься шимже, который цинично провоцирует коллег на дележку информацией на блюдечке, разменивая свою лень на своё лицо. ))
V>>В некоторых реализация сборка мусора вызывается каждый раз при исчерпании текущих свободных ячеек в предвыделенном пуле их, что может провоцировать (и провоцировала) сборку мусора даже когда потребности в этом не было, т.е. не было "забытых" объектов.
V>>В других реализациях сборка мусора вызывается явно в рамках борьбы такими с холостыми срабатываниями.
S>Чегось?
ЧТД. ))
А ведь это были важные отличия реализаций Лиспа.
V>>В любом случае, с многопоточной сборкой мусора в современных VM этот подход имел мало общего.
S>Это вообще иррелевантно к данному обсуждению. Речь о языке, а не о реализации.
Бгг...
Если GC не вызывается автоматом, то твои рассуждения "о языке" идут сразу в dev/nul.
V>>Да и сборщик мусора консервативный, а не точный, как в Java или .Net.
V>>Т.е. не перемещает данные (не уплотняет память).
S>Чегось? Продолжается безудержный бред, да?
Очередное ЧТД.
Исходный и десятки лет реализованный GC не уплотнял память на уровне ячеек.
Он уплотнял только страницы, если/когда эти страницы освобождались полностью.
Но оно же происходит и в обычном менеджере памяти в том же Си безо всякого GC.
Исходный GC связывал освобождаемые ячейки CONS в глобальный список свободных ячеек и не умел ничего более.
Перемещающи/копирующий GC должен уметь корректировать ссылки, а GC Лиспа той эпохи этого не делал.
(Не берусь утверждать за некие потенциальные новые реализации, бо за современным Лиспом не слежу, но десятилетиями оно работало именно так)
Да и, сам вопрос уплотнения памяти в Лисп может стоять только из соображений локальности, т.е. из-за наличия кеш-памяти в современных процессорах.
В эпоху засилья Лиспа было фиолетово для эффективности работы, располагаются ли ячейки подряд или в разных участках памяти.
V>>Т.е., не помогла особо метаинформация.
S>Прекрасно помогла.
Не-а.
V>>Второй вариант без проблем может быть реализован в Форте на библиотечном уровне.
V>>В Лиспе на библиотечном уровне никак, т.к. отсутствуют встроенные примитивы прямой работы с памятью и соотв. доступ ко внутренним кишкам интерпретатора.
V>>(в кое-каких реализациях присутствуют, но это уже намного более поздние эксперименты в попытках привнести в Лисп свойства полее поздних ЯВУ общего назначения)
S>В лиспе сборка мусора является частью языка. Отсутствие примитивов прямой работы с памятью — это скорее плюс, чем минус.
Про плюсы/минусы можно рассуждать только в контексте конкретной решаемой задачи.
Если бы у Лиспа были одни плюсы, не нужны были бы другие языки.
V>>С выходом объектного Паскаля и первых версий VB от MS, скорее.
S>Это в (бывшем) СССР. А в мире объектные версии Паскаля прошли практически незамеченными массовой публикой.
VB зато широко использовался.
Visual FoxPro тоже, кстате.
S>Попробуйте найти литературу по ООП с примерами на Паскале или VB.
По VB и VFoxPro литературы мильон.
V>>Java изначально ограничила саму ООП-парадигму и .Net совершила ту же ошибку в первоначальном дизайне.
S>Мне даже интересно, какие именно аспекты ООП-парадигмы проигнорировала Java.
Я говорю не про игнор, а про наложенные ограничения на реализацию парадигмы.
Джава наложила то ограничение, что экземпляры пользовательских типов будут всегда располагаться в динамической куче, а такого ограничения в исходной парадигме нет.
Это подробности конкретной реализации в неких принятых ограничениях.
V>>Помнишь обсуждение хотя бы новых управляемых указателей на методы в .Net?
V>>А обсуждение типов-делегатов все нулевые года?
V>>Надо ж было умудриться описать делегаты как объекты, что делегаты с одинаковой сигнатурой стали несовместимы. ))
S> Это, собственно, и есть попытки выйти за пределы ООП-парадигмы.
Сотый раз повторюсь — этого не требовалось.
Приличный "почти современный" ООП был реализован еще до Смолтолка, в Алгол-68, и там был функциональный тип и лямбды, т.е. лямбда-исчисление.
Я уже подкидывал тебе мысль, что ФП-подход — это подход системы ограничений к процедурному программированию (функциональный тип был известен еще в эпоху процедурного программирования), в то время как ООП-подход — это инструментарий расширения.
ООП-подход включил известные на тот момент парадигмы целиком.
Дальнейшее развитие ООП-парадигмы и соотв. языков шло в области описания таких ограничений, коль они требуются, ср-вами языка.
Именно поэтому современные ООП-языки принято считать "мультипарадигменными", хотя это не так, разумеется.
S>В чистом ООП никаких делегатов нет. Есть интерфейсы с определёнными сигнатурами.
Мда... Будем разгребать:
Берём абстрактный функциональный ФП-тип.
Этот тип задан своей сигнатурой.
Для целей реализации лямбда-исчисления функциональный тип представлен кортежом { функция, данные }, где абстрактный тип лямбды определён сигнатурой функции.
В .Net это можно было бы описать так:
interface IFunc<TArg, TRet> {
TRet Invoke(TArg arg);
}
Где данные передаются через неявно подаваемый this.
В ООП само понятие interface описано как кортеж таких функций.
Это прямо изначально по Барбаре Лисков, если что (главный теоретик ООП, а не этот ботун Алан Кей).
При сокращении кортежа до одной ф-ии получаем абстрактный функциональный тип.
Собсно, делегаты так и реализованы, т.е. никакого выхода за ООП-парадигму в них нет.
"Выход" за ООП-парадигму там можно усмотреть только в выразительности описания таких объектов, бо требуется описать всего лишь сигнатуру единственной ф-ии, и это будет сигнатура метода Invoke.
А разгрести эту кашу можно лишь помня, что в ФП действует "утиная типизация", то бишь, такие функциональные объекты с одинаковой сигнатурой считаются одинаковым типом.
И оно не просто так, а потому что бинарная механика реализаций таких объектов идентична, т.е. бинарной ошибки реинтерпретации памяти быть не может.
В дотнете, однако, делегаты с одинаковой сигнатурой неприводимы друг к другу, хотя обладают такой же бинарной совместимостью.
В этом месте ФП в дотнете заканчивается, толком не начавшись.
S>А несовместимость делегатов с одинаковой сигнатурой в рамках ООП не является самостоятельной особенностью. Она скорее связана с выбором между утиной типизацией и номинативной типизацией.
Ага, т.е. ты хорошо понимаешь про утиную типизацию, просто Ваньку тут валяешь.
Номинативная типизация — это тоже левое ограничение, не требуемое для реализации концепции ООП.
Это просто такое решение, ограничиться именно ею.
Хотя, базовая машинка способна вызывать методы не того объекта через свою систему команд, а вся "загвоздка" была бы только в верификации сгенерированного компилятором кода, верно?
Т.е. всего-то надо было снабдить верификатор возможностью утиной типизации неких типов, помеченных атрибутом как "делегат" (их даже не обязательно было наследовать от базового Delegate, достаточно было наследовать прямо от Object, а остальные ср-ва можно было бы получать от метакласса).
Плюсом было бы то, что загрузчик-верификатор мог бы эффективно "склеивать" попадающие под утиную типизацию такие функциональные типы, т.е. не засорять память копиями одного и того же бинарного кода!
Типов-делегатов ведь овердохрена, но уникальных сигнатур потенциально меньше.
Все эти Action<> и Func<>, таки, выглядят крайне ущербно — как попытка залатать исходно присутствующую дыру во фреймворке.
V>>Это серьёзный косяк, как ни крути.
V>>C++ эту ошибку не совершал.
S>Скажем, в С++ два одноимённых типа с "одинаковой сигнатурой" ничуть не более совместимы между собой, чем в дотнете.
1. На шаблонном уровне совместимы.
2. Компилятор склеивает идентичный бинарный код различных типов.
А в дотнете ни ограничения в генериках на абстрактную сигатуру делегата не задать, ни описать эту абстрактную сигнатуру явно, скажем, где-то так:
bool Find<T>(ICollection collection, bool predicate(T arg)) {...}
Об костыли Action<> и Func<> мы спотыкаемся сразу же, когда речь идёт о модификаторах, например ref/in/out, а недавно еще появились scope и readonly.
Плюс аргументами генериков не могут быть ref-структуры, поэтому, приходится описывать типы делегатов ручками как в старые добрые времена, когда генериков еще не было, бгг...
S>Это в ФП подобные проблемы возникнуть не могут, т.к. там совместимость любых функций с одинаковой сигнатурой ставится во главу угла.
Разумеется, но это же без проблем.
Просто считается, что любой функциональный тип с одной сигнатурой — он ровно один на всех.
Тут проблем нет.
И не зря в функциональных типах часто развита система алиасинга типов (аналог typedef в Си/С++).
Слава богу, в 2022-м появился global using в C#. ))
Пара десятилетий потребовалось для вменяемого алиасинга.
Но это должно было быть сразу, и под функицональынй тип должна была быть механика неявного алиасинга, как в ФП.
В общем, придирки к дотнету в этом вопросе не в том, что они чего не придумали сами (за это их никто бы не полоскал), а за то, что не взяли уже давно придуманное и отлаженное.
Причём, это не какая-то мелочь, а самая базовая весч, которую они же взялись реализовать сразу же — функциональынй тип!
Ну так и делайте его ровно так, как он уже давно отшлифован как в концепции, так и в реализации этой концепции что на уровне компилятора, что в бинарном виде.
Там же, хосподя, примитивщина всё.
Например, по выходу дотнета за несколько вечеров я накатал компилятор ограниченной реализации Схемы.
Ты бы знал, как я матерился, выбирая представление функционального типа в конечном образе, ууу...
Зато освоил способы описания типов-делегатов "на лету", в процессе работы компилятора.
Разумеется, у меня был глобальный словарь всех уникальных сигнатур, встреченных программой.
Т.е., в наколенном варианте за несколько вечеров это решить можно, а на уровне инфраструктуры "забыли", угу...
При том, что F# был одним из первых реализованных языков.
Я не зря потом купил книгу одного из авторов дотнета, как они описывали процесс создания фреймворка, мне просто было интересно, как ои до такого дошли.
Книга написано вполне читабельно, но вызывает сожаление — они "тренировались на кошках" в основном в деле реализации C# и Питона.
При том, что Питон под дотнет так и остался невостребован.
Лучше бы разрабатывали его из F# и C#.
S>А в лиспе так и вообще, одинаковость сигнатуры не является критерием.
Ес-но, тут динамическая типизация vs статическая.
Но утиная типизация или нет перпендикулярна статической типизации, прикинь? ))
Порвал шаблончик или нет?
(опять и снова отсылаю к статически компиллируемым ФП-языкам)
V>>Повторяю в сотый раз — "чисто ФП-шные" штуки были в ООП чуть не в первых ООП-языках.
V>>Например, в том же Алголе-68.
V>>Т.е. функциональный тип как первоклассный в языке, лямбды.
S>Мне кажется, вы слишком смело называете Алгол ООП-языком. Никакого ООП в нём не было.
Я называю Алгол-68.
Это первый полноценный ООП-язык.
V>>Симула, кстате, была тоже лишь попыткой объектного расширения Алгола.
S>Как так — взяли ООП-шный алгол и попробовали привинтить к нему "объектное расширения"?
Ну да.
Берется язык со всем его синтаксисом и стандартной библиотекой.
Берутся исходники всего этого.
Допиливается.
Получается Симула, которая практически тот же Алгол.
Но работы была проведена недостаточно качественно в 67-м, поэтому появился Алгол-68.
На Симуле обкатали концепции, но недоделки стали видны сразу же — этот язык не мог претендовать на роль языка общего назначения.
Поэтому Алгол 68 уже мог.
Симулу могли бы назвать "Объектным Алголом".
Похоже, тебя чуть сбивает с толку совсем левое название языка. ))
Тот же Алгол, хосподя...
V>>Да и Смолтолк приобрёл свои характерные черты не в первой версии 72, и даже не в 76, а только в 80.
S>:sigh:
S>http://wiki.c2.com/?AlanKaysDefinitionOfObjectOriented
Мы тут говорили о коде как о данных.
Это появилось только с метаклассами в Смолтолке-80.
До этого никакой рефлексии не было.
Что касается ООП-парадигмы — она была уже давно обкатана до первых версий Смолтолка.
Смолтолк — это попытка выделить ООП-парадигму "в чистом виде".
Никакой практической пользы "чистота ООП" Смолтолка не имела до введения метаклассов аж в 80-м году, когда стало возможным проводить инспекцию кода, рефакторить и всё это было обёрнуто в IDE.
Поэтому, заслуга Смолтолка не в ООП-парадигме, а в развитой системе метаинформации об ООП-like объектах.
В возможности динамического создания типов и экземпляров этих типов.
А ты тут в очередной раз показываешь непонимание, откуда у чего растут ноги в IT. ))
Причина-следствие, причина-следствие... Это же важно, не?
V>>Для этого значение должно быть const в контексте внешнего кода.
S>Не-не-не, Дэвид Блейн. Вот я пишу свой код. И в нём мне бы хотелось полагаться на то, что его вызывают не с чем попало, а с неизменяемым объектом.
Да какие проблемы (мать, мать, мать...) — опиши сам объект как неизменяемый.
Тебе показать, как описывать иммутабельные объекты в С++?
Модификатор const к ссылке/указателю на объект разрешает вызывать только const-методы этого объекта.
То бишь, предполагается, что у этого объекта могут быть и не const-методы.
Я что в тот раз фигел от твоего непонимания сей элементарщины, что сейчас...
Заметь, в дотнете для структур ввели аналогичную семантику readonly для методов.
Поэтому, если структура передаётся по readonly ref-ссылке (кратно in), то можно вызывать только readonly методы.
В точности как в С++.
Неужели все вокруг идиоты и не понимают, ЗАЧЕМ это делается именно так? ))
Похоже, не понимаешь только ты, считая всех вокруг заранее идиотами.
V>>Т.е. значение может быть const по всей иерархии "внешности", вплоть до объявления.
S>Внешний код ещё не написан — каким образом я смогу потребовать от него const на этом значении "по всей иерархии внешности"?
Но тип аргумента-объекта уже должен уже быть? ))
V>>Это на выбор программиста.
S>
Ну да.
Хочешь полной иммутабельности — не запрещено.
Хочешь частичной — тоже велкам.
Выбирай наиболее эффективную стратегию решения конкретной задачи.
V>>Что будет лишь малой частью всех возможных сценариев по слишком очевидной причине (тут уже сложно не материться) — ввиду наиболее сильного наложенного на сценарий ограничения. S>Ок, как видим, вы так и не поняли, что такое иммутабельность, и почему она ортогональна const.
Мы видим несколько иное — чванливость Синклера, который иногда не в состоянии воспринимать информацию по причине ЧСВ и более ни по какой. ))
V>>Странно, что мы вообще спорили тогда и сейчас вокруг подобной элементарщины...
S>Да ничего странного — у вас отсутствует обучаемость. Годы идут, а вы так и не вышли за пределы своих заблуждений.
И даже сейчас ты не понял, как описывать иммутабельные типы в С++?
Признайся, уже понял и тебе дико стыдно.
S>В качестве упражнения можете попробовать спроектировать на С++ пару абстрактных классов — изменяемый вектор и immutable вектор. В терминах дотнета это были бы IImmutableList<T> и IList<T>. И посмотрите, можно ли их отнаследовать друг от друга; можно ли получить один из другого при помощи модификатора const; понадобится ли вообще модификатор const при проектировании этих классов или при их использовании.
1. В дотнете я могу породить мутабельный тип из IImmutableList<T> аж бегом и со свистом.
2. В этой же технике в точности аналогично можно и в С++.
Ты не там гарантии иммутабельности ищещь.
Гарантии иммутабельности может иметь только сам тип.
Еще в прошлые разы я обращал твоё внимание на то, что более выгодно с практической точки зрения, чтобы не твой код требовал иммутабельности объекта, а чтобы внешний код требовал, чтобы твой код не мог модифицировать объект.
Почему так? — по очевидной причине, ведь твой код тогда сможет работать как с тру-иммутабельными объектами, так и с неиммутабльными при рождении, но которые решили сделать иммутабельными "чуть позже". Например, некий достаточно сложно инициализируемый ветвистый словарь чего-то там.
В целях эффективности в момент наполнения словарь может быть мутабельным, а потом в какой-то момент владение передаётся константной ссылке/указателю и ву а ля.
В отсутствии const в этом сценарии потребуется два типа — мутальный и иммутабельный, где готовый мутабельный словарь копируется в иммутабельный, с соответствующими накладными расходами и прессингом на память. В С+ в этом сценарии достаточно скопировать ссылку/указатель.
Мне казалось, что ты понял эту особеность уже тогда, коль закончил пререкаться...
Но сейчас, смотрю, тебя всё еще распирает от врождённого упрямства "это ВЫ чего-то не понимаете, а не Я!!!111"
Значит, ни хрена ты не понял.
Походу, и не пытался, наивно надеясь, что всё понимаешь и так.
Не всё.