Rationale for Ada 2005: Object oriented model

RUSTOP
BACKNEXT

ENG

3. The prefixed notation

@ As mentioned in the Introduction, the Ada 95 object oriented model has been criticized for not being really OO since the notation for applying a subprogram (method) to an object emphasizes the subprogram and not the object. Thus given

  1        package P is
  2                type T is tagged ... ;
  3                procedure Op (X : T; ... );
  4                ...
  5        end P;

@ then we usually have to write

  1        P.Op (Y, ... ); -- subprogram first

@ in order to apply the operation to an object Y of type T whereas an OO person would expect to write

  1        Y.Op ( ... ); -- object first

@ Some hard line OO languages such as Smalltalk take the view that everything is an object and that all activities are operations upon some object. Thus adding 2 and 3 can be seen as sending a message to 2 instructing 3 to be added to it. This is clearly an extreme view.

@ Older languages take the view that subprograms are dominant and that they act upon parameters which might be raw numbers such as 2 or denote objects such as a circle. Ada 95 primarily takes this view which reflects its Pascal foundation over 20 years ago. Thus if Area is a function which returns the area of a circle then we write

  1        A := Area (A_Circle);

@ However, when we come to tasks and protected objects Ada takes the OO view in which the identity of the object comes first. Thus given a task Actor with an entry Start we call the entry by writing

  1        Actor.Start ( ... );

@ So Ada 95 already uses the object notation although it only applies to concurrent objects such as tasks. Other objects and, in particular, objects of tagged types have to use the subprogram notation.

@ A major irritation of the subprogram notation is that it is usually necessary to name the package containing the declaration of the subprogram thus

  1        P.Op (Y, ... ); -- package P mentioned

@ There are two situations when P need not be mentioned – one is where the procedure call is actually inside the package P, the other is where we have a use clause for P (and even that sometimes does not give the required visibility). But these are special cases.

@ In Ada 2005 we can replace P.Op(Y, ... ); by the so-called prefixed notation

  1        Y.Op ( ... ); -- package P never mentioned

@ provided that

@ The reason there is never any need to mention the package is that, by starting from the object, we can identify its type and thus the primitive operations of the type. Note that a class wide operation can be called in this way only if it is declared at the same place as the primitive operations of T (or one of its ancestors).

@ There are many advantages of the prefixed notation as we shall see but perhaps the most important is ease of maintenance from not having to mention the package containing the declaration of the operation. Having to name the package is often tricky because in complicated situations involving several levels of inheritance it may not be obvious where the operation is declared. This happens especially when operations are declared implicitly and when class-wide operations are involved. Moreover if we change the structure for some reason then operations might move.

@ As a simple example consider a hierarchy of plane geometrical object types. All objects have a position given by the two coordinates x and y (this is the position of the centre of gravity of the object). There will be other specific properties according to the type such as the radius of a circle. In addition there might be general properties such as the area of the object, its distance from the origin and moment of inertia about it centre.

@ There are a number of ways in which such a hierarchy might be structured. We might have a package declaring a root abstract type and then another package with several derived types.

  1        package Root is
  2                type Object
  3                        is abstract tagged record
  4                                X_Coord : Float;
  5                                Y_Coord : Float;
  6                        end record;
  7                function Area (O : Object) return Float is abstract;
  8                function MI (O : Object) return Float is abstract;
  9                function Distance (O : Object) return Float;
 10        end Root;
 11
 12        package body Root is
 13                function Distance (O : Object) return Float is
 14                begin
 15                        return Sqrt (O.X_Coord**2 + O.Y_Coord**2);
 16                end Distance;
 17        end Root;

@ This package declares the root type and two abstract operations Area and MI (moment of inertia) and a concrete operation Distance. We might then have

  1        with Root;
  2        package Shapes is
  3                type Circle is new Root.Object
  4                        with record
  5                                Radius : Float;
  6                        end record;
  7                function Area (C : Circle) return Float;
  8                function MI (C : Circle) return Float;
  9
 10                type Triangle is new Root.Object
 11                        with record
 12                                A, B, C : Float; -- lengths of sides
 13                        end record;
 14                function Area (T : Triangle) return Float;
 15                function MI (T : Triangle) return Float;
 16
 17                -- and so on for other types such as Square ...
 18        end Shapes;

@ (In the following discussion we will assume that use clauses are not being used. This is quite realistic because many projects forbid use clauses.) Having declared some objects such as A_Circle and A_Triangle we can then apply the operations Area, Distance, and MI. In Ada 95 we write

  1        A := Shapes.Area (A_Circle);
  2        D := Shapes.Distance (A_Triangle);
  3        M := Shapes.MI (A_Square);

@ Observe that the operation Distance is inherited and so is implicitly declared in the package Shapes for all types even though there is no mention of it in the text of the package Shapes. However, if we were using Ada 2005 and the prefixed notation then we could simply write

  1        A := A_Circle.Area;
  2        D := A_Triangle.Distance;
  3        M := A_Square.MI;

@ and there is no mention of the package Shapes at all.

@ A clever friend then points out that by its nature Distance is the same for all types so it would be safer to avoid the risk of it getting changed by making it class wide. So we change the declaration of Distance in the package Root thus

  1        function Distance (O : Object'Class) return Float;

@ and recompile our program. But the Ada 95 version won't recompile. Why? Because class wide operations are not inherited. So there is only one function Distance and it is declared in the package Root. So all our calls of Distance have to be changed to

  1        D := Root.Distance (A_Triangle);

@ However, if we had been using the prefixed notation then there would have been nothing to change.

@ Our manager might then read about the virtues of child packages and tell us to restructure the whole thing as follows

  1        package Geometry is
  2                type Object is abstract ...
  3                ... -- functions Area, MI, Distance
  4        end Geometry;
  5
  6        package Geometry.Circles is
  7                type Circle is new Object with
  8                        record
  9                                Radius : Float;
 10                        end record;
 11                ... -- functions Area, MI
 12        end Geometry.Circles;
 13
 14        package Geometry.Triangles is
 15                type Triangle is new Object with
 16                        record
 17                                A, B, C : Float;
 18                        end record;
 19                ... -- functions Area, MI
 20        end Geometry.Triangles;
 21        -- and so on

@ This is of course a much more beautiful structure and avoids having to write Root.Object when doing the extensions. But, horrors, our assignments in Ada 95 now have to be changed to

  1        A := Geometry.Circles.Area (A_Circle);
  2        D := Geometry.Distance (A_Triangle);
  3        M := Geometry.Squares.MI (A_Square);

@ But the lucky programmer using Ada 2005 can still write

  1        A := A_Circle.Area;
  2        D := A_Triangle.Distance;
  3        M := A_Square.MI;

@ and have a refreshing coffee (or a relaxing martini) while we are toiling with the editor.

@ Some time later the program might be extended to accommodate triangles that are specialized to be equilateral. This might be done by

  1        package Geometry.Triangles.Equilateral is
  2                type Equilateral_Triangle is new Triangle with private;
  3                ...
  4        private
  5                ...
  6        end;

@ This type of course inherits all the operations of the type Triangle. We might now realize that the object A_Triangle of type Triangle was equilateral anyway and so it would be better to change it to be of type Equilateral_Triangle. The lucky Ada 2005 programmer will only have to change the declaration of the object but the poor Ada 95 programmer will have to change the calls on all its primitive operations such as

  1        A := Geometry.Triangles.Area (A_Triangle);

@ to the corresponding

  1        A := Geometry.Triangles.Equilateral.Area (A_Triangle);

@ Other advantages of the prefixed notation were mentioned in the Introduction. One is that it unifies the notation for calling a function with a single parameter and directly reading a component of the object. Thus we can write uniformly

  1        X := A_Circle.X_Coord;
  2        A := A_Circle.Area;

@ Of course if we were foolish and had a visible component Area as well as a function Area then we could not call the function in this way.

@ But now suppose we decide to make the root type private so that the coordinates cannot be changed inadvertently. Moreover we decide to provide functions to read them. So we have

  1        package Geometry is
  2                type Object is abstract tagged private;
  3                function Area (O : Object) return Float is abstract;
  4                function MI (O : Object) return Float is abstract;
  5                function Distance (O : Object'Class) return Float;
  6                function X_Coord (O : Object'Class) return Float;
  7                function Y_Coord (O : Object'Class) return Float;
  8        private
  9                type Object is tagged
 10                        record
 11                                X_Coord : Float;
 12                                Y_Coord : Float;
 13                        end record;
 14        end Geometry;

@ Using Ada 95 we would now have to change statements such as

  1        X := A_Triangle.X_Coord;
  2        Y := A_Triangle.Y_Coord;

@ into

  1        X := Geometry.X_Coord (A_Triangle);
  2        Y := Geometry.Y_Coord (A_Triangle);

@ or (if we had not been wise enough to make the functions class wide) perhaps even

  1        X := Geometry.Triangles.Equilateral.X_Coord (A_Triangle);
  2        Y := Geometry.Triangles.Equilateral.Y_Coord (A_Triangle);

@ whereas in Ada 2005 we do not have to make any changes at all.

@ Another advantage mentioned in the Introduction is that when using access types explicit dereferencing is not necessary. Suppose we have

  1        type Pointer is access all Geometry.Object'Class;
  2        ...
  3        This_One : Pointer := A_Circle'Access;

@ In Ada 95 (assuming that X_Coord is a visible component) we have to write

  1        Put (This_One.X_Coord); ...
  2        Put (This_One.Y_Coord); ...
  3        Put (Geometry.Area (This_One.all));

@ whereas in Ada 2005 we can uniformly write

  1        Put (This_One.X_Coord); ...
  2        Put (This_One.Y_Coord); ...
  3        Put (This_One.Area);

@ and of course this remains unchanged if we make the coordinates into functions whereas the Ada 95 statements will need to be changed.

@ There are other structural changes that can occur during program development which are much easier to cope with using the prefix notation. For example, a class wide operation might be moved. And in the case of multiple interfaces to be described in the next section an operation might be moved from one interface to another.

@ It is clear that the prefixed notation has significant benefits both in terms of program clarity and for program maintenance.

@ Other variations on the rules for the use of the notation were considered. One was that the mechanism should apply to untagged types as well but this was rejected on the grounds that it might add to rather than reduce confusion in some cases. In any event, untagged types do not have class wide types so they are intrinsically simpler.

@ It is of course important to note that the first parameter of an operation plays a special role since in order to take advantage of the prefixed notation we have to ensure that the first parameter is a controlling parameter. Treating the first parameter specially can appear odd in some circumstances such as when there is symmetry among the parameters. Thus suppose we have a set package for creating and manipulating sets of integers

  1        package Sets is
  2                type Set is tagged private;
  3                function Empty return Set;
  4                function Unit (N : Integer) return Set;
  5                function Union (S, T : Set) return Set;
  6                function Intersection (S, T : Set) return Set;
  7                function Size (S : Set) return Integer;
  8                ...
  9        end Sets;

@ then we can apply the function Union in the traditional way

  1        A, B, C : Set;
  2        ...
  3        C := Sets.Union (A, B);

@ The object oriented addict can also write

  1        C := A.Union (B);

@ but this destroys the obvious symmetry and is rather like sending 3 to be added to 2 mentioned at the beginning of this discussion.

@ Hopefully the mature programmer will use the OO notation wisely. Maybe its existence will encourage a more uniform style in which the first parameter is always a controlling operand wherever possible. Of course it cannot be used for functions which are tag indeterminate such as

  1        function Empty return Set;
  2        function Unit (N : Integer) return Set;

@ since there are no controlling parameters. If a subprogram has just one parameter (which is controlling) such as Size then the call just becomes X.Size and no parentheses are necessary.

@ Note that the prefix does not have to be simply the name of an object such as X, it could be a function call so we might write

  1        N := Sets.Empty.Size;     -- N = 0
  2        M := Sets.Unit (99).Size; -- M = 1

@ with the obvious results as indicated.

Rationale for Ada 2005: Object oriented model

@ENGRUSTOPBACKNEXT

3. Префиксная нотация

@ Как было упомянуто во Введении, объектно-ориентированная модель ada критиковалась за то, что она не совсем соответсвовала концепции OOП, так как в ней при применении подпрограммы (метода) к объекту делался упор на подпрограмму, а не на объект. Рассмотрим пакет:

  1        package P is
  2                type T is tagged ... ;
  3                procedure Op (X : T; ... );
  4                ...
  5        end P;

@ обычный вызов фунции будет выглядеть так:

  1        P.Op (Y, ... ); -- subprogram first

@ в то время как ООП требует следующий порядок применения операции Op к объекту Y типа T:

  1        Y.Op ( ... ); -- object first

@ В некоторых языках жестко ориентированных на ООП, например, таком как Smalltalk, имеют дело исключительно только с объектами и любые действия связаны только с ними. Таким образом, сложение двух чисел 2 и 3 могут рассматриваться как посылка сообщения добавить объекту 2 'прибавиться' к объекту 3. Понятно, что это весьма циничный и экстремиский подход.

@ Старые языки были основаны на представлении, что подпрограммы являются доминирующими, и что они действуют на параметры, которые могут быть числами такими как 2 или обозначать объекты, такие как круг. Ада 95 получает представление, которое отражает её Паскальные корни более чем 20 летней давности. Таким образом, если Area - функция возвращающаят площадь круга, тогда мы имеем право написать:

  1        A := Area (A_Circle);

@ Однако, при рассмотрении задач и защищенных объектов Ada получает представление OOП в котором на первом месте объект. Таким образом, имея задачу Actor с точкой входа Start мы вызываем вход Start следующим образом:

  1        Actor.Start ( ... );

@ Ада 95 уже имеет объектную нотацию, хотя это относится только к параллельным объектам, таким как задачи. Другие объекты и, в частности, объекты теговых типов должны были использовать традиционную подпрограммную нотацию.

@ Главное неудобство подпрограммной нотации состоит в том, что при необходимости вызвать фунцию содержащуюся в неком пакете необходимо указывать сам пакет:

  1        P.Op (Y, ... ); -- package P mentioned

@ Есть две ситуации, когда пакет P может не упоминаться: во-первых - когда вызов процедуры находится непосредственно в пакете P, во-вторых - когда у нас определен спецификатор использования (use) для пакета P (и даже в этом случае это не всегда дает необходимую видимость). Но это особые случаи.

@ В Аде 2005 мы можем заменить вызов P.Op (Y...), так называемой, префиксной нотацией:

  1        Y.Op ( ... ); -- package P never mentioned

@ при условии, что

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

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

@ В качестве примера рассмотрим иерархию геометрических объектов на плоскости. Всем объектам дадим координаты x и y (это - позиция центра тяжести объекта). Зададим атрибуты специфические для каждого типа объектов (например, радиус для круга). Кроме того, у объектов могут быть и общие свойства, такие как площадь, расстояния от начала координат и момент инерции.

@ Есть много способов структурировать такую иерархию. У нас мог бы быть пакет, объявленный как корневой абстрактный тип, и пакет с несколькими производными типами.

  1        package Root is
  2                type Object
  3                        is abstract tagged record
  4                                X_Coord : Float;
  5                                Y_Coord : Float;
  6                        end record;
  7                function Area (O : Object) return Float is abstract;
  8                function MI (O : Object) return Float is abstract;
  9                function Distance (O : Object) return Float;
 10        end Root;
 11
 12        package body Root is
 13                function Distance (O : Object) return Float is
 14                begin
 15                        return Sqrt (O.X_Coord**2 + O.Y_Coord**2);
 16                end Distance;
 17        end Root;

@ Этот пакет определяет корневой тип с двумя абстрактными операциями Area и MI (момент инерции) и конкретную операцию Distance. Тогда мы можем написать:

  1        with Root;
  2        package Shapes is
  3                type Circle is new Root.Object
  4                        with record
  5                                Radius : Float;
  6                        end record;
  7                function Area (C : Circle) return Float;
  8                function MI (C : Circle) return Float;
  9
 10                type Triangle is new Root.Object
 11                        with record
 12                                A, B, C : Float; -- lengths of sides
 13                        end record;
 14                function Area (T : Triangle) return Float;
 15                function MI (T : Triangle) return Float;
 16
 17                -- and so on for other types such as Square ...
 18        end Shapes;

@ (В дальнейшем обсуждении мы предположим, что спецификаторы использования (use) не определены. Это весьма реалистичо, потому что во многих проектах запрещают спецификаторы использования). Объявив некоторые объекты, такие как A_Circle и A_Triangle мы можем применить операции Area, Distance, и MI. На Аде 95 мы должны были написать:

  1        A := Shapes.Area (A_Circle);
  2        D := Shapes.Distance (A_Triangle);
  3        M := Shapes.MI (A_Square);

@ Заметим, что операция Distance унаследована и неявно объявлена в пакете Shapes для всех типов даже при том, что нет никакого упоминания о ней в тексте пакета Shapes. Однако, если бы мы использовали Аду 2005 и префиксную нотацию, тогда мы могли бы написать:

  1        A := A_Circle.Area;
  2        D := A_Triangle.Distance;
  3        M := A_Square.MI;

@ где нет никакого упоминания о пакете Shapes вообще.

@ Заметим, что метод Distance - один и тот же для всех типов, и было бы более безопасно избежать риска его изменения, сделав его надкласового типа. Таким образом, мы изменяем объявление Distance в пакете Root следующим образом:

  1        function Distance (O : Object'Class) return Float;

@ и перекомпилируем нашу программу. Но на Аде 95 мы потерпим неудачу. Почему? Потому что надклассовые операции не наследуются. Таким образом, есть только одна функция Distance, и она объявлена в пакете Shape. Таким образом, все наши вызовы Distance должны быть изменены на:

  1        D := Root.Distance (A_Triangle);

@ Однако, если бы мы использовали префиксную нотацию тогда нам ничего не надо было менять.

@ Наш босс, начитавшись о достоинствах дочерних пакетов, потребовал бы от нас переделать всё следующим образом:

  1        package Geometry is
  2                type Object is abstract ...
  3                ... -- functions Area, MI, Distance
  4        end Geometry;
  5
  6        package Geometry.Circles is
  7                type Circle is new Object with
  8                        record
  9                                Radius : Float;
 10                        end record;
 11                ... -- functions Area, MI
 12        end Geometry.Circles;
 13
 14        package Geometry.Triangles is
 15                type Triangle is new Object with
 16                        record
 17                                A, B, C : Float;
 18                        end record;
 19                ... -- functions Area, MI
 20        end Geometry.Triangles;
 21        -- and so on

@ Это уже гораздо более красивая структура, избавляющая нас от необходимости писать Root.Object, делая расширения. Но наши ужасы на Аде 95 на этом не заканчиваются:

  1        A := Geometry.Circles.Area (A_Circle);
  2        D := Geometry.Distance (A_Triangle);
  3        M := Geometry.Squares.MI (A_Square);

@ В то время программист использующий Аду 2005 имеет возможность написать:

  1        A := A_Circle.Area;
  2        D := A_Triangle.Distance;
  3        M := A_Square.MI;

@ и уже пьет кофе (или расслабляется мартини), пока мы возимся с редактором.

@ Некоторе время спустя программа могла бы быть расширена, чтобы приспособить равносторонние треугольники. Это могло бы быть сделано так:

  1        package Geometry.Triangles.Equilateral is
  2                type Equilateral_Triangle is new Triangle with private;
  3                ...
  4        private
  5                ...
  6        end;

@ Тип Equilateral_Triangle наследует все операции типа Triangle. Мы могли бы пожелать чтобы объект A_Triangle типа Triangle был равносторонним треугольником и заменить его тип на Equilateral_Triangle. Счастливый программист на Аде 2005 должен будет только изменить объявление объекта, а бедный программисты на Аде 95 должен будет изменить запросы ко всем ее примитивным операциям.

  1        A := Geometry.Triangles.Area (A_Triangle);

@ переделать на:

  1        A := Geometry.Triangles.Equilateral.Area (A_Triangle);

@ Есть ещё одно преимущество префиксной нотации, упомянутое во Введении. Помимо вызова функции с единственным параметром, можно непосредственно читать компонент объекта. Таким образом, мы можем написать:

  1        X := A_Circle.X_Coord;
  2        A := A_Circle.Area;

@ Конечно, если бы мы были бы так глупы, что имели видимую переменную Area так же как функцию Area, тогда мы не могли бы вызвать функцию таким образом.

@ Но теперь предположим, что мы решаем сделать корневой тип частным так, чтобы координаты не могли быть изменены произвольно. Кроме того, мы решаем определить функции, чтобы читать их. Таким образом, мы имеем:

  1        package Geometry is
  2                type Object is abstract tagged private;
  3                function Area (O : Object) return Float is abstract;
  4                function MI (O : Object) return Float is abstract;
  5                function Distance (O : Object'Class) return Float;
  6                function X_Coord (O : Object'Class) return Float;
  7                function Y_Coord (O : Object'Class) return Float;
  8        private
  9                type Object is tagged
 10                        record
 11                                X_Coord : Float;
 12                                Y_Coord : Float;
 13                        end record;
 14        end Geometry;

@ Используя Аду 95, мы должны были бы изменить предыдущие утверждения:

  1        X := A_Triangle.X_Coord;
  2        Y := A_Triangle.Y_Coord;

@ на:

  1        X := Geometry.X_Coord (A_Triangle);
  2        Y := Geometry.Y_Coord (A_Triangle);

@ или (если мы не были достаточно мудры, чтобы сделать надклассовые функции), возможно даже

  1        X := Geometry.Triangles.Equilateral.X_Coord (A_Triangle);
  2        Y := Geometry.Triangles.Equilateral.Y_Coord (A_Triangle);

@ тогда как в Аде 2005 мы не должны делать никаких изменений вообще!

@ Другое преимущество, упомянутое во Введении состоит в том, что при использовании ссылочных типов явное разыменование не обязательно. Предположим, что мы имеем:

  1        type Pointer is access all Geometry.Object'Class;
  2        ...
  3        This_One : Pointer := A_Circle'Access;

@ В Аде 95 (предполагая, что X_Coord является видимым компонентом) мы должны написать:

  1        Put (This_One.X_Coord); ...
  2        Put (This_One.Y_Coord); ...
  3        Put (Geometry.Area (This_One.all));

@ В то время как на Аде 2005 мы напишем:

  1        Put (This_One.X_Coord); ...
  2        Put (This_One.Y_Coord); ...
  3        Put (This_One.Area);

@ и это не требует изменнений если мы превращаем координаты в функции, тогда как Ада 95 утверждения должны быть изменены.

@ Есть другие структурные изменения, которые могут произойти в течение развития программы, которые намного проще, чтобы справиться с использованием префиксной нотации. Например, надклассовая операция может быть перемещена. И в случае множественных интерфейсов, которые будут описаны в следующем разделе операция могла бы быть перемещена от одного интерфейса к другому.

@ Ясно, что префиксная нотация имеет существенные выгоды и в терминах ясности программы и для обслуживания программ.

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

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

  1        package Sets is
  2                type Set is tagged private;
  3                function Empty return Set;
  4                function Unit (N : Integer) return Set;
  5                function Union (S, T : Set) return Set;
  6                function Intersection (S, T : Set) return Set;
  7                function Size (S : Set) return Integer;
  8                ...
  9        end Sets;

@ тогда мы можем применить функцию Union традиционным способом:

  1        A, B, C : Set;
  2        ...
  3        C := Sets.Union (A, B);

@ Объектно ориентированный маньяк может написать:

  1        C := A.Union (B);

@ но это нарушает очевидную симметрию и скорее походит на посылку команды числу 3 прибавится к числу 2 упомянутое в начале этого обсуждения.

@ Мы надеемся, зрелый программист будет использовать OOП нотацию мудро. Возможно это поощрит более однородный стиль, в котором первый параметр всегда и везде является управляющим операндом, где это возможно. Конечно, это не может использоваться для функций, которые являются тэгом, неопределенного типа:

  1        function Empty return Set;
  2        function Unit (N : Integer) return Set;

@ где нет никаких управляющих параметров. Если подпрограмма имеет только один управляющий параметр, типа Size тогда, запрос становится X.Size, и никакие круглые скобки не нужны.

@ Отметим, что префикс может и не быть только именем объекта типа X, он может быть и вызовом функции:

  1        N := Sets.Empty.Size;     -- N = 0
  2        M := Sets.Unit (99).Size; -- M = 1

@ с очевидными результатами, как показано в комментариях.

@ ENG RUS

TOP BACK NEXT

2010-10-31 12:07:31

программа для такси скачать бесплатно . . .