[[Property:uuid|9BEE6EE3-5652-4C2E-BD2E-E6A50787508E]]
[[Property:weight|1]]
[[Property:title|Language-related facilities]]
A number of classes offer facilities which are very close to the language level. Here too the book ''[[Eiffel: The Language]]'' covers the classes in detail, so we can satisfy ourselves with a quick summary; the flat-short forms appear in part C.
==Basic types==
The basic types [[ref:libraries/base/reference/boolean_chart|BOOLEAN]] , [[ref:libraries/base/reference/character_8_chart|CHARACTER]] , [[ref:libraries/base/reference/integer_32_chart|INTEGER]] , [[ref:libraries/base/reference/real_32_chart|REAL]] and [[ref:libraries/base/reference/real_64_chart|DOUBLE]] are defined by classes of the Kernel library.
In reading the class specifications for the numeric types [[ref:libraries/base/reference/integer_32_chart|INTEGER]] , [[ref:libraries/base/reference/real_32_chart|REAL]] and [[ref:libraries/base/reference/real_64_chart|DOUBLE]] , you might think that the type declarations are too restrictive. For example the addition operation in class [[ref:libraries/base/reference/real_32_chart|REAL]] reads
infix "+" (other: REAL): REAL
but there is actually no problem here. A language convention applicable to all arithmetic expressions, the Balancing rule, states that in any such expression all operands are considered to be converted to the heaviest type, where [[ref:libraries/base/reference/real_64_chart|DOUBLE]] is heavier than [[ref:libraries/base/reference/real_32_chart|REAL]] and [[ref:libraries/base/reference/real_32_chart|REAL]] is heavier than [[ref:libraries/base/reference/integer_32_chart|INTEGER]] . So mixed-type arithmetic, consistent with common practice, is possible and indeed frequent.
==Arrays==
To create and manipulate one-dimensional arrays, use class [[ref:libraries/base/reference/array_chart|ARRAY]] of the Kernel Library. Arrays are not primitive language elements; instead, they are handled through class [[ref:libraries/base/reference/array_chart|ARRAY]] . This class is 'normal' in the sense that it may be used just as any other class by client and descendant classes. It is also somewhat special, however, in that the Eiffel compiler knows about it and uses this knowledge to generate efficient code for array operations.
To create an instance of [[ref:libraries/base/reference/array_chart|ARRAY]] , use the creation instruction
create my_array.make (1, u)
where the arguments indicate the lower and upper bounds. These bounds will then be accessible as my_array
.
lower and my_array
.
upper. The number of items is my_array
.
count; feature capacity is a synonym for count. The class invariant expresses the relation between count, lower and upper.
To access and change the item at index ''i'' in array a, you may use features item and put, as in
x := my_array.item (i)
my_array.put (new_value, i)
Function item has a bracket alias, so that you may also write the first assignment above more concisely as
x := my_array [i]
Features item and put have preconditions requiring the index ( i
in the above calls) to be within the bounds of the array. This means that you can detect bounds violations (which correspond to bugs in the client software) by using a version of class [[ref:libraries/base/reference/array_chart|ARRAY]] compiled with precondition checking on. The bounds of an array may be changed dynamically through procedure resize. Previously entered elements are retained. Rather than an explicit resize, you may use calls to procedure force which has the same signature as put but no precondition; if the index is not within the current bounds force will perform a resize as necessary.
==Optimizing array computations==
''' CAUTION''': Although [[ref:libraries/base/reference/array_chart|ARRAY]] benefits from an efficient implementation, its more advanced facilities such as resizing do not come for free. For extensive computations on large arrays, an optimization may be desirable, bypassing these facilities. The technique yields loops that run at about the same speed as the corresponding loops written in C or Fortran (the usual references for array computations). It is of interest for advanced uses only, so that you may safely skip this section on first reading unless your domain of application is numerical computation or some other area requiring high-performance array manipulations.
The optimization relies on the class SPECIAL, used internally by [[ref:libraries/base/reference/array_chart|ARRAY]] but of no direct interest to client developers in most common uses. With the declarations
my_array: ARRAY [SOME_TYPE]
direct_access: SPECIAL [SOME_TYPE]
you may use direct_access in lieu of 'my_array' within a critical loop, provided none of the operations may resize the array. Typically, the operations should only include put and item. In such a case you can use the following scheme:
direct_access:= my_array.area
-- The critical loop:
from
some_initialization
index := some_initial_index
until
index = some_final_index
loop
...
x := direct_access.item (index)
...
direct_access.put (some_value, index)
...
end
This replaces an original loop where the operations were on my_array
. Feature area of [[ref:libraries/base/reference/array_chart|ARRAY]] gives direct access to the special object, an instance of SPECIAL, containing the array values. Features put and item are available in SPECIAL as in [[ref:libraries/base/reference/array_chart|ARRAY]] , but without the preconditions; in other words, you will not get any bounds checking. Instances of SPECIAL are always indexed from zero, in contrast with arrays, whose lower bound is arbitrary, 1 being the most common value. But rather than performing index translations (that is to say, subtracting my_array
.
lower from index throughout the loop) it is preferable to use the following simple technique: if the lower bound 'lb' of my_array
is 1 or another small integer, use 0 as lower bound instead when creating my_array
, but only use the positions starting at 'lb'. You will waste a few memory positions (0 to lb-1), but will not have to change anything in your algorithm and will avoid costly subtractions.
It is important to note that this optimization, if at all necessary, should at most affect a few loops in a large system. You should always begin by writing your software using the normal [[ref:libraries/base/reference/array_chart|ARRAY]] facilities; then once you have the certainty that the software is correct, if you detect that a large array computation is hampering the efficiency of the system, you may apply the above technique to get the fastest performance out of that computation. The change to the software will be minimal - a few lines - and will be easy to undo if necessary.
==Tuples==
A new Kernel Library class is introduced: [[ref:libraries/base/reference/tuple_chart|TUPLE]] .
Alone among all classes, class TUPLE has a variable number of generic parameters. TUPLE, TUPLE [X], TUPLE [X, Y], TUPLE [X, Y, Z]
and so on are all valid types, assuming valid types X, Y, Z
and so on.
Conformance rules:
[CONF1]
For n >= 0
TUPLE [U1, U2, ..., Un, Un+1] conforms to
TUPLE [U1, U2, ..., Un]
(and hence to TUPLE [T1, T2, ..., Tn]
if each of the Ui conforms to each of the Ti for 1 <= i <= n
.)
In particular all tuple types conform to [[ref:libraries/base/reference/tuple_chart|TUPLE]] , with no parameter.
[CONF2]
For n >= 0 If *every* one of the types T1, T2, ..., Tn conforms
to a type T, then TUPLE [T1, T2, ..., Tn] conforms
to ARRAY [T]
{{Definition|Tuple Type|A "tuple type" is any type based on class [[ref:libraries/base/reference/tuple_chart|TUPLE]] , i.e. any type of the form TUPLE [T1, T2, ..., Tn]
for any n (including 0, for which there is no generic parameter). }}
{{note|CONF1 should be understood in terms of the underlying mathematical model.
Mathematically, TUPLE [T1, T2, ..., Tn]
is the set TUPLE n of all partial functions f from N+ (the set of non-negative integers) to T1 U T2 U ... Tn
, such that:
The domain of f contains the interval 1..n (in other words, f is defined for any i such that 1 <= i <= n).
For 1 <= i <= n, f (i) is a member of Ti. }}
With this definition, TUPLE n
is indeed a subset of TUPLE n+1
, and in particular TUPLE 0
, the empty set, is a subset of TUPLE n
for any n.)
Semantics: an instance of TUPLE [T1, T2, ..., Tn]
is a tuple whose first element is an instance of T1
, the second element being an instance of T2
etc. (The precise definition is the mathematical one given in note 1.) Note that there can be more than n elements to the tuple: for example a tuple with first element 5 and second element "FOO" is an instance of all of the following tuple types: TUPLE; TUPLE [INTEGER]; TUPLE [INTEGER, STRING]
.
It may seem restrictive at first to permit only one class, [[ref:libraries/base/reference/tuple_chart|TUPLE]] , to have an arbitrary number of actual generic parameters. Why not have a general mechanism for declaring any class C
so that all of C [X], C [X, Y]
etc. are valid? But in fact this is not really a restriction. To obtain this effect without any complicated language convention, just declare C
as
C [G -> TUPLE]
and then use the generic derivations
C [TUPLE [X]]
C [TUPLE [X, Y]]
and so on. This also makes it possible to have the effect of some fixed parameters and some variable ones, as in C [G, H, I -> TUPLE]
so we have all the necessary flexibility.)
==Tuple expressions==
Let e1, e2, ..., en be expressions of respective types T1, T2, ..., Tn. Then the expression [e1, e2, ..., en]
denotes an instance of TUPLE [T1, T2, ..., Tn]
, whose first element is e1, the second element being e2, etc.
Tuple expressions can be nested: whereas [1, 2, 3]
is a tuple with three elements (representing an instance of TUPLE [INTEGER, INTEGER, INTEGER]), [1, [2, 3]]
is a tuple with two elements, the second one itself a tuple; the overall expression represents an instance of TUPLE [INTEGER, TUPLE [INTEGER, INTEGER]]
.
As a special case of tuple expression syntax, the delimiters [
and ]
are replaced by parentheses for the tuple representing the actual argument list of a routine call (see section 4).
==Tuple features==
The exact specification of class [[ref:libraries/base/reference/tuple_chart|TUPLE]] will be described in an addition to ELKS. The principal features are:
* [[ref:libraries/base/reference/tuple_chart|count]] (number of significant elements)
* [[ref:libraries/base/reference/tuple_chart|item]] (i), with the obvious precondition: the i-th element, of type [[ref:libraries/base/reference/any_chart|ANY]] (since the value of i is not known at compile time); also first, second, third, fourth and fifth, of the appropriate types.
* [[ref:libraries/base/reference/tuple_chart|put]] (x, i), with the obvious precondition: replace i-th element with x. If argument x is not of the appropriate type T i
there is no effect.
* [[ref:libraries/base/reference/tuple_chart|is_equal]] : redefined to consider only the first n elements, where n is the smaller length.
Other features under consideration include:
* stripped (i): a tuple of type TUPLE [T1, T2, Ti-1, Ti+1, ..., Tn]
, derived from the current one by removing the i-th component, again with the obvious precondition.
* wedged (x, i): a tuple with one more element, inserted at position i.
* '''infix''' "+": tuple concatenation
* '''infix''' "++": element concatenation; t ++ x is the same thing as t.wedged (x, t.count + 1).
==What have we gained?==
First we have solved the only case in the Eiffel programming language in which an expression has no precisely defined type: polymorphic manifest arrays. We don't have manifest arrays any more, but manifest tuples, with a precisely defined type. No incompatibility is introduced thanks to rule CONF2. The original syntax for manifest arrays, Result := <>
, will continue to be supported.
Second, we can define functions that return multiple results. This is a quite significant increase in expressive power. No common language has that. (You have to go to Lisp and functional languages.) Just define TUPLE [...]
as the result type; in the function, you will write things like
Result := [e1, e2, ..., en]
Also, from a theoretical viewpoint, feature calls are simpler and more homogeneous: every feature takes exactly one tuple as argument and returns exactly one tuple as a result. (Either of these tuples may be empty: the first for a feature with no argument, the second for a procedure.) The syntax for a call becomes
Feature Arguments
with Arguments defined as
Tuple_expression
where the Tuple_expression uses the form given in section 2 but with the outermost [
and ]
delimiters replaced by parentheses to conform to usual practice. So the call
f (a, b, c)
which we continue to think of as having three arguments a, b and c, formally has only one tuple argument [a, b, c]
. This is of course not to be confused with a call of the form
g ([a, b, c])
which has one argument (a tuple with three elements) in both the ordinary and the formal sense.
==Active, iterators, numerical applications, introspection==
For a set of important applications of tuples see the book chapter on [[EiffelBase, Iteration|agents and iterators]] which also covers aspects of numerical software and related topics following from the tuple mechanism.
==Temporary limitations==
The implementation of tuples has the following limitations:
* Conformance of [[ref:libraries/base/reference/array_chart|ARRAY]] types to [[ref:libraries/base/reference/tuple_chart|TUPLE]] types is not yet fully supported.
* Class [[ref:libraries/base/reference/tuple_chart|TUPLE]] does not have features such as first and second. You must use item and, in most cases, an object test.
==Strings==
Strings are handled by class [[ref:libraries/base/reference/string_8_chart|STRING]] , similar in many respects to [[ref:libraries/base/reference/array_chart|ARRAY]] . Strings are of arbitrary size. The make creation procedure takes an integer argument, as in:
s, s1, s2, s3: STRING
...
create s.make (30)
The argument indicates the number of characters for the initial allocation. This is not an absolute limit: the string will automatically grow or shrink as a result of future operations. You may always request a resizing explicitly by calling procedure resize.
==String descriptor==
The object attached at run-time to an entity such declared of type [[ref:libraries/base/reference/string_8_chart|STRING]] is not the actual sequence of characters but a string descriptor, which contains a reference to the actual string contents.
As a result, four assignment or assignment-like operations are possible:
* '''A1''' s1 := s
* '''A2''' s2.share (s)
* '''A3''' s3 := s.twin
* '''A4''' s4.copy (s)
As illustrated below, ''' A1''' is a reference assignment: s1
will be attached to the same descriptor as s. ''' A2''' keeps the descriptors distinct, but make them refer to the same sequence of characters. In ''' A3''', s3
will be attached to a new string, completely distinct from the string attached to s1
although made of identical characters. ''' A4''' has almost the same effect as '''A3''', but is only applicable if s4
was not void, and will override the existing descriptor rather than creating a new one.
[[Image:strings]]
fig. 1: Effect of string assignment and copy operations
[[ref:libraries/base/reference/basic_routines_chart|BASIC_ROUTINES]] provides a number of conversion functions, such as charconv.