Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2011-12

Limitations of static "ranged" integer types and succ/pred, and dynamic solution.

11 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Skybuck Flying

Hello,

(Delphi limitation, but probably applies to free pascal as well):

Take following examples:

Tvalue = smallint;

or
Tvalue = word

or

Tvalue = 0..5000;

^ All of these examples are quite bad in the following way:

They become very static, which makes it pretty much impossible to change their types are pretty much a left over from the days of turbo pascal, and is a reason why people switched to C, where arrays where dynamic in range/size.

The succ/pred functions are also pretty useless and dangerous.

If a pascal type/array using these static types needs to change to a dynamic type, and these succ/pred functions will lead to out of range problems/situations.

My recommendation for the Delphi language is to do away with these static types and instead change them to dynamic types, just like arrays where expanded with dynamic types.

Ofcourse it's nice and perhaps even required to keep these static types for backward compatibility but it would also be nice if these types can be expanded with a dynamic type, so it can be safely used with dynamic arrays which can be any size/range.

How this should be solved syntax-ically I don't know but I will give it a try below, perhaps:

var
  MinValue : integer;
  MaxValue : integer;
Tvalue = MinValue...MaxValue;

then this type could also be used as follows:

var
  DynamicArrayOfValues : array[Tvalue] of integer;

The user could then even fall back to old style of programming:

MinValue := 5000;
MaxValue := 10000;
DynamicArrayOfValues.Allocate;
DynamicArrayOfValues [5000] := 1000;
DynamicArrayOfValues [6000] := 2000;
^ array starts at 5000 kinda cool.
Etc...

Finally the pred/succ could be safely used as well:

vValue := 5000;
DynamicArrayOfValues[Pred(vValue)] := etc; // would wrap back to 6000
DynamicArrayOfValues[Succ(vValue)] := etc; // would wrap back to 5000

Just some idea's for you to explore...

Bye,
Skybuck.

Reply by email

Skybuck Flying

Actually "wrap back" is probably not the correct behaviour for succ/pred.

So it should probably not "wrap back".

Instead it could give an out of range error message.

However at least Low and High should be able to be used on the dynamic range type, so that these limits can dynamically be acquired with those two functions.

This could help...

The user can then modify code from static to dynamic with minimal changes:

if Index = High(DynamicRangeType) then
begin
Index := Low(DynamicRangeType);
end;

To implement the range of the dynamic range type perhaps the following could be used:

SetLow( DynamicRangeType, 5000 );
SetHigh( DynamicRangeType, 6000 );
An oo version could also look like;
DynamicRangeType.SetLow( 5000 );
DynamicRangeType.SetHigh( 6000 );

Bye,
Skybuck.

Reply by email

Hans-Peter Diettrich
to Skybuck Flying: (Delphi limitation, but probably applies to free pascal as well): Take following examples:
Show quoted text 8 lines
^ All of these examples are quite bad in the following way: They become very static, which makes it pretty much impossible to change their 's by design. These types are pretty much a left over from the days of turbo pascal, and is a reason why people switched to C, where arrays where dynamic in range/size.

C arrays are emulated by pointer operations, i.e. p[i] is equivalent to (p+i)*, with no chance left for range checks.

Delphi has added dynamic arrays, with range checks possible at runtime.

The succ/pred functions are also pretty useless and dangerous.

Depends on proper implementation (with wrap-around).

DoDi

Reply by email

Vinzent Höfler
to Skybuck Flying: (Delphi limitation, but probably applies to free pascal as well): Take following examples:
Skybuck Flying wrote
(Delphi limitation, but probably applies to free pascal as well):

You have a very interesting definition of "limitation", but well...

Show quoted text 15 lines
Take following examples: Tvalue = smallint; or Tvalue = word or Tvalue = 0..5000; ^ All of these examples are quite bad in the following way: They become very static, which makes it pretty much impossible to change their can't see any reason why you would even want to change a range during

runtime. After all, there usually was a pretty good reason why you applied a range the first time, otherwise you would have used Integer directly.

My recommendation for the Delphi language is to do away with these static types and instead change them to dynamic types, just like arrays where expanded with dynamic types.

As if the only purpose of range types is to index dynamic arrays.

Show quoted text 6 lines
Ofcourse it's nice and perhaps even required to keep these static types for backward compatibility but it would also be nice if these types can be expanded with a dynamic type, so it can be safely used with dynamic arrays which can be any size/range.

In that case you just need one type: InfiniteInteger (or maybe call it UnconstrainedInteger), where it's bit size changes during runtime up to the point where the computer memory is exhausted. So, instead of getting Range-Check errors, you get memory allocation failures trying to allocate an integer requiring 2**128 bits. :->

Show quoted text 10 lines
var MinValue : integer; MaxValue : integer; Tvalue = MinValue...MaxValue; then this type could also be used as follows: var DynamicArrayOfValues : array[Tvalue] of integer;

Umm, and what shall happen, if I change MinValue somewhere inbetween? The array size and/or index range shall magically change with it?

And why would that be better than a (more) simple

var
    Dynamic : array[MinValue .. MaxValue] of Integer;
type
    TValue = Low (Dynamic) .. High (Dynamic);

The array and the type you're indexing it with are tied to each other either
way, so there's no point in changing only one of it. Or, to express it differently: As long as you don't know the size of the array you also don't know the range of the index type - and vice versa.

Well, considering that the latter is (to some extent) already available in the
current compiler, your proposal is IMO quite useless.

Or - may I speak freely, sir? ... Then, I think, it's bullshit.

Vinzent.

Reply by email

Skybuck Flying
to Vinzent Höfler

The reason for the proposal to add a dynamic/runtime version of Low/High/Succ/Pred is pretty simply:

Currently there simply is no dynamic/runtime version of it.

Thus any code which is written using low/high/succ/pred is static in nature, when the code needs to be changed to dynamic it's likely that this will cause problems.

The problems are a bit vague, perhaps even hard to spot, correct and understand, I am not going to examine these problems in detail right now, but these problems probably exist.

All that is needed to fix these problems is a dynamic version of these methods. But then again this might require even more work to have dynamic integer types/ranges as well to complement/support it.

I'll shall try to give one example of a potential problem:

Situation 1:

type
  Tvalue = 0..5000;
var
  vIndex : TValue;
  vA : TValue;
  vB : TValue;
for Index := Low(TValue) to High(TValue) do
begin
   if vA = High(TValue) then
   begin
       vA := Low(TValue);
   end else
   begin
       vA := Succ(vA);
   end;
end;

^ This is a real world example and will probably compile just fine or perhaps with a few correction.

Now suppose the Tvalue can no longer be static and must be changed to dynamic, a choice could be made to turn this into a smallint like so:

TValue = smallint

Suddenly this changes the range of low, high, succ, pred.

At runtime the real range is set, for example to 32000.

This breaks all the code, the wrap backs will be wrong, the vA will go out of range and will go to 32768 and perhaps even wrap back to negative.

Typecasts might also add to the problem, not sure about that.

Thus by simply changing the "ranged" static Tvalue to a more "open range" static integer type a whole can of worms/problems is opened.

These problems could be more easily solved if Low/High/Succ/Pred was more dynamic in nature and could adept to the runtime change.

Bye,
Skybuck.

Reply by email

waldo kitty
to Marco van de Voort
Marco van de Voort wrote 6 lines
In our previous episode, Skybuck Flying said: > > Currently there simply is no dynamic/runtime version of it. You are totally right. Please submit a patch for conforming arrays support :-)

ROTFL! sneaky sneaky sneaky but i love it! hehehe...

good show ;)

Reply by email

waldo kitty
to waldo kitty

On 12/23/2011 14:04, waldo kitty wrote:
[stuff]

ouch... my profound apologies to everyone... that was supposed to go private and i have only myself and my [reply] (vs [reply list] or [reply all]) button to blame...

Reply by email

Vinzent Höfler
to Skybuck Flying: The reason for the proposal to add a dynamic/runtime version of Low/High/Succ/Pred is…
Skybuck Flying wrote
The reason for the proposal to add a dynamic/runtime version of Low/High/Succ/Pred is pretty simply: Currently there simply is no dynamic/runtime version of it.

So what? I don't think that's necessary.

Thus any code which is written using low/high/succ/pred is static in nature, when the code needs to be changed to dynamic it's likely that this will cause problems.

No, it won't.

The problems are a bit vague, perhaps even hard to spot, correct and understand, I am not going to examine these problems in detail right now, but these problems probably exist.

Probably? If you want a whole new feature in a language which _by_ _design_ is based on static types, you should find a use case where no other solution
exists. (For certain values of "exist", of course.)

All that is needed to fix these problems is a dynamic version of these methods.

Yeah, and all is needed to write correct software is the single processor instruction: DWIM (Do What I Mean). Unfortunately nobody has implemented one,
yet.

Show quoted text 27 lines
I'll shall try to give one example of a potential problem: Situation 1: type Tvalue = 0..5000; var vIndex : TValue; vA : TValue; vB : TValue; for Index := Low(TValue) to High(TValue) do begin if vA = High(TValue) then begin vA := Low(TValue); end else begin vA := Succ(vA); end; end; ^ This is a real world example and will probably compile just fine or perhaps with a few correction. Now suppose the Tvalue can no longer be static

Now, you should explain why this can't be the case. Not simply state that it is so.

Actually, I never needed a dynamic range type. But that, of course, doesn't say they aren't useful per se, but to convince someone as ignorant as me you
should at least tell a use case where one would be really needed why you can't get away with local type declarations then.

Show quoted text 6 lines
and must be changed to dynamic, a choice could be made to turn this into a smallint like so: TValue = smallint Suddenly this changes the range of low, high, succ, pred.

Yes, and everything still works, because the range is still static.

At runtime the real range is set, for example to 32000.

Why? If the range changes during runtime, you probably don't even need a range in the first place.

This breaks all the code, the wrap backs will be wrong, the vA will go out of range and will go to 32768 and perhaps even wrap back to negative.

Yes, but that's because you are not using the range of the type, but assuming
some arbitrary sub-range the compiler doesn't even know about.

Thus by simply changing the "ranged" static Tvalue to a more "open range" static integer type a whole can of worms/problems is opened.

Yes. So my question would be, why are you so keen on opening it? ;) What shall happen to all those dependent variables that are declared with that type? What happens to arrays using this type as index? Not to mention that it seems a bad idea to let the range change dynamically. After all, obeying the range is some property which is checked at run-time especially for the reason that sometimes people like me are stupid enough to violate it.

While I'm at it, what about an adapting range type? For example, all ranges start with an empty range (let's say "1 .. 0") and each time you assign a value to a variable of that type, it will adapt to the new range, assuming that me - as the programmer - was completely free of errors when doing it. Of course, all other variables declared adapt the same way:

type
    Something = ; // "" seems like a nice symbol for "anything"
var
    A : Something;
    B : array[Something] of Integer; // Initially a zero-size array.
[...]
WriteLn (Low (A), " .. ", High (A), "/", Length (B)); // prints "1 .. 0/0".
A := 1;
WriteLn (Low (A), " .. ", High (A), "/", Length (B)); // prints "1 .. 1/1"
now.
A := -50000;
WriteLn (Low (A), " .. ", High (A)); // prints "-50000 .. 1/50002" now.

Nice idea, eh? NOT.

These problems could be more easily solved if Low/High/Succ/Pred was more dynamic in nature and could adept to the runtime change.

I don't see problems here. If I have a type "TrafficLight" that goes from "Red" via "Yellow" to "Green", I don't see any reason why I should be able to suddenly add a "Blue" value to is a statically typed language. And statically types usually means just that: Statically. This includes some properties of a type like the minimum and maximum value it can take.

Vinzent.

Reply by email

Skybuck Flying
to Marco van de Voort: In our previous episode, Skybuck Flying said: You are totally right. Please submit a…

-----Original Message-----

Earlier message 86 lines
From: Marco van de Voort Sent: Friday, December 23, 2011 13:21 To: FPC developers' list Subject: Re: [fpc-devel] Limitations of static "ranged" integer typesandsucc/pred, and dynamic solution. In our previous episode, Skybuck Flying said: > > Currently there simply is no dynamic/runtime version of it. " You are totally right. Please submit a patch for conforming arrays support :-) " Glad you agree, now we can discuss possible solutions ;) :) First a solution has to be found, possible solutions could be: TRangedInteger = record Low : integer; High : integer; Value : integer; end; or perhaps just a range or perhaps both. TRange = record Low : integer; High : integer; end; And then perhaps some checks/runtime checks can be build in by the compiler. Alternatively perhaps assignment operation, increment operator and other operators could be overloaded to do the necessary range checking ;) I am not sure what Low/High/Succ/Pred do ? Do they ever throw an runtime error, or exception ? Perhaps just having a constructor would already be enough to just solve the loop: var vIndex : TRangedInteger; vValue : TRangedInteger; // problem, need to set range again :( begin vIndex := TRangedInteger.Create( -Random(100), Random(100) ); vValue := TRangedInteger.Create( vIndex ); // perhaps could be solved with a copy from the other. // would be more nice if it was like a dynamic type or so ;) vRange := TRangedInteger.Create( 0, AskUserForMax ); // ask user for values vIndex := TRangedInteger.Create( vRange ); vValue := TRangedInteger.Create( vRange ); for vIndex := vIndex.Low to vIndex.High do begin if vValue = vValue.High then begin vValue.Low; end else begin vValue := vValue.Succ; end; end; end; So you see coming up with a solution wasn't that hard, however this is still a somewhat half-baked solution. It would be nice if simply Low(vValue) and High(vValue) could be used, so static (compile-time)and dynamic code (run-time) looks the same and can be compiled without code changes. So somehow Low and High would need to be "attached" to the runtime type: TRangedInteger. Perhaps Low and High and Succ and Pred should become "operators" ? For record ?!? So that they can be overriden ? Bye, Skybuck.

Reply by email

Marco van de Voort
to Skybuck Flying

In our previous episode, Skybuck Flying said:

Show quoted text 21 lines
" You are totally right. Please submit a patch for conforming arrays support :-) " Glad you agree, now we can discuss possible solutions ;) :) First a solution has to be found, possible solutions could be: TRangedInteger = record Low : integer; High : integer; Value : integer; end; or perhaps just a range or perhaps both. TRange = record Low : integer; High : integer; end;

See the ISO spec for conforming arrays. Arrays are all the same type, so you only have to the range once per array. Afaik they are passed as pseudo parameters though, not with structures.

So you see coming up with a solution wasn't that hard, however this is still a somewhat half-baked solution.

Exactly, and why bother so much if a complete ISO committee has already specified it for you?

It has been on the wish list of (at least some of) the FPC team for a long time to implement this, all was lacking was somebody with the interest in making it happen.

I'm very glad you finally decided to do something about it!

Reply by email