Итак, запомнив, что у проблемы два аспекта - прогноз (описание) и контроль (проверка допустимости поведения данных), поищем другие решения.
Отметим, что по отношению к ЯП мы сейчас колеблемся между технологической и авторской позициями, задевая семиотическую.
Второй вариант: структурная совместимость типов. Воспользуемся принципом обозначения повторяющегося. Заметили, что часто приходится иметь дело с перечнями атрибутов? Значит, нужно обозначить повторяющийся перечень некоторым именем и упоминать это имя вместо самого перечня. Следовательно, нужен языковый конструкт для объявления таких имен.
Естественно считать, что имя обозначает не только сам перечень атрибутов, но и класс данных, обладающих объявленными характеристиками. Мы пришли к понятию типа данных как класса объектов-данных, обладающих известными атрибутами. А искомый языковой конструкт - это объявление типа данных. Его назначение - связывать объявляемое имя с указанным перечнем атрибутов данных. Подчеркнем, что основой классификации данных остается перечень характеристик-атрибутов, а имя типа лишь обозначает соответствующий перечень.
Теперь, чтобы характеризовать данное, не нужно умалчивать атрибуты, как в ПЛ/1, можно коротко и ясно обозначать их одним именем. Кажется, совсем просто, но это шаг от ПЛ/1 к Алголу-68, в котором очень близкая к изложенной концепция типа данных.
С проблемой прогнозирования мы справились, а как с проблемой контроля? По-прежнему нужно сравнивать перечни атрибутов. Другими словами, здесь мы никак не продвинулись. Как справиться с проблемой? Ведь для контроля поведения данных (например, контроля совместимости аргументов с параметрами) недостаточно знать имена типов: если имена совпадают, все ясно, а вот когда не совпадают, нужно проверять совместимость характеристик типов.
Проблема структурной совместимости типов (иногда говорят, проблема структурной эквивалентности типов, потому что простейшее правило совместимости - эквивалентность атрибутов) в общем случае очень сложна, может оказаться даже алгоритмически неразрешимой.
Дело в том, что как только возникают имена типов, естественно их применять и в перечнях атрибутов. Например, при определении комбинированного типа указываются типы полей, при определении процедурного типа - типы параметров процедуры и т. п. Но в таком случае имя типа может оказаться связанным уже не с единственным перечнем атрибутов, а с классом таких перечней (возможно, бесконечным, например, описывающим свойства рекурсивных структур-списков). Другими словами, допустимые для каждого типа перечни атрибутов определяются, например, контекстно-свободной грамматикой (БНФ). Так что проблема эквивалентности типов сводится к проблеме эквивалентности контекстно-свободных грамматик, которая в общем случае алгоритмически неразрешима.
Третий вариант: именная совместимость типов. Поистине блестящее решение состоит в том, чтобы полностью избавиться от проблемы структурной совместимости, "чуть-чуть" подправив концепцию типа, сделав центральным понятием не атрибуты, а имя типа. При этом типы с разными именами считаются разными и в общем случае несовместимыми (если программист не ввел соответствующих операций преобразования типов). Другими словами, забота о содержательной совместимости характеристик объектов полностью перекладывается на программиста, определяющего типы (что вполне естественно). Если теперь потребовать, чтобы каждый объект данных был связан ровно с одним типом, то и прогнозировать, и проверять - одно удовольствие! Нужно сказать, что объект будет вести себя так-то, обозначаем стиль (характер) его поведения именем типа из имеющегося набора типов. Нет подходящего - объявляем новый. Нужно проверить совместимость, сравниваем имена типов. Просто, ясно и быстро.
Но наше "чуть-чуть" - в историческом плане шаг от Алгола-68 с его структурной совместимостью типов к Аде с ее именной совместимостью через Паскаль, где именная совместимость принята для всех типов, кроме диапазонов (для них действует структурная совместимость).
4.2.3. Строгая типизация и уникальность типа
Априорная несовместимость типов, названных разными именами, вместе с идеей "каждому объекту данных - ровно один тип" образуют концепцию типа, которую мы назовем концепцией уникальности типа (или просто уникальностью типа). Это ключевая концепция аппарата типов в Аде. Сформулируем правила (аксиомы) уникальности типа:
1. Каждому объекту данных сопоставлен один и только один тип.
2. Каждому типу сопоставлено одно и только одно имя (явное или неявное). Типы с неявными именами называются анонимными, и все считаются различными.
3. При объявлении каждой операции должны быть явно указаны (специфицированы) имена типов формальных параметров (и результата, если он есть).
4. Различные типы априорно считаются несовместимыми по присваиванию и любым другим операциям.
Очень близкая концепция в литературе часто называется строгой типизацией [26], а ЯП с такой концепцией типа - строго типизированными. От уникальности типа строгая типизация отличается возможным ослаблением четвертой аксиомы. Поэтому мы ввели специальный термин "уникальность типа" для "совсем строгой" типизации.
4.2.4. Критичные проблемы, связанные с типами
Остался маленький вопрос. Прогнозировать - легко, проверять - легко. А легко ли программировать? Как увязать несовместимость типов, обозначенных разными именами, с естественным желанием иметь операции, применимые к объектам разных типов (полиморфные операции). Ведь если связать с формальным параметром операции некоторый тип, то к объектам других типов эта операция окажется неприменимой.
Назовем отмеченную проблему проблемой полиморфизма операций. Напомним, что полиморфизм операций широко распространен в математике, жизни, программировании. Операция "+" применима к целым числам, вещественным числам, матрицам, комплексным числам, метрам, секундам и т. п.
Проблема полиморфизма возникла, как только мы взглянули на уникальность типа со стороны операций. Но неприятности поджидают нас и со стороны данных. Вполне естественны ситуации, когда в процессе работы программы один и тот же объект выступает в разных ролях, обладает различными характеристиками. Например, число яблок может превратиться в число людей, взявших по яблоку; буфер, работавший в режиме очереди, может начать функционировать в режиме стека; у массива может измениться размерность и т. п.
Каким должен быть единственный (уникальный) тип такого объекта? Если он отражает сразу много ролей, то не придем ли мы к отсутствию контроля (ведь пока буфер работает как очередь, к нему нужно запретить обращаться как к стеку и наоборот)? Если же только одну роль, то как ему попасть в другую (ведь разные типы априорно несовместимы)? Назовем выделенную проблему янус-проблемой (объект ведет себя, как двуликий Янус, даже "многоликий").
4.2.5. Критичные потребности и критичные языковые проблемы
Несколько драматизируем ситуацию и временно представим, что найти решение проблемы полиморфизма не удалось. И вот мы в роли программиста, который старается следовать хорошему стилю создания программ.
Допустим, что к его услугам тип "целый", тип "комплексный", тип "матрица". Ему нужно предоставить пользователю возможность складывать объекты любого из названных типов. Хороший стиль программирования требует ввести операционную абстракцию, позволяющую пользователю игнорировать несущественные детали (в данном случае - особенности каждого из трех типов данных) и действовать независимо от них. Попросту говоря, нужно ввести единую (полиморфную) операцию сложения. Если такой возможности ЯП не предоставит, то у программиста может оказаться единственный разумный вариант - избегать пользоваться таким ЯП. Аналогичным ситуациям нет числа. Например, есть возможность рассчитать потребности взвода, роты, батальона, полка. Требуется ввести абстракцию - "рассчитать потребности подразделения" и т. п.
Мы пришли к понятию критичной технологической потребности. Технологическая потребность называется критичной для ЯП в некоторой ПО, если язык не может выжить в этой ПО без средств удовлетворения этой потребности. Проблему удовлетворения критичной потребности назовем критичной языковой проблемой.
4.2.6. Проблема полиморфизма
Допустим, что критичность проблемы полиморфизма для Ады осознана. Как же ее решать?
По-видимому, самым естественным было бы ввести "объединяющий" тип (например, "операнд_сложения" или "боевое_подразделение"), частными случаями которого были бы исходные типы. И определить нужную операцию для объединяющего типа. Но что значит "частными случаями"? Ведь в соответствии с концепцией уникальности каждый объект принадлежит только одному типу. Если это "взвод", то не "боевое_подразделение"! Так что в чистом виде эта идея не проходит.
Нечто подобное можно реализовать с помощью так называемых ВАРИАНТНЫХ типов, но каждый вариант должен быть выделен соответствующим значением дискриминанта, причем заботиться об этом должен пользователь такой "квазиполиморфной" операции. Поэтому такое решение можно рассматривать лишь как суррогат.
Вот если бы к услугам программиста уже был тип "боевое_подразделение", а ему понадобилось ввести новые операции именно для взводов, то (при условии, что у объектов "боевое_подразделение" есть поле "вид") можно было бы объявить, например,
type Т - взвод is new боевое_подразделение (вид => взвод);
Теперь "взвод" - это уже знакомый нам ПРОИЗВОДНЫЙ тип с РОДИТЕЛЬСКИМ типом боевое_подразделение. К его объектам применимы все операции, применимые к боевому_подразделению. Но можно теперь объявить новые операции, применимые только к "взводам", т. е. к "боевым_подразделениям", в поле "вид" которых - значение "взвод". Итак, это решение проблемы полиморфизма "сверху вниз", т. е. нужно заранее предусмотреть частные случаи нужного типа.
Основной вариант решения проблемы полиморфизма, предлагаемый Адой, это так называемое ПЕРЕКРЫТИЕ операций. Идея состоит в том, что связь между вызовом операции и ее объявлением устанавливается не по одному только имени операции, а по так называемому "профилю" (имени операции с учетом типов операндов, типа результата и даже имен формальных параметров (если вызов по ключу)). Другими словами, идея перекрытия в том, что денотат знака определяется не по самому знаку, а с привлечением его ограниченного контекста.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |


