Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do literals of the form [...] have meaning that appears to depend upon context?

Consider the following program:

{$APPTYPE CONSOLE}

type
  TMyEnum = (enum1, enum2, enum3);

var
  Arr: TArray<TMyEnum>;
  Enum: TMyEnum;

begin
  Arr := [enum3, enum1]; // <-- this is an array
  for Enum in Arr do
    Writeln(ord(Enum));
  Writeln('---');

  for Enum in [enum3, enum1] do // <-- this looks very much like the array above
    Writeln(ord(Enum));
  Writeln('---');

  Readln;
end.

The output is:

2
0
---
0
2
---

Why do the two loops produce different output?

like image 996
David Heffernan Avatar asked Jun 29 '15 14:06

David Heffernan


2 Answers

Because an array contains order information and a set does not.


Explanation with the use of documentation:

The internal data format of a static or dynamic array:

is stored as a contiguous sequence of elements of the component type of the array. The components with the lowest indexes are stored at the lowest memory addresses.

Walking over these indices with a for in loop is done in incremental order:

The array is traversed in increasing order, starting at the lowest array bound and ending at the array size minus one.

On the other side, the internal data format of a set:

is a bit array where each bit indicates whether an element is in the set or not.

Thus all these "indiced bits" are stored in one and the same "value". That is why a set can be typecasted to an Integer type, and why the order in which the bits are added is lost: [enum3, enum1] = [enum1, enum3].

like image 146
NGLN Avatar answered Sep 29 '22 06:09

NGLN


for Enum in Arr do
  Writeln(ord(Enum));

Here, Arr is an array, and so the items of the array are output in order. The documentation says:

The array is traversed in increasing order.

Hence 2 is output before 0.

for Enum in [enum3, enum1] do
  Writeln(ord(Enum));

Here, [enum3, enum1] is a set and the enumerator for a set happens to enumerate in order of increasing ordinal value. So the output has 0 first.

I don't think that is stated anywhere in the documentation that sets are enumerated in that order, but empirically that appears to be the case. However, since sets are an unordered type one should not be relying on their enumeration order anyway.


So the question then becomes understanding how [...] can be a set or an array at different points in the code. This all stems from the new XE7 dynamic array syntax which introduces (another) syntactical ambiguity. When we write

Arr := [enum3, enum1];

then [enum3, enum1] is an array. The compiler knows that Arr is an array and that information defines the type of the literal.

But when we write

for Enum in [enum3, enum1] do

then [enum3, enum1] is a set. Here the literal could in principle be either array or set. In such situations, I believe that the compiler will always prefer sets.

Again, I can't find any documentation that states that this is so, but empirically this is the case. Presumably since set enumerators pre-date the new dynamic array syntax they take precedence when there is ambiguity.

The meaning of a literal of the form [...] depends on its context.

like image 28
David Heffernan Avatar answered Sep 29 '22 05:09

David Heffernan