Rationale for Ada 2005: Structure and visibility
RUSTOPBACKNEXT
ENG |
5. Limited types and return statements
@ The general idea of a limited type is to restrict the operations that a user can do on the type to just those provided by the author of the type and in particular to prevent the user from doing assignment and thus making copies of objects of the type. @ However, limited types have always been a problem. In Ada 83 the concept of limitedness was confused with that of private types. Thus in Ada 83 we only had limited private types (although task types were inherently limited). @ Ada 95 brought significant improvement by two changes. It allowed limitedness to be separated from privateness. It also allowed the redefinition of equality for all types whereas Ada 83 forbade this for limited types. In Ada 95, the key property of a limited type is that assignment is not predefined and cannot be defined (equality is not predefined either but it can be defined). The general idea of course is that there are some types for which it would be wrong for the user to be able to make copies of objects. This particularly applies to types involved in resource control and types implemented using access types. @ However, although Ada 95 greatly improved the situation regarding limited types, nevertheless two major difficulties have remained. One concerns the initialization of objects and the other concerns the results of functions. @ The first problem is that Ada 95 treats initialization as a process of assigning the initial value to the object concerned (hence the use of := unlike some Algol based languages which use = for initialization and := for assignment). And since initialization is treated as assignment it is forbidden for limited types. This means that we cannot initialize objects of a limited type nor can we declare constants of a limited type. We cannot declare constants because they have to be initialized and yet initialization is forbidden. This is more annoying in Ada 95 since we can make a type limited but not private. @ The following example was discussed in the Introduction
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale for Ada 2005: Structure and visibility
@ENGRUSTOPBACKNEXT5. Ограниченные типы и операторы return
@ Основная идея ограниченных типов состоит в том чтобы ограничить операции, которые пользователь может сделать с типом (разрешаются только те которые предоставленны автором типа) и в особенности препятствовать тому, чтобы пользователь выполнял присваивания и таким образом сделал копии объектов типа.
@ Однако, ограниченные типы всегда были проблемой. В Аде 83 понятие limitedness было тесно связано с private типом. Таким образом в Аде 83 мы могли ограничивать только private типы (хотя типы задачи были неотъемлемо ограничены).
@ В Аде 95 было сделано два существенных изменения. А именно, было позволено limitedness быть отделенным от private. А также было разрешено переопределение равенства для всех типов, тогда как в Аде 83 запрещалось это для ограниченных типов. В Аде 95, ключевое свойство ограниченного типа состоит в том, что присваивание не предопределено и не может быть определено (равенство не предопределено изначально, но это может быть определено). Основная идея состоит в том, что есть некоторые типы, для которых пользователю запрещено делать копии объектов. Это особенно относится к типам, вовлеченным в контроль за ресурсом, и типам использующим ссылочные типы.
@ Однако, хотя Ада 95 очень улучшила ситуацию относительно ограниченных типов, однако две главных трудности остались. Первая касается инициализации объектов, другая - некоторых проблем с результатами функций.
@ Первая проблема состоит в том, что Ада 95 обрабатывает инициализацию как процесс назначения начального значения некоторому объекту (используя знак := в отличие от Алгол - подобных языков, где принято использовать = для инициализации а := для присваивания). Т.о. инициализация обрабатывается как присваивание, которое запрещено для ограниченных типов. Это означает, что мы не можем инициализировать объекты ограниченного типа, и при этом мы не можем объявить константы ограниченного типа. Мы не можем объявить константы, потому что они должны быть инициализированы, и в то же время инициализация запрещена. Это является весьма раздражающим в Аде 95, так как мы можем сделать тип ограниченным, но не приватным.
@ Следующий пример обсуждался во Введении:
|
@ Отметим, что этот тип явно ограниченный (но не частный), но его компоненты не ограничены. Если мы объявляем объект типа T на Аде 95 тогда, мы должны инициализировать компоненты (назначая их) индивидуально таким образом:
|
@ Мало того, что это является раздражающим, но ещё и способствует порождению разного рода ошибок. Если мы добавляем дальнейший компонент D к типу T тогда, мы могли бы забыть инициализировать его. Одно из преимуществ агрегатов состоит в том, что мы должны поставлять все компоненты, который автоматически гарантируют анализ полного обзора.
@ Эта проблема не возникала в Аде 83, потому что мы не могли сделать тип ограниченным, не делая его также частным и таким образом индивидуальные компоненты не были видимы так или иначе.
@ Ада 2005 преодолевает эту трудность, заявляя, что инициализация агрегатом не является фактически присваиванием даже при том, что изображается тем же самым символом. Это разрешает:
|
@ Мы должны думать об отдельных компонентах, как инициализируемых индивидуально на месте - фактическое значение не создаётся перед назначением.
@ Читатель мог бы напомнить, что та же самая вещь случается, когда агрегат используется, чтобы инициализировать controlled тип; этого не было в первоначальном определении ada, но было в исправлении AI-83 в 2001 Опечатке [2].
@ Мы теперь можем объявить константу ограниченного типа как ожидается:
|
@ Ограниченные агрегаты могут использоваться во многих других контекстах также? как заданное по умолчанию выражение в составляющем объявлении, так, если мы вкладываем тип T в некоторый другой тип (который тогда всегда ограничивается - это могло быть явно ограничено, но есть общее правило, что тип неявно ограничен, если у этого есть хотя бы один ограниченный компонент), мы могли бы иметь:
|
@ как выражение в рекордном агрегате, так новое использование типа Twrapper в:
|
@ как выражение в составном регулярном значении точно так же, таким образом мы могли бы иметь:
|
@ как выражение для части предка агрегата расширения, так, если TT был отмечен как в:
|
@ как выражение в инициализированной программе распределения, таким образом мы могли бы иметь:
|
@ как фактический параметр для параметра подпрограммы ограниченного типа режима в:
|
@ так же как заданное по умолчанию выражение для параметра:
|
@ так же как результат оператора return:
|
@ это действительно касается другого главного изменения к ограниченным типам, к которым мы возвратимся через мгновение.? как фактический параметр для универсального формального ограниченного объектного параметра режима в:
|
@ Последний пример интересен. Ограниченные generic параметры не были разрешены в Аде 95 вообще, потому что не было никакого способа передать фактический параметр, потому что механизм generic параметра, как полагают, является присваиванием. Но теперь фактический параметр можно передать как агрегат. Агрегат может также использоваться как значение по умолчанию для параметра таким образом:
|
@ Напомним, что есть различие между подпрограммными и generic параметрами. Параметрам подпрограммы всегда позволяли быть ограниченных типов, так как они главным образом осуществляются ссылкой, и никакое копирование не случается так или иначе. Единственное исключение относится к ограниченными частным типам, где полный тип является элементарным типом.
@ Изменение в Аде 2005 состоит в том, что агрегат может использоваться как фактический параметр в случае параметра подпрограммы в режиме в котором это было не возможно в Аде 95.
@ Иногда у ограниченного типа есть компоненты, где начальное значение не может быть дано, как в защищенном типе Semaphore:
|
@ Так жек как защищенный тип неотъемлемо ограничен, тип PT также ограничен, потому что тип с ограниченным компонентом становится тоже ограниченным. Хотя мы не можем дать явное начальное значение для Semaphore, мы все еще хотели бы использовать агрегат, чтобы получить проверку охвата. В таких случаях мы можем использовать символ блока <> как описано в предыдущей секции, чтобы означать использование значение по умолчанию для типа (если любой). Таким образом мы можем написать:
|
@ Главное правило, которому нужно всегда повиноваться состоит в том, что значения ограниченных типов никогда не могут копироваться.
@ Рассмотрим вложенные ограниченные типы:
|
@ Если мы объявим объект типа Inner:
|
@ тогда мы не cможем использовать An_Inner в агрегате типа Outer:
|
@ Это незаконно, потому что мы копировали бы значение. Но мы можем использовать вложенный агрегат как упоминалось ранее:
|
@ Другое главное изменение к проблемам ограниченных типов - возврат значения из функций.
@ Мы видели, что способность инициализировать объект ограниченного типа с агрегатом решает проблему предоставления начального значения к ограниченному типу при условии, что тип не является частным.
@ Ада 2005 вводит новый подход к возвращению результата функций, которые могут использоваться, чтобы решить эту и другие проблемы.
@ Рассмотрим сначала следующий случай ограниченного типа:
|
@ Мы можем объявить функцию, которая возвращает значение типа T при условии, что возвращение не вызывает копирования. Например мы могли иметь:
|
@ Эта функция встраивает агрегат на месте в return выражении и поставляет его в точку вызова функции. Такую функцию можно вызывать в тех же вышеупомянутых местах, где агрегат может использоваться, чтобы встроить ограниченное значение на месте. Например:
|
@ Таким образом, сама функция встраивает значение в переменной V, создавая возвращенное значение. Следовательно адрес V передают функции как своего рода скрытый параметр.
@ Конечно, если T не является частным тогда, это достигает не больше, чем просто следующее:
|
@ Но функция Init может использоваться, даже если тип частный. Это что-то вроде функция конструктора для типа. Кроме того, функция Init может использоваться, чтобы сделать некоторое общее вычисление с параметрами прежде, чем поставить конечное значение, и это вносит значительную гибкость.
@ Мы отметили, что такую функцию можно вызвать во всех местах, где агрегат может использоваться, и это включает в return выражение подобной функции или даже непосредственно
|
@ Это может также использоваться в пределах агрегата. Предположим, что у нас есть функция которая возвращает значение ограниченного типа Inner следующим образом:
|
@ тогда мы не только можем использовать это, чтобы инициализировать объект типа Inner, но мы можем использовать это в объявлении объекта типа Inner таким образом:
|
@ В последнем случае адрес компонента An_Outer передаётся как скрытый параметр для функции Make_Inner.
@ Иметь возможность использовать функцию таким образом обеспечивает большую гибкость, но порой требуется ещё больше гибкости. Новый синтаксис разрешает конечному возвращенному объекту быть объявленным и затем управляться общим способом перед заключительным возвращением из функции.
@ Основная конструкция следующая:
|
@ Основная идея состоит в том, что объект R объявлен и может тогда управляться произвольным способом прежде, чем будет наконец возвращен. Отметим, что использование зарезервированного слова do чтобы ввести операторы аналогичным способом как в операторе принятия. Последовательность завершается конструкцией end return и в этом месте функция возвращент управление туда откуда она была вызвана. Отметим что если функцию назвали в конструкции такой как инициализация объекта X ограниченного типа T следующим образом:
|
@ тогда переменная R в функции является фактически инициализируемой переменной X. Другими словами, адрес X передаётся как скрытый параметр функции Make, чтобы создать пространство для R.
@ Т.е. никакое копирование не выполняется.
@ У последовательности операторов может также быть обработчик исключения:
|
@ Если мы будем нуждаться в локаных переменных в пределах расширенного return оператора тогда, мы можем объявить внутренний блок обычным способом:
|
@ В объявлении R может быть назначено начальное значение:
|
@ Кроме того, очень как в операторе принятия do ... end return часть может быть опущена, таким образом мы просто можем написать:
|
@ или
|
@ который удобен, если мы только хотим возвратить объект с его заданным по умолчанию или явным начальным значением.
@ Заметьте, что расширенные return операторы не могут быть вложены, но могen иметь простые return операторы внутри:
|
@ Заметим, что у простых операторов возвращения в расширенном операторе возвращения нет выражения, т.к. результатом возвращения является объект R, объявленный в расширенном операторе возвращения непосредственно.
@ Хотя расширенные операторы возвращения не могут быть вложены но их может быть несколько в функции, возможно в переходах условного оператора или операторах выбора. Это было бы весьма вероятно в случае типа с дискриминантами:
|
@ Это также иллюстрирует важный момент, что хотя мы и вводили эти расширенные операторы возвращения в контексте большей гибкости для ограниченных типов, они могут использоваться с любыми другими типами во всем таком как неограниченный тип Person. Механизм прохождения скрытого параметра, который является адресом для возвращенного объекта конечно только, относится к ограниченным типам. В случае неограниченных типов результат просто поставляют обычным способом.
@ Мы можем также переименовать результат вызова функции - даже если он ограничен.
@ Тип результата функции может быть constrained или unconstrained как в случае типа Person, но фактический поставленный объект должен иметь определенный подтип. Например предположим, что мы имеем:
|
@ Тогда тип, UA является unconstrained, но подтип CA - constrained. Мы можем использовать их оба с расширенными операторами возвращения.
@ В случае constrained подтип в расширенном операторе возвращения должен статически соответствовать (типично, это будет то же самое дословно, но нуждаться не), таким образом:
|
@ В unconstrained случае результат R должен быть constrained или его подтипом или его начальным значением. Таким образом:
|
@ or
|
@ Другое важное изменение к результату функций, который обсуждался в предыдущей статье состоит в том, что тип результата может иметь анонимный ссылочный тип. Таким образом мы можем написать функцию, такую как функция Mate_Of (A: access Animal'Class) return access Animal'Class; Введение явных ссылочных типов для результата означает, что Ада 2005 в состоянии обойтись без понятия возвращения ссылкой.
@ Это тем не менее, вводит значимую несовместимость между Адой 95 и Адой 2005. У нас мог бы например быть пул подчиненных задач, действующих как серверы. Индивидуальные подчиненные задачи могли бы быть заняты или свободны (idle). У нас могла бы быть задача менеджера, которая распределяет подчиненные задачи различным заданиям. Менеджер мог бы объявить задачи как массив:
|
@ и затем имеется другой массив свойств задач такой как:
|
@ Пусть нам необходима функция для поиска свободной задачи. На Аде 95 мы бы написали:
|
@ Это не допустимо на Аде 2005. Если тип результата ограничен (как в этом случае) тогда, выражение в операторе возвращения должно быть составным или функциональным запросом и не объектом, таким как Slaves (K).
@ На Аде 2005 функция должна быть переписана, чтобы честно возвратить ссылочное значение, обращающееся к типу задачи вместо того, чтобы вызвать таинственное понятие возвращения ссылкой.
@ Таким образом мы должны будем написать:
|
@ и все вызовы Get_Slave должны быть изменены, чтобы переписываться также.
@ Это - возможно самая серьезная несовместимость между Адой 95 и Адой 2005. Но тогда, в конце дня, честность - лучшая политика.
2010-10-24 00:26:54
. .