Rationale for Ada 2005: Access types
RUSTOPBACKNEXT
ENG |
4. Downward closures
@ This section is really about access to subprogram types in general but the title downward closures has come to epitomize the topic. @ The requirements for Ada 83, (Strawman .. Steelman) were strangely silent about whether parameters of subprograms could themselves be subprograms as was the case in Algol 60 and Pascal. Remember that Pascal was one of the languages on which the designs for the DoD language were to be based. @ The predictability aspects of the requirements were interpreted as implying that all subprogram calls should be identified at compilation time on the grounds that if you didn't know what was being called than you couldn't know what the program was going to do. This was a particularly stupid attitude to take. The question of predictability (presumably in some safety or security context) really concerns the behaviour of particular programs rather than the universe of all programs that can be constructed in a language. @ In any event the totality of subprograms that might be called in a program is finite and closed. It simply consists of the subprograms in the program. Languages such as Ada are not able to construct totally new subprograms out of lesser components in the way that they can create say floating point values. @ So the world had to use generics for many applications that were natural for subprograms as parameters of other subprograms. Thankfully many implementers avoided the explosion that might occur with generics by clever code sharing which in a sense hid the parameterization behind the scenes. @ The types of applications for which subprograms are natural as parameters are any where one subroutine is parameterized by another. They include many mathematical applications such as integration and maximization and more logical applications such as sorting and searching and iterating. @ As outlined in the Introduction, the matter was partly improved in Ada 95 by the introduction of named access-to-subprogram types. This was essentially done to allow program call back to be implemented. @ Program call back is when one program passes the "address" of a subprogram within it to another program so that this other program can later respond by calling back to the first program using the subprogram address supplied. This is often used for communication between an Ada application program and some other software such as an operating system which might even be written in another language such as C. @ Named access to subprogram types certainly work for call back (especially with languages such as C that do not have nested subprograms) but the accessibility rules which followed those for general access to object types were restrictive. For example, suppose we have a general library level function for integration using a named access to subprogram type to pass the function to be integrated thus
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale for Ada 2005: Access types
@ENGRUSTOPBACKNEXT4. Нисходящие замкнутые выражения
@ Этот раздел - на самом деле о ссылках к подпрограммным типам, а заголовок, "нисходящие замкнутые выражения" вынесен для того чтобы обозначить проблему.
@ Возможности Ады 83 по передаче подпрограмм в качестве параметров других подпрограмм были на удивление слабыми по сравнению с такими языками как Алгол 60 и Паскаль. Напомним, что Паскаль был одним из основных базовых языков Министерсва Обороны.
@ Аспекты предсказуемости требовали, чтобы все вызовы подпрограмм были идентифицированы во время компиляции на том основании, что если Вы не знаете что вызываете, тем более, Вы не можете знать, что программа собирается делать. Это было особенно глупым требованием. Вопрос предсказуемости (в контексте безопасности) действительно касается поведения специфических программ, а не области всех программ, которые могут быть созданы на языке.
@ В любом случае, множество подпрограмм, которое может быть вызвано в программе является конечным и замкнутым. Оно просто состоит из подпрограмм в программе. Языки типа Ады не в состоянии создать полностью новые подпрограммы из меньших компонентов в процессе выполнения, которым они могут создать, ??? говорят с плавающей запятой значения ???.
@ Таким образом, мир должен был использовать generics-средства для приложений в которых были подпрограммы как параметры других подпрограмм. К счастью, большинство разработчиков избегали проблем, которые могли бы произойти с generics-срествами умным совместным использованием кода, которое в некотором смысле негласно скрывало параметризацию.
@ Приложения в которых подпрограммы используются как параметры - это где одна подпрограмма параметризуется другой. Это используется во многих математических приложениях, типа интеграции и максимизации и приложений типа сортировки и поиска и выполнения итераций.
@ Как было уже сказано во Введении, вопрос был частично улучшен в Аде 95 введением именованных ссылочных типов к подпрограмме. Это было сделано в основном, для реализации механизма обратного вызова (callback).
@ Обратный вызов - это когда одна программа передает "адрес" подпрограммы другой программе так, чтобы эта другая программа могла позже вызвать обратно первую программу, используя полученный адрес. Это часто используется для коммуникации между прикладной программой Ады и некоторым другим программным обеспечением, типа операционной системы, которая могла быть написана на другом языке, например на C.
@ Именованная ссылка на подпрограмму конечно работала для обратного вызова (особенно с языками типа C, которые не имеют вложенных подпрограмм), но правила доступа к объектам были весьма ограниченными. Например, пусть мы имеем функцию библиотечного уровня для интеграции, которая использует именованную ссылку к типу подпрограммы для вычисления функции, которая будет интегрироваться:
|
@ Но мы не можем сделать даже самой простой интеграции нашей собственной функции естественным способом. Например, предположим, что мы желаем интегрировать функцию Exp (X ** 2):
|
@ Но это незаконно из-за проверки области видимости, которая предназначена для того чтобы препятствовать использованию например такого кода:
|
@ Здесь мы попытались назначить ссылку к локальной функции F глобальной переменной Evil. Если бы такое назначение разрешалось, тогда запрос Evil косвенно вызывал бы функцию F в контексте в котором её объявление уже не существовало и функция F тогда попыталась бы назначить переменную Y, которой больше не существовало и чье пространство памяти могло бы теперь использоваться для чего - то еще.
@ Мы можем подвести итог этого говоря, что мы пытаемся вызвать F когда его уже не существует.
@ Ада 2005 преодолевает эту проблему, вводя анонимные ссылки на типы подпрограмм. Эта возможность рассматривалась ещё при разработке дизайна ada, но в это время это не было сделано по двум основным причинам. Во-первых, при реализации необходимость ведения display-векторов, а не статических ссылок посчитали препятствием. И во-вторых, соображения лёгкой поддержки недавно введенных теговых типов. Конечно можно было продолжить использовать generics. Но при дальнейшем анализе оказалось что предполагаемые трудности оказались не так уж велики. Кроме того, чрезмерное использование generics, когда другие языки сорок лет назад включили более естественный механизм, было утомительно. Так в конце концов Ада 2005 включает анонимные ссылки на типы подпрограмм.
@ Мы перезаписываем функцию интеграции следующим образом:
|
@ Обратите внимание на спецификацию функции Integrate в которой параметр Fn имеет анонимную ссылку на тип подпрограммы. Внутри процедуры реализован метод трапеций, там же через параметрическую ссылку Fn вызывается интегрируемая функция. Таким объявлением в Integrate преодолены проблемы видимости, и нам разрешено написать Integrate (F'Access...) так же, как мы могли написать P (X'Access) в примере предыдущего раздела, где мы обсуждали анонимные ссылки на объектовые типы.
@ Рассмотрим преобразование типов при присваивании глобальной переменной. Следующий текст иллюстрирует и ссылку на объектовый и ссылку на подпрограммный типы в качестве параметров.
|
@ Это повторяет часть структуры предыдущего раздела. Процедура P имеет в качестве параметров ссылку на объект Objptr и ссылку на подпрограмму Procptr; они - оба анонимного типа.
@ Вызов P в локальном блоке передает адреса локального объекта An_Obj и локальной процедуры A_Proc. Здесь всё корректно. Теперь попытаемся внутри процедуры P присвоить значения параметра глобальным объектам Evil_Obj и Evil_Proc с намерением косвенного присваивания через Evil_Obj и косвенного вызова через Evil_Proc после того, как объект и процедура, упомянутая больше не существуют.
@ Оба эти неправильных действия будут заблокированы как противоречащие правилам видимости.
@ В случае параметра Objptr его уровень видимости соответствует видимости локальной переменной An_Obj, и таким образом, преобразование AOT будет заблокировано во время выполнения возбуждением исключения Program_Error. Но если бы An_Obj был объявлен на том же самом уровне как и AOT, а не в пределах внутреннего блока, тогда такое преобразование было бы корректно.
@ Однако, несколько иные правила относительно анонимных ссылок на подпрограммный тип в качестве параметра. Они не несут индикацию относительно уровня видимости фактического параметра, и они обрабатывается как будто область видимости бесконечна (строго - глубже чем что - нибудь еще). Это правило предотвращает преобразование в APT тип, которое будет обнаружено во время компиляции. Но заметим, что если бы процедура A_Proc была объявлена на том же самом уровне как APT тогда, преобразование всё равно было бы незаконным, потому что уровень видимости продолжал бы рассматривается как бесконечный.
@ Есть множество причин для различной обработки анонимных ссылок к подпрограммным типам.
@ Имеется много проблем с именованными ссылочными типами на подпрограммы, такими как *func в языке С в почти всех компиляторах. Разрешение преобразования от анонимного ссылочного типа подпрограммы к именованному таким образом вызвало бы проблемы, потому что эта модель не работает специально для ??? базируемого выполнения дисплея ???. Нести уровень видимости не предотвратило бы эти преобразования. Ключевая цель состояла в том, чтобы просто обеспечить средство, соответствующее этому на Паскале а не поощрять чрезмерные вольности с ссылочными типами на подпрограммы. Напомним, что признак Unchecked_Access разрешается для ссылочных типов на объекты, но это считается слишком опасным для ссылочных типов на подпрограммы по целому ряду причин.
@ Есть альтернативные пути решения проблем видимости так или иначе. Так двойная интеграция, представленная во Введении может быть легко обойдена во многих случаях. Вычислим двумерный интеграл на интервале от 0 до 1 по x и y при помощи следующей программы:
|
@ Сущность проблемы была в том, что F должен был быть объявлен в G, потому что необходимо обратиться к параметру X из G. Но проницательный читатель отметит, что этот пример не очень убедителен, потому что интегралы могут быть отделены и функции могут быть объявлены на библиотечном уровне следующим образом:
|
@ и это работает в Аде 95.
@ Однако, если эти два интеграла были бы более замысловатыми, или область была бы не квадратной, а треугольной так, чтобы граница внутреннего интеграла зависела от внешней переменной как в (1 (x¦ ¦ xy дуплекс дю), 0) 0 тогда вложение функций было бы жизненно важно.
@ Мы теперь рассмотрим более изящный пример, который иллюстрирует, как мы могли бы проинтегрировать произвольную функцию двух переменных F (x, y) по прямоугольной области.
@ Предположим, что мы имеем функцию Integrate для одного измерения как раньше:
|
@ Рассмотрим:
|
@ Новая функция Integrate для двух перегрузок измерений и использований функция Integrate для одного измерения (хороший пример перегрузки). С этой общностью снова невозможно упорядочить структуру способом, который является законным в Аде 95.
@ Мы могли бы использовать двумерную подпрограмму интеграции, чтобы решить первоначальную тривиальную проблему следующим образом:
|
@ Как упражнение читатель мог бы любить перезаписать дву-мерную функцию, чтобы обрабатывать не - прямоугольную область. Трюк состоит в том чтобы передать границы внутреннего интеграла также как функцию. Конфигурация тогда становится:
|
@ В случае, если читатель думает, что эта тема является слишком уж математической, заметим, что анонимный доступ к параметрам подпрограммы широко используется в новой контейнерной библиотеке, избегая таким образом ненужного использование generics.
@ Например, пакет Ada.Containers.Vectors объявляет процедуру:
|
@ Здесь обновляется элемент векторного Контейнера, индекс которого - Index вызовом процедуры Process с тем элементом как параметр. Таким образом, если мы имеем вектор целых чисел V, и мы должны удвоить значение тех с индексом в диапазоне 5 - 10, тогда мы сначала объявили бы процедуру:
|
@ и тогда мы имеем право написать:
|
@ Следующие детали использования ссылочных типов на подпрограммы с контейнерами будут рассматриваться в более поздней публикации.
@ Наконец должно быть отмечено, что анонимные ссылочные типы на подпрограммы могут также использоваться во всех тех местах, где разрешаются анонимные ссылочные типы на объекты. Они как автономные объекты, как компоненты массивов и записей, как функциональные результаты, в переименованиях, и в ссылочных дискриминантах.
@ Читатель, который любит длинные последовательности зарезервированных слов, должен понять к настоящему времени, что нет никаких ограничений в Аде 2005 по этому поводу. Например, функция без параметров может возвратить ссылку на функцию как результат, а та, в свою очередь, может быть подобного вида. Таким образом, мы будем иметь нечто вроде: type FF is access function return access function...
@ Попытки компилировать такие ссылочные типы на функции неизбежно приведут к безумию.
2010-10-24 00:26:54
. .