Logo

14 мая 2015 г.

Фрактальная модель: теория объектно-ориентированной ОС

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

Данная статья основана на материалах 2012 года, публиковавшихся на предыдущем сайте проекта и даже упомянутом в качестве литературы к одной из статей конференции «Объектные системы — 2013» (см. ссылки в конце статьи).

Сейчас текст адаптирован под текущую ситуацию в проекте и публикуется как предварительный материал к описанию фрактальной модели как ключевой концепции. Изначально планировалось разбить его на несколько частей, но поскольку тема не развивалась с 2013 года, решено опубликовать всё в одной статье, закрыв технический долг по теориям ООП.

«Теория всего» объектно-ориентированной ОС

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

То есть, говоря об объектно-ориентированной ОС, мы должны дать определение программы и файла через понятие класса и объекта, либо же вообще от них отказаться и пользоваться только терминологией ООП, иначе не избежать двусмысленностей и подмены понятий.

Всё есть объект?

Разработчики ОС, пытаясь отойти от классической парадигмы и двинуться в сторону объектов, чаще всего отталкиваются от понятия:
Всё есть объект.
Но на самом деле всё не так просто, как кажется. В самом деле, что есть объект? Всё? Совсем всё? Вот файл на диске — объект? А если файл исполняемый, в нем внутри лежат секции кода, данных, ресурсов, перемещаемые символы и пр. Эти секции — объекты? А одиночный ресурс в ресурсах, — та же иконка, — объект? А палитра иконки? А цвет палитры?

Если подняться выше, каталог — это объект? А раздел с файловой системой? Том носителя? Сам носитель как устройство? Да и сам компьютер как узел глобальной сети — объект или нет? Как вместить это в ОС? Провести границу? Как? Отделить «хорошие» объекты от «плохих»? Или объекты от необъектов?

Всё есть контейнер!

Если подходить к понятию объекта абстрактно, в общем случае мы не можем определить границы объектности и степень вложенности объектов друг в друга. Объект, могущий содержать в себе другие объекты, называют контейнером. Если принять невозможность определения границ вложенности объектов за данность, правильное определение «Всё есть объект» звучит как:
Всё есть контейнер.
Мысля в парадигме контейнеров, мы считаем объект вырожденным случаем контейнера, а не наоборот. Кроме того, могут существовать так называемые переходы уровней, аналогичные фазовым или квантовым переходам в физике, когда сущность, представленная неделимым объектом на одном уровне, является контейнером на другом, низлежащем.

Например, машинное слово состоит из байтов, а байт — из битов, но вместе они лежат на одном уровне — программном. На уровне оборудования биты есть импульсы напряжения, уже недоступные программно. Сходные границы разделяют объекты в памяти и на диске, а также локальные и удаленные (сетевые) объекты.

Фрактал

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

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

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

На самом же деле, не имея объектно-ориентированной ОС, настоящего ООП мы так до сих пор и не видели, ограничиваясь лишь частными его применениями.

Фрактальность по агрегации и фрактальность по наследованию

Фрактальность ОО-модели проявляется в двух измерениях одновременно:
  • Бесконечная вложенность контейнеров, описанная выше — фрактальность по агрегации.
  • Бесконечное появление новых классов с перераспределением части функциональности старых на промежуточные уровни абстракции — фрактальность по наследованию.
Получается, что перед программистом, вставшим на путь разработки объектно-ориентированной ОС, стоит задача совладать с фракталом, для чего вначале нужно подробней его изучить.

Хрупкий базовый класс

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

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

Фрактальность ОС во времени

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

Возможная хрупкость базового класса в будущем — и есть фрактальность во времени — фрактальность по наследованию, присутствующая в объектно-ориентированной ОС одновременно с фрактальностью по агрегации — вложению контейнеров друг в друга.

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

Декомпозиция

Убедившись, что разработка объектно-ориентированной ОС — процесс непрерывной ОО-декомпозиции, вначале нужно дать определение этой декомпозиции. Для начала введем понятие нормализации, исходя из гипотезы, что любая сложная ООП-система должна иметь некоторое устойчивое состояние, в котором сохраняется исходная работоспособность, но число связей минимизировано.
Система с минимальным числом связей, тождественно решающая некоторую задачу, является нормализованной.
Декомпозиция — поиск этого состояния и сама процедура нормализации.

Объектно-ориентированная БД

Понятие нормализации подводит нас к еще одной идее: фрактальная модель соответствует не только объектно-ориентированной операционной системе, но и объектно-ориентированной базе данных.

Это тоже довольно известная концепция, время от времени будоражащая умы разработчиков: использовать базу данных вместо файловой системы как основу ОС. Заменить ФС на БД пытались многие. По крайней мере, заявляли об этом. На деле все обещания оказались либо мечтами, либо маркетингом. В лучшем случае делались попытки прикрутить обычную СУБД к обычной ОС, не увенчавшиеся успехом.

Мы же, рассуждая логически, пришли к идее объектно-ориентированной БД на концептуальном уровне, получив ее одновременно с объектно-ориентированной ОС. Если рассматривать полученную систему как СУБД, получаем:
  • Ядро СУБД должно контролировать целостность данных с учетом наследования и агрегации, поскольку именно эти отношения заменяют реляционные.
  • Должна обеспечиваться согласованность системы и атомарность переходов из одного согласованного состояния в другое — транзакции. Каскадные изменения объектов в рамках одной транзакции, фиксируемые или откатываемые одномоментно, концептуально ложатся на фрактальную модель и ничем ей не противоречат.
  • Иметь в своей основе какой-то принцип балансировки, позволяющий отделить нормализованное состояние модели от денормализованного по формальным признакам.
То есть, для дальнейшего развития мы должны вывести правило нормализации — оно и станет нашей процедурой декомпозиции, позволяющей фракталу расти в нужном направлении итеративно (транзакционно?) в условиях, когда опережающая декомпозиция невозможна.

Подготовка к нормализации

Чтобы вывести правило нормализации, вначале нужно минимизировать число сущностей, упаковав друг в друга всё, что используется сейчас в ООП. Мы ведь решили рассуждать об объектно-ориентированной ОС в обычных терминах, понятных каждому ОО-программисту?

В практическом ООП, помимо классов и объектов, используются следующие понятия: интерфейсы, примеси, поля, методы и свойства. А вот в реляционной модели всего два понятия — сущности и атрибуты, в реализации СУБД становящиеся таблицами и полями. Можно ли как-то сократить число используемых сущностей в ООП?

Интерфейсы

Интерфейс в ООП — абстрактный класс, без полей и без реализации методов. В языках программирования Delphi и Java интерфейсы есть как отдельные сущности, а в C++ их нет, их роль играют чистые абстрактные классы. Делаем вывод, что на практике важна абстрактность класса или отдельных его методов, а не сами интерфейсы как сущность.

Delphi и Java разрешают множественное наследование только от интерфейсов, потому что не хотят решать проблему дублирования полей и реализаций методов, неизбежно возникающую при наследовании от нескольких предков. В C++ такое наследование есть, но уж очень запутанно.

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

Примеси и типажи

Примесь (mixin) или типаж (trait) отличается от интерфейса наличием уже реализованных методов, подмешиваемых к основному классу для придания ему дополнительного поведения. Примеси похожи на интерфейсы своей абстрактностью: создавать экземпляры примесей нельзя, как и экземпляры интерфейсов.

Абстрактность как отношение

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

Чтобы избавиться от интерфейсов и примесей, нужно вывести абстрактность как отношение между классами. Тут нас ожидает проблема: содержимое классов составляют две сущности — поля и методы, но отношения между ними не формализованы. Поля и методы пришли из процедурного программирования и являются ОО-оберткой над переменными и процедурами. Для выведения абстрактности как отношения нужно либо формализовать взаимосвязь полей и методов, либо вообще объединить их в одну сущность.

Свойства

Так сложилось, что понятие свойств, несмотря на свою близость концепциям классического ООП, в популярные языки было добавлено довольно поздно, и впервые свойства появились в Delphi. Для нас свойства примечательны тем, что являются абстракцией как над полями, так и над методами, поскольку реализацией свойства может быть указано и то, и другое.

Свойства — мощный механизм, удобно абстрагирующий согласованное изменение состояния объекта и упрощающий реализацию такого изменения. По сути, это уже готовая абстракция понятию «атрибут», переведенному из реляционной модели в ООП. Тогда поставленный ранее вопрос об упаковке перефразируется следующим образом: можно ли как-то избавиться от полей и методов, полностью упаковав их в свойства?

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

Взаимная абстрактность

Упаковав поля и методы в свойства, можно вывести понятия абстрактности и взаимной абстрактности:
Абстрактное свойство не имеет реализации.
Классы взаимно-абстрактны, если не имеют реализации одинаковых свойств.
Оба определения даны в общей форме и подразумевают раскрытие понятия одинаковости реализацией или средой. В него входит определение уникальности свойств в пределах класса, наличие или отсутствие единого предка всех классов или даже использование «утиной» типизации вместо строгой. В нашей работе одинаковость определяется правилами перегрузки (overload) свойств.

Нормализация

Сведя базовые понятия ООП к двум сущностям — классам и свойствам, можно дать определение нормализации классов:
Наследование возможно только от взаимно-абстрактных классов.
Тем самым мы допускаем множественное наследование, но избегаем усложнения не введением новых сущностей, а определением абстрактности как отношения — взаимной абстрактностью классов.

Принципы SOLID

Получив правило нормализации классов, продолжим аналогию с СУБД, и вспомним про ACID — набор требований к СУБД. К ОО-системам есть похожий набор требований, обозначаемый аббревиатурой SOLID:

Избавление от приведения типов

Как и в случае БД, нормализация исключает дублирование данных, представляя собой реализацию декомпозиции. В свою очередь, декомпозиция на основе нормализации классов является альтернативой какой-то другой операции, и использование нормализации позволяет избавиться от этой операции. В случае ООП таковой операцией является приведение типа.
В большинстве случаев явное приведение типов свидетельствует о дефектах проектирования.
Б. Страуструп
В самом деле, в практике ООП приведение типа приходится использовать, чтобы обойти недостаточность декомпозиции и невозможность внесения изменений в библиотечные классы из-за их хрупкости. Если же декомпозиция выполняется путем нормализации, в приведении типа нет нужды, потому как любой класс в любое время может быть разбит на нужное число взаимно-абстрактных предков, и приведение типа никогда не потребуется. В этом смысле нормализация в ООП работает точно так же, как и в реляционных СУБД: данные выносятся в справочники, а между таблицами устанавливаются связи «один-к-одному», «один-ко-многим» и «многие-ко-многим».
Нормализация классов избавляет от приведения типов.

Нарушение принципа открытости/закрытости

Рассматривая фрактальную модель с точки зрения принципов SOLID, легко заметить, что фрактальность во времени нарушает принцип открытости/закрытости, придавая хрупкость классам и лишая нас возможности проводить декомпозицию по мере надобности, постепенно строя фрактал. Как уже говорилось, построение фрактальной модели невозможно без решения проблемы хрупкости классов.

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

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

Декларативная целостность

Рассмотрев проблему открытости/закрытости через призму фрактальной модели, получаем:
  • Контроль правильности решений во фрактальной ОС должен исключить человеческий фактор. По возможности целостность (правильность) должна быть декларативной.
  • Технически разработка фрактальной ОС невозможна из-за нефрактальности исходных текстов.
То есть, чтобы решить проблему хрупкости базовых классов и сделать возможной разработку фрактальной ОС, задачу нужно превратить в техническую:
  • Перейти к декларативной целостности.
  • Отказаться от исходных текстов.
Тут снова можно провести параллель с СУБД. Реляционная модель — чистая абстракция, и все ее представления в виде UML или SQL-кода — лишь проекции, отражающие взгляды на ее в удобном для просмотра или редактирования виде. При изменении постановки требуется повторная декомпозиция и внесение согласованных изменений в модель, что ведет к перегенерации исходных текстов. Их можно править вручную, а можно использовать CASE-средства. При этом абстрактность реляционной модели принимается за данность, и хрупкость исходных текстов не служит причиной отказа от реляционных СУБД. Наоборот, придумываются инструменты для генерации исходных текстов по имеющимся БД или из UML-диаграмм, — те самые CASE-средства.

Обратимый код

Обеспечить декларативную целостность фрактальной модели при сохранении ее редактируемости человеком можно только одним способом — при помощи обратимого кода.
Разработка фрактальной ОС возможна лишь на основе обратимого кода.
Обратимый код станет мостиком между человеком и машиной:
  • Для ядра ОС обратимый код будет обеспечивать формальное описание фрактальной модели, не ограниченное текстовым представлением.
  • Взаимная обратимость между кодом и текстом позволит редактировать части модели в привычном человеку представлении, временно переводя их в текстовый вид, а потом возвращая разностные изменения обратно в модель.
Отвязавшись от текстов, мы делаем первый шаг к декларативной целостности. Чтобы обеспечить декларативную целостность на практике, нужно связать ООП с одной из парадигм декларативного программирования. В языке Кантор ООП связано с функциональным программированием, а классы являются особым типом функций.

Ссылки

2 комментария

  1. Навеяно вашими статьями о том, что раз есть ООП и ООБД, то должна быть и ОООС.

    Суть объектно-ориентированного подхода вот в чём: есть объекты с определёнными свойствами, которые разбиты по классам. С ними мы имеем дело в реальном мире -- тогда почему бы не иметь с ними дела при программировании? Большинство современных языков создаёт объекты -- экземпляры классов -- и через них работает. Каждый класс -- это полноценная подпрограмма, способная к наследованию (то есть к получению свойств родительского класса и передаче своих -- по наследству)

    Этот подход работает как при операциях с данными, так и при их хранении. Но операционные системы всё ещё используют процедурный подход. Более того, именно через него эмулируется объектно-ориентированный подход. Многие пытались это исправить,но мало у кого что вышло. Вот несколько заметок о том, как это можно организовать.

    Для начала, объект -- это ячейка памяти, где через разделители указаны имена свойств и адреса ячеек памяти, где эти свойства хранятся. Тогда нулевой объект -- это пустая ячейка с памятью. Первый объект -- это объект "свойство", который ссылается на нулевой объект. Второй объект -- это объект "наследник", который имеет свойство "родитель", через которое ссылается на первый. Третий объект -- "Создатель". У него есть два свойства -- "родитель" (второй объект) и "создание" -- команда на ассемблере, которая получает через разделитель два параметра (имя свойства и значение). Эта команда делает следующие операции:

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

    Нетрудно догадаться, что эта команда третьего объекта создаёт всё кучу других объектов, где есть команды, реализующие элементарные операции (условия, циклы и прочее). Но делает она это не сама -- есть четвёртый объект "Архив". Как родитель указан третий объект, а второе свойство -- хранение -- является командой с тремя параметрами: имя объекта, имя его свойства, значение этого свойства. Нетрудно догадаться, что в четёртом объекте находится история всех созданных свойств всех объектов и их адреса. То есть теперь есть доступ к адресам всех объектов, которые создаются через команду "конструктор".

    Функционал четвёртого объекта уже позволяет создавать сложные команды с кучей взаимных ссылок и с использованием условий и циклов. И всех предыдущих команд. Только навигация несколько громоздкая, да и удобство пользования четвёртым объектом не самое большое. Для этого есть пятый объект -- "Классификатор". Его родитель -- четвёртый объект (куда он входит как одно из свойств), и у него есть ещё несколько отличительных свойств:

    Прототип -- объект, копия которого делается при создании экземпляра класса
    Экземпляры -- объект, где хранятся имена и адреса всех экземпляров
    Конструктор -- объект, команда которого возвращает копию прототипа (экземпляр класса), имя объекта и его адрес записываются в свойство "Экземпляры".

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

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

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

    При такой организации системы каждый следующий этап расширяет и объясняет предыдущий вплоть до его переопределения. Так, если четвёртый объект все описания объектов и свойств хранит в одном месте, создавая разросшуюся ячейку памяти, то шестой объект уже даёт гораздо более гибкие возможности для настройки. Более того, через базу данных решается и другая проблема -- разросшееся число ссылок. Действительно, у высокоуровневых объектов значения свойств -- это другие объекты, и до ячеек с собственно данными дело доходит очень долго. Однако все эти ссылки на ячейки можно объединить в одну длинную строку таблицы БД, а имея прототип строки, можно сразу вытащить оттуда значения, минуя все ссылки.

    Еще одна проблема ООП -- никогда не знаешь, как сделать правильную структуру классов, так как при развитии системы уровень абстракции объектов следует повышать. Но и эта проблема решается, поскольку можно переопределить свойство "родитель" и работающие с ним методы. Для этого создаваемый класс наследует не родительскому классу, а объекту класса "Посредник", у которого в родителях -- нужный класс и в свойствах -- функция "абстрагировать":

    Все свойства объекта, кроме частных и защищённых, а также все публичные методы, не требующие обращения к частным и защищённым свойствам и методам данного объекта, становятся свойствами объекта "посредник".
    Объект "посредник" получает имя и регистрируется в БД.
    Функции можно передать новые свойства и методы в качестве параметров, и они станут свойствами и методами данного объекта.
    Перед и после него создаются новые объекты класса "Посредник", чтобы проявить возможности для дальнейшего абстрагирования.

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

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

    Решается проблема небольшим расщирением языка С, которое позволяет реализовывать первые 4 объекта (и кучу побочных объектов в рамках четвёртого, несводимых к стандартным командам языка С) -- его можно писать только на ассемблере. В рамках четвёртого объекта можно вставить объекты, свойствами которых будут стандартные команды на языке С (условия, циклы) и более сложные программы, способные обрабатывать строковые переменные (которыми, для языка С, и будут являться объекты). На языке С далее можно написать и пятый, и шестой объекты -- при компиляции они из строковых переменных превратятся именно в то, что нужно. А затем потребуется перевести стандартные программы того же ядра Linux в новый формат, то есть сделать их методами объектов.

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

    ОтветитьУдалить

Выбрав в выпадающем списке пункт «Имя/URL», можно оставить комментарий от своего имени без предварительной регистрации.