Lazarus

Programming => General => Topic started by: Bogen85 on September 29, 2022, 04:31:31 am

Title: Immutable data types
Post by: Bogen85 on September 29, 2022, 04:31:31 am
Yes, I know, I've seen this discussed a few times...

It seems like some of the issues (primarily related to how it would fit with pascal syntax) are:

Doing complex initial assignment in the var section (especially if it uses parameters from parent function or needs to make function calls is tedious...).

If with additional keywords it something like the following could work within existing pascal syntax (suggested names are just example names, not necessarily what I'm suggesting):

Code: Pascal  [Select][+][-]
  1. program immutable;
  2.  
  3.   procedure something (const a, b, c: integer);
  4.     var
  5.       d: integer; immutable;
  6.       e: integer; immutable;
  7.       f: integer; immutable;
  8.     begin
  9.       d := a + 4; final;
  10.       e := b + 5; final;
  11.       f := c + 6; final;
  12.       writeln ('a: ', a);
  13.       writeln ('b: ', b);
  14.       writeln ('c: ', c);
  15.       writeln ('d: ', d);
  16.       writeln ('e: ', e);
  17.       writeln ('f: ', f);
  18.     end;
  19.  
  20. begin
  21.   something (1, 2, 3);
  22. end.
  23.  

Using the immutable keyword, the final keyword would not likely not be used.
Basically with immutable the first assignment would lock in the value, and the compiler would not allow for further assignment.

If there was enough community and developer buy in to to such a feature change, I might be willing to do a lot of the legwork, providing I'd get sufficient assistance.



Title: Re: Immutable data types
Post by: marcov on September 29, 2022, 09:53:52 am
I don't see a description of what final would do, but modifiers afaik don't apply to statement lines. The closest I can come up with as analogous syntax would be aligned() so the syntax probably would be more

Code: Pascal  [Select][+][-]
  1.   f := final(c + 6);
  2.  


Title: Re: Immutable data types
Post by: Warfley on September 29, 2022, 11:24:57 am
Immutability must be transitive otherwise it is useless. If i have an immutable integer, but can make a mutable pointer to it, it is effectively not immutable.

So considering your example
Code: Pascal  [Select][+][-]
  1. var
  2.   i: Integer; immutable;
  3.   pi: PInteger; immutable;
  4. begin
  5.   i := 42; // Initial assignment is fine, any change afterwards would be violation
  6.   pi := @i; // Also fine because initial assignemnt
  7.   pi^ := 32; // This does not change pi but the pointer behind. As only pi is immutable not what it points towards this is fine, but violates the immutability of i
  8. end;
And this is generally not decidable:
Code: Pascal  [Select][+][-]
  1. var
  2.   i: Integer; immutable;
  3.   j: Integer;
  4.   pi: PInteger; immutable;
  5. begin
  6.   i := 42; // Initial assignment is fine, any change afterwards would be violation
  7.   pi := ifThen(Random(2) = 1, @i, @j); // Initial assignment is fine
  8.   pi^ := 32; // In 50% of cases (when random is 1) pi will point to i, which would be a violation of it's immutability, but in the other 50% of cases this would point to j, which is mutable and therefore fine

So the immutability information must transitively be passed with the pointer information, so there must be the concept of a pointer to an immutable object. But that pointer could also be mutable and immutable. Also there can be a pointer to that pointer and so on.
Without this in a language like pascal, which heavily relies on pointers a concept of immutability would be pointless.

So what you end up with is having to incorporate this into your type system, so for example:
Code: Pascal  [Select][+][-]
  1. type
  2.   ImmutableInt = immutable Integer;
  3.   PImmutableInt = ImmutableInt^;
  4.  
  5. var
  6.   i: ImmutableInt;
  7.   pi: PImmutableInt;
  8. begin
  9.   i := 42;
  10.   pi := @i;
  11.   pi^ := 32; // Error because pi dereferences to an immutable object
  12.  

And basically what we have here is the exact way the C typesystem works.
Title: Re: Immutable data types
Post by: Zaher on September 29, 2022, 12:44:26 pm
How the compiler will know it the value locked , you need to add AI  8)

Code: [Select]
program immutable;


  procedure something(var d: integer);
    begin
      d := d + 1; //compiler will raise error here in second call
    end;

var
d: integer; immutable;
begin
  d := 1;
  something (d);

  d := 2; final;
  something (d);
end.
[code]
Title: Re: Immutable data types
Post by: MarkMLl on September 29, 2022, 01:34:02 pm
Immutability must be transitive otherwise it is useless. If i have an immutable integer, but can make a mutable pointer to it, it is effectively not immutable.

The easiest way is to declare a value and allow constrained modification, followed by a custom block during which it's immutable. On exit from the block it's no longer in scope:

Code: Pascal  [Select][+][-]
  1. for i: integer= 0 to 9 do begin
  2. ...
  3. end;
  4.  

...and we all know what the developers think about /that/ sort of thing.

MarkMLl
Title: Re: Immutable data types
Post by: Warfley on September 29, 2022, 01:46:35 pm
Also such a simple immutability concept must extend to methods:
Code: Pascal  [Select][+][-]
  1. {$ModeSwitch AdvancedRecords}
  2. type
  3.   TTestRec = record
  4.     i: Integer;
  5.     procedure Increment;
  6.   end;
  7.  
  8. procedure TTestRec.Increment;
  9. begin
  10.   Inc(Self.i);
  11. end;
  12.  
  13. var
  14.   r: TTestRec = (i: 0); immutable;
  15. begin
  16.   Inc(r.i); // Should throw an error because r is immutable
  17.   r.Increment; // What about this?
  18. end;

So you need a notion which methods can alter (i.e. for which methods self is immutable). E.g.:
Code: Pascal  [Select][+][-]
  1.   TTestRec = record
  2.     i: Integer;
  3.     procedure Increment; immutable;
  4.   end;
And thereby re-invent the C++ const system.
Title: Re: Immutable data types
Post by: Bogen85 on September 29, 2022, 01:48:51 pm
How the compiler will know it the value locked , you need to add AI  8)

Rhetorical answer, I know: So, C/C++/Rust all have AI's built into them? (And C/C++ has that AI for more than 20 years?)

But to get back to Pascal. And yes, a lot more involved then this, but at a basic level the compiler just has to keep track when emitting code that when it sees the the first assignment, it marks that identifier internally as already assigned. Any attempt to emit code that would result in another assignment, would result in an error.

FPC can already warn you (and you can make that warning an error) that you are trying to access a variable that has not been initialized yet. So at some level it is already do that kind of tracking.
Title: Re: Immutable data types
Post by: Zaher on September 29, 2022, 01:56:37 pm
It is exists also D lang , and they have good examples https://dlang.org/spec/const3.html

If you need add this feature you need to modify other syntax of language too not just adding new modifier (or AI  :D ).
Title: Re: Immutable data types
Post by: Bogen85 on September 29, 2022, 01:59:07 pm
The easiest way is to declare a value and allow constrained modification, followed by a custom block during which it's immutable. On exit from the block it's no longer in scope:

Code: Pascal  [Select][+][-]
  1. for i: integer= 0 to 9 do begin
  2. ...
  3. end;
  4.  

...and we all know what the developers think about /that/ sort of thing.

MarkMLl

And currently that is not possible. Something similar can already be achieved where the index is read only and the index can't be modified.
The following works (if you build and install FPC from current git sources), but there is a lot of overhead involved to support it.

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. {$modeswitch anonymousfunctions}
  3. {$modeswitch functionreferences}
  4.  
  5. program readonlyloop;
  6.  
  7. type
  8.   tLoop = reference to procedure (const i, limit: integer);
  9.  
  10.   procedure main;
  11.     var
  12.       L1: tLoop;
  13.  
  14.     begin
  15.       L1 := procedure (const i, limit: integer) begin
  16.         if i >= limit then exit;
  17.         writeln ('at local iteration ', i);
  18.         L1 (i+1, limit);
  19.       end;
  20.       L1 (10, 25);
  21.     end;
  22.  
  23. begin
  24.   main;
  25. end.
  26.  

So, FPC can already do this sort of immutablity, albeit the syntax looks odd, and there would need to be a lot of optimization added to make that work efficiently.
Title: Re: Immutable data types
Post by: Bogen85 on September 29, 2022, 02:00:57 pm
It is exists also D lang , and they have good examples https://dlang.org/spec/const3.html

Yes, I'm familiar with D lang, my language list in my rhetorical answer was not exhaustive...  :D
Title: Re: Immutable data types
Post by: Thaddy on September 29, 2022, 02:01:05 pm
No AI involved.
Just a Pascal range, like
Code: Pascal  [Select][+][-]
  1. program test;
  2. type Tmyrange = 0..9;
  3. var
  4.   i: Tmyrange;
  5. begin
  6.   for i in Tmyrange do writeln(i);
  7. end.
Here I is not an index, but content. The index itself is a good example of immutable. You can't even see it... :D
Title: Re: Immutable data types
Post by: marcov on September 29, 2022, 02:09:33 pm
Changing the type system might be a too big job for an initial project/feature in the compiler.
Title: Re: Immutable data types
Post by: PascalDragon on September 29, 2022, 02:10:39 pm
Something similar can already be achieved where the index is read only and the index can't be modified.
The following works (if you build and install FPC from current git sources), but there is a lot of overhead involved to support it.

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. {$modeswitch anonymousfunctions}
  3. {$modeswitch functionreferences}
  4.  
  5. program readonlyloop;
  6.  
  7. type
  8.   tLoop = reference to procedure (const i, limit: integer);
  9.  
  10.   procedure main;
  11.     var
  12.       L1: tLoop;
  13.  
  14.     begin
  15.       L1 := procedure (const i, limit: integer) begin
  16.         if i >= limit then exit;
  17.         writeln ('at local iteration ', i);
  18.         L1 (i+1, limit);
  19.       end;
  20.       L1 (10, 25);
  21.     end;
  22.  
  23. begin
  24.   main;
  25. end.
  26.  

So, FPC can already do this sort of immutablity, albeit the syntax looks odd, and there would need to be a lot of optimization added to make that work efficiently.

You can just as well use a nested function pointer together with an anonymous function. Much less overhead this way. ;)

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. {$modeswitch anonymousfunctions}
  3. {$modeswitch nestedprocvars}
  4.  
  5. program readonlyloop;
  6.  
  7. type
  8.   tLoop = procedure (const i, limit: integer) is nested;
  9.  
  10.   procedure main;
  11.     var
  12.       L1: tLoop;
  13.  
  14.     begin
  15.       L1 := procedure (const i, limit: integer) begin
  16.         if i >= limit then exit;
  17.         writeln ('at local iteration ', i);
  18.         L1 (i+1, limit);
  19.       end;
  20.       L1 (10, 25);
  21.     end;
  22.  
  23. begin
  24.   main;
  25. end.

Regarding some immutable mechanism: for the JVM target FPC already supports immutability in the form of a separate section inside class and record declarations, but only for external classes:

Code: Pascal  [Select][+][-]
  1.   ANNfcEvent = class sealed external 'android.nfc' name 'NfcEvent' (JLObject)
  2.   public
  3.     final var
  4.       fnfcAdapter: ANNfcAdapter; external name 'nfcAdapter';
  5.   end;
  6.  

This is not supported on other platforms, because this would require full DFA support and thus this currently simply can not be supported.
Title: Re: Immutable data types
Post by: Bogen85 on September 29, 2022, 02:15:27 pm
No AI involved.
Just a Pascal range, like
Code: Pascal  [Select][+][-]
  1. program test;
  2. type Tmyrange = 0..9;
  3. var
  4.   i: Tmyrange;
  5. begin
  6.   for i in Tmyrange do writeln(i);
  7. end.
Here I is not an index, but content. The index itself is a good example of immutable. You can't even see it... :D

Yes, and it can't be modified inside that loop, agreed.
Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. type
  4.   Tmyrange = 0..9;
  5. var
  6.   i: Tmyrange;
  7. begin
  8.   for i in Tmyrange do writeln (i);
  9.  
  10.   for i in Tmyrange do begin
  11.     writeln (i);
  12.     i := succ(i);
  13.   end;
  14. end.
  15.  

Code: Text  [Select][+][-]
  1. Free Pascal Compiler version 3.3.1 [2022/09/26] for x86_64
  2. Copyright (c) 1993-2022 by Florian Klaempfl and others
  3. Target OS: Linux for x86-64
  4. Compiling ./lazforum/test_loop.pas
  5. test_loop.pas(12,7) Error: Illegal assignment to for-loop variable "i"
  6. test_loop.pas(16) Fatal: There were 1 errors compiling module, stopping
  7. Fatal: Compilation aborted
  8. Error: /home/shared-development/fpc_usr/lib/fpc/3.3.1/ppcx64 returned an error exitcode
  9.  


Also, i is exposed.
Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. type
  4.   Tmyrange = 0..9;
  5. var
  6.   i: Tmyrange;
  7. begin
  8.   writeln (i);
  9.   for i in Tmyrange do writeln (i);
  10.   writeln (i);
  11. end.
  12.  

And it's final value accessible after the loop, and still actually modifiable.

Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. type
  4.   Tmyrange = 0..9;
  5. var
  6.   i: Tmyrange;
  7.  
  8.   procedure something;
  9.   begin
  10.     inc(i);
  11.     writeln ('something ', i);
  12.   end;
  13.  
  14. begin
  15.   writeln ('before ', i);
  16.   for i in Tmyrange do begin
  17.     writeln ('inside ', i);
  18.     something;
  19.   end;
  20.   writeln ('after ', i);
  21. end.
  22.  

Code: Text  [Select][+][-]
  1. before 0
  2. inside 0
  3. something 1
  4. inside 2
  5. something 3
  6. inside 4
  7. something 5
  8. inside 6
  9. something 7
  10. inside 8
  11. something 9
  12. after 9
  13.  

Title: Re: Immutable data types
Post by: PascalDragon on September 29, 2022, 02:19:01 pm
And it's final value accessible after the loop, and still actually modifiable.

But before and after the loop the value of the index variable is considered undefined (except for the later if the loop has been left with a Break or goto). This is more obvious if you use a function instead of the main body, because then it won't necessarily be 0 before the loop.
Title: Re: Immutable data types
Post by: Bogen85 on September 29, 2022, 02:24:26 pm
And it's final value accessible after the loop, and still actually modifiable.

But before and after the loop the value of the index variable is considered undefined (except for the later if the loop has been left with a Break or goto). This is more obvious if you use a function instead of the main body, because then it won't necessarily be 0 before the loop.

Agreed (as far as the undefined portions). But it still accessible and modifiable in a nested procedure. Is that considered undefined as well?
Title: Re: Immutable data types
Post by: jamie on September 29, 2022, 03:34:20 pm
Please don't bring that rusty language to the table.
If u need that kind of control then maybe u need scripture?

 I think a record with a few operator overloads will do.
Title: Re: Immutable data types
Post by: Thaddy on September 29, 2022, 06:34:06 pm
People got on for IONS before Zero...
https://en.wikipedia.org/wiki/0
Feel free to also donate to wikipedia..
Title: Re: Immutable data types
Post by: marcov on September 29, 2022, 08:46:31 pm
People got on for IONS before Zero...
https://en.wikipedia.org/wiki/0
Feel free to also donate to wikipedia..

And even aeons before that  >:D
Title: Re: Immutable data types
Post by: MarkMLl on September 29, 2022, 08:53:29 pm
People got on for IONS before Zero...
And even aeons before that  >:D

I think he meant there was a -ve charge...

MarkMLl
Title: Re: Immutable data types
Post by: Thaddy on September 29, 2022, 10:50:20 pm
And you both can miss around 10 euro to the wikimedia foundation. We three of the age, we actually had real books, real touchable Encyclopedia, I still have them. (two of them,  over 60 volumes, Elsevier and Spectrum) Brittanica was not easily available back in the 50/60's... 8) And in the 70's I was either playing guitar or remembering everything I forgot by now. (inc girls) Not my current wife of course. I just married ONCE.
Title: Re: Immutable data types
Post by: Bogen85 on September 30, 2022, 12:27:01 am
Alright...

Thanks for that humor.  :D

Functional programming (now possible with anonymous methods, references to them, and the full closure support they offer) as is can likely do most of what is necessary here.

With const parameters should give all needed compile time read-only access controls I'm needing for some particular use cases.

Both for certain types of iterators and read-only data structures for concurrent applications (yes, that could be abused/bypassed, I'm obviously aware of that, but as already pointed out, records with the applicable access controls and properties can enforce that at runtime, in addition to the compile time checks mentioned above.

Beyond that, the Pascal syntax for variable declaration and initial assignment don't seem to be conducive to this sort of "evolution" of the language.

Not being conducive to this sort of "evolution" of pascal, many would see as good thing.

FPC has already evolved to support this type of functional programming, which could be sufficient to provide the immutability "features" that many desire. I say "many", there are obviously "many" who have no desire to have these features, so they won't need to leverage functional programming to get access to them.

My intent for this was not for an iterator index, but exploring that aspect is what led to functional programming achieving what I'm look for.

We will see... Worst case @PascalDragon sees a lot of issue reports from me on anonymous method closure problems... (and related).

Here is an example a type of generic iterator for looping... But of course there are other things that apply similar mechanisms...

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. {$modeswitch anonymousfunctions}
  3. {$modeswitch functionreferences}
  4.  
  5. program readonlyloop;
  6.  
  7. type
  8.   tLoop = reference to procedure (const i: integer; var leave: boolean);
  9.  
  10.   function looper (const initial_value, final_value: integer; const loopPr: tLoop): boolean;
  11.     var
  12.       i: integer;
  13.     begin
  14.       result := false;
  15.       for i := initial_value to final_value do begin
  16.         loopPr (i, result);
  17.         if result then exit;
  18.       end;
  19.     end;
  20.  
  21.   procedure main;
  22.     const
  23.       too_much = 10;
  24.  
  25.     begin
  26.       looper (4, 15,
  27.         procedure (const i: integer; var leave: boolean) begin
  28.           writeln('loop #1 i: ', i);
  29.           if i > too_much then begin
  30.             leave := true;
  31.             exit;
  32.           end;
  33.           writeln('still in loop.')
  34.         end);
  35.  
  36.       looper (2, 5,
  37.         procedure (const i: integer; var leave: boolean) begin
  38.           writeln('loop #2 i: ', i);
  39.           leave := false;
  40.         end);
  41.     end;
  42.  
  43. begin
  44.   main;
  45. end.
  46.  

Which results in:
Code: Text  [Select][+][-]
  1. loop #1 i: 4
  2. still in loop.
  3. loop #1 i: 5
  4. still in loop.
  5. loop #1 i: 6
  6. still in loop.
  7. loop #1 i: 7
  8. still in loop.
  9. loop #1 i: 8
  10. still in loop.
  11. loop #1 i: 9
  12. still in loop.
  13. loop #1 i: 10
  14. still in loop.
  15. loop #1 i: 11
  16. loop #2 i: 2
  17. loop #2 i: 3
  18. loop #2 i: 4
  19. loop #2 i: 5
  20.  
Title: Re: Immutable data types
Post by: Bogen85 on September 30, 2022, 03:04:37 am
The reason why I say that anonymous methods solves this problem, is because my original source code musings from this initial post can use the following (now supported in FPC) to achive immutable variable assignments that the compiler enforces (but yes, as others have stated, could be bypassed with pointers.)

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. {$modeswitch anonymousfunctions}
  3.  
  4. program immutable;
  5.  
  6.   procedure something (const a, b, c: integer);
  7.     begin
  8.       procedure (const d, e, f: integer) begin
  9.         writeln ('a: ', a);
  10.         writeln ('b: ', b);
  11.         writeln ('c: ', c);
  12.         writeln ('d: ', d);
  13.         writeln ('e: ', e);
  14.         writeln ('f: ', f);
  15.       end(a+4, b+5, c+6);
  16.     end;
  17.  
  18. begin
  19.   something (1, 2, 3);
  20. end.
  21.  

d, e, f are indeed one time assignable and compiler enforces that.

producing this output:

Code: Text  [Select][+][-]
  1. $ ./lazforum/immutable
  2. a: 1
  3. b: 2
  4. c: 3
  5. d: 5
  6. e: 7
  7. f: 9
  8.  
Title: Re: Immutable data types
Post by: Joanna on September 30, 2022, 08:18:13 am
Hello I guess I got here late to discussion and bogen85 is away in irc.
Can anyone explain how the anonymous method works ? I’ve never seen
Code: Pascal  [Select][+][-]
  1.  procedure something (const a, b, c: integer);
  2.     begin
  3.       procedure (const d, e, f: integer) begin
Syntax before and didn’t know it was even possible.

Is this part the anonymous procedure calling itself?
Code: Pascal  [Select][+][-]
  1.  end(a+4, b+5, c+6);

What keeps this procedure from being called more than once?
Title: Re: Immutable data types
Post by: Bogen85 on September 30, 2022, 02:48:27 pm
Hello I guess I got here late to discussion and bogen85 is away in irc.
Can anyone explain how the anonymous method works ? I’ve never seen
Code: Pascal  [Select][+][-]
  1.  procedure something (const a, b, c: integer);
  2.     begin
  3.       procedure (const d, e, f: integer) begin
Syntax before and didn’t know it was even possible.

Support for anonymous methods in free pascal is a recent addition, from May of this year and available if you clone FPC from source and build and install it.
PascalDragon knows a lot more of the details about this.

They are not in the current release of FPC, but will be available in the next release.
EDIT: Next major release, as pointed out by @marcov.

An anonymous method is, too make a long answer short, basically a function or procedure declared inline.

To be used it can be:

An anonymous method has view of all variables and any other identifiers visible in the scope of where it is defined.
In other words, any identifiers that can be used before or after the anonymous method can be used in the anonymous method.
This is true even when the method is passed somewhere else to be called, where that somewhere else does not have the same variable/identifier scope.

This "capturing" of identifiers in it's view that is using so the anonymous method can be called somewhere else is referred to as a closure.
That "view" is part of the anonymous method definition along with the definition you do see.

The anonymous method being directly associated with this closure is what allows it be called elsewhere, where the variable/identifier scope is not necessarily.

From a usage standpoint, anonymous method are similar to procedure and function pointers.
What makes them "special" is the closure that is directly associated with them.

Is this part the anonymous procedure calling itself?
Code: Pascal  [Select][+][-]
  1.  end(a+4, b+5, c+6);
Yes

What keeps this procedure from being called more than once?

It is not assigned to a variable of type reference to procedure.
If it were it could be easily be called more than once.

It is only in the code once, and it only calls itself once.

It is also not in a loop and being called multiple times.

Title: Re: Immutable data types
Post by: marcov on September 30, 2022, 03:15:39 pm
They are not in the current release of FPC, but will be available in the next release.

Next major release. So not in 3.2.4, which will be a fixes (3.2 branch) release.
Title: Re: Immutable data types
Post by: PascalDragon on September 30, 2022, 03:27:19 pm
We will see... Worst case @PascalDragon sees a lot of issue reports from me on anonymous method closure problems... (and related).

Well, the more bugs for anonymous functions and function references are found (and fixed) before the next major release, the better. :D

And it might be nitpicky, but in FPC they are called “anonymous functions”, not “methods” (I'm aware that Delphi calls them “methods”, but the construct itself has nothing per se that justifies that name).

Hello I guess I got here late to discussion and bogen85 is away in irc.
Can anyone explain how the anonymous method works ? I’ve never seen
Code: Pascal  [Select][+][-]
  1.  procedure something (const a, b, c: integer);
  2.     begin
  3.       procedure (const d, e, f: integer) begin
Syntax before and didn’t know it was even possible.

See also this (https://forum.lazarus.freepascal.org/index.php/topic,59468.0.html) announcement post.

Is this part the anonymous procedure calling itself?
Code: Pascal  [Select][+][-]
  1.  end(a+4, b+5, c+6);

What keeps this procedure from being called more than once?

The procedure has no name, so there is no way to call it again (or even inside itself) once it returns.

To be used it can be:
  • Called immediately
  • Assigned to a procedure or function reference variable to be called later
  • Passed as a parameter in a call that receives a procedure or function reference

Depending on what is captured by an anonymous function it can also be assigned to a function pointer or a method pointer or (always) a nested function pointer. That's an extension compared to what Delphi allows. :)
Title: Re: Immutable data types
Post by: Bogen85 on September 30, 2022, 04:07:54 pm
We will see... Worst case @PascalDragon sees a lot of issue reports from me on anonymous method closure problems... (and related).

Well, the more bugs for anonymous functions and function references are found (and fixed) before the next major release, the better. :D

And it might be nitpicky, but in FPC they are called “anonymous functions”, not “methods” (I'm aware that Delphi calls them “methods”, but the construct itself has nothing per se that justifies that name).

Thanks.

I will try to use the correct nomenclature from now on.  :)
Title: Re: Immutable data types
Post by: AlanTheBeast on September 30, 2022, 05:22:41 pm
And it's final value accessible after the loop, and still actually modifiable.

But before and after the loop the value of the index variable is considered undefined (except for the later if the loop has been left with a Break or goto). This is more obvious if you use a function instead of the main body, because then it won't necessarily be 0 before the loop.

Of course if i were not declared at all, and only in scope for the "for" loop, then it would not be accessible at all after the loop.

Code: Pascal  [Select][+][-]
  1.             x := 23;        // i does not exist
  2.             for i in Tmyrange do                          // i exists!  (and probably in a nice snug, fast register)
  3.                begin
  4.                        GreatBallofFire [i] := x + i -1;
  5.                end;                                            //  i ceases to exist
  6.            i := i + 1;                                       // compiler go boo!
  7.  
Title: Re: Immutable data types
Post by: Bogen85 on September 30, 2022, 07:15:20 pm
But how can this be done in Free Pascal?

Of course if i were not declared at all, and only in scope for the "for" loop, then it would not be accessible at all after the loop.

Code: Pascal  [Select][+][-]
  1.             x := 23;        // i does not exist
  2.             for i in Tmyrange do                          // i exists!  (and probably in a nice snug, fast register)
  3.                begin
  4.                        GreatBallofFire [i] := x + i -1;
  5.                end;                                            //  i ceases to exist
  6.            i := i + 1;                                       // compiler go boo!
  7.  

I just now tried that.

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2.  
  3. program immutable2;
  4.  
  5.   type
  6.     tSet1 = (a, b, c);
  7.  
  8.   procedure something_else;
  9.     //var
  10.     //  i: tSet1;
  11.     begin
  12.       for i in tSet1 do begin
  13.         writeln(ord(i));
  14.       end;
  15.     end;
  16.  
  17. begin
  18.   something_else;
  19. end.
  20.  

That results in:

Code: Text  [Select][+][-]
  1. /home/shared-development/fpc_usr/bin/fpc -Fu./src -Fu./lib -Xc -XD -Xi -Sm -Sg -Os -gl -Sd -vnewh -Senwh -Sh -FE./bin lazforum/immutable2.pas
  2. Hint: Start of reading config file ~/.fpc.cfg
  3. Hint: End of reading config file ~/.fpc.cfg
  4. Free Pascal Compiler version 3.3.1 [2022/09/26] for x86_64
  5. Copyright (c) 1993-2022 by Florian Klaempfl and others
  6. Target OS: Linux for x86-64
  7. Compiling lazforum/immutable2.pas
  8. immutable2.pas(12,11) Error: Identifier not found "i"
  9. immutable2.pas(13,21) Error: Identifier not found "i"
  10. immutable2.pas(20) Fatal: There were 2 errors compiling module, stopping
  11. Fatal: Compilation aborted
  12. Error: /home/shared-development/fpc_usr/lib/fpc/3.3.1/ppcx64 returned an error exitcode
  13.  

But the following does compile and run:
Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2.  
  3. program immutable2;
  4.  
  5.   type
  6.     tSet1 = (a, b, c);
  7.  
  8.   procedure something_else;
  9.     var
  10.       i: tSet1;
  11.     begin
  12.       for i in tSet1 do begin
  13.         writeln(ord(i));
  14.       end;
  15.     end;
  16.  
  17. begin
  18.   something_else;
  19. end.
  20.  

Which when run gives:

Code: Text  [Select][+][-]
  1. 0
  2. 1
  3. 2
  4.  
Title: Re: Immutable data types
Post by: AlanTheBeast on September 30, 2022, 10:15:11 pm
But how can this be done in Free Pascal?

It can't.
But (IMO) it should.
Title: Re: Immutable data types
Post by: MarkMLl on October 01, 2022, 10:48:31 am
But how can this be done in Free Pascal?

It can't.
But (IMO) it should.

Hence my example at https://forum.lazarus.freepascal.org/index.php/topic,60749.msg455490.html#msg455490 which could also be extended to e.g. a with extension.

However I'd point out that the core developers have unequivocally rejected this sort of thing in the past: it's their project.

MarkMLl
Title: Re: Immutable data types
Post by: Arioch on October 01, 2022, 08:11:04 pm
i think it now would be done in the course of Delphi catch-up and it would be like

Code: Pascal  [Select][+][-]
  1. begin
  2.   a := ...
  3.   b := ...
  4.   begin
  5.       const c = a+b;
  6.       ...
  7.  

However, this is only hald a feature, as functional languages do not require immediate binding,

Code: Pascal  [Select][+][-]
  1. val
  2.   b: integer;
  3. begin
  4.     .....
  5.   a := b + 2; // error, b non-init
  6.     ....
  7.   b := 10;   // ok
  8.   a := b + 2; // ok
  9.      ....
  10.   b := 20;  // error, b is already bound
  11.     .....
  12.  

But yeah, probably won't happen.

P.S. as for taking pointers - i do not see it outside the already esiting realm between taking pointers to typed consts, untyped consts and function const parameters. Language should protect us from _accidentally_ doing self-harm, when we intentionally go unsafe we would alway find ways.
Title: Re: Immutable data types
Post by: Bogen85 on October 01, 2022, 11:49:54 pm
The reason why I say that anonymous functions solves this problem, is because my original source code musings from this initial post can use the following (now supported in FPC) to achive immutable variable assignments that the compiler enforces (but yes, as others have stated, could be bypassed with pointers.)

And it does not always need to be anonymous functions. It can be named nested functions. But the point is that const var parameters satisfy cursory compiled enforced immutability and by leveraging them one can cut down the required number of var declarations overall.
Title: Re: Immutable data types
Post by: MarkMLl on October 02, 2022, 09:07:38 am
P.S. as for taking pointers - i do not see it outside the already esiting realm between taking pointers to typed consts, untyped consts and function const parameters. Language should protect us from _accidentally_ doing self-harm, when we intentionally go unsafe we would alway find ways.

Which is one reason why I'm a fan of the Modula-3 approach: insist that certain types of operation should only be possible in modules marked as unsafe, and don't allow inexperienced team members to work on unsafe modules.

MarkMLl
Title: Re: Immutable data types
Post by: Warfley on October 02, 2022, 11:23:27 am
P.S. as for taking pointers - i do not see it outside the already esiting realm between taking pointers to typed consts, untyped consts and function const parameters. Language should protect us from _accidentally_ doing self-harm, when we intentionally go unsafe we would alway find ways.
Well, the thing is that you need to use pointers in pascal. Most simple case is classes, class instances are always pointers, there is no way you can use a class without a pointer. Other cases are unknown parameter passing (e.g. when calling QueueAsyncCall you need to pass your data as raw pointer), or when using classical TList, etc.

Using pointers is essential to pascal, and having an immutability concept that does not involve pointers is basically useless, because it is not applicable to a lot of your code. Thats also not always intentional. If you are in a situation where you need to take the pointer, then after you took the pointer and you pass it somewhere all immutability information is lost. So unless you check always where each pointer comes from you can easiely accidentally change the value behind that pointer simply because you don't know that it is supposed to be immutable
Title: Re: Immutable data types
Post by: MarkMLl on October 02, 2022, 11:33:17 am
Using pointers is essential to pascal, and having an immutability concept that does not involve pointers is basically useless, because it is not applicable to a lot of your code. Thats also not always intentional. If you are in a situation where you need to take the pointer, then after you took the pointer and you pass it somewhere all immutability information is lost. So unless you check always where each pointer comes from you can easiely accidentally change the value behind that pointer simply because you don't know that it is supposed to be immutable

But you don't need to use pointers or take addresses explicitly in most cases: they've been hidden in well-tested units by the RTL/FCL/LCL developers.

MarkMLl
Title: Re: Immutable data types
Post by: Arioch on October 02, 2022, 05:11:02 pm
Well, the thing is that you need to use pointers in pascal. Most simple case is classes

When classes are said to be "most simple case" it says a lot :-)

Quote from: Warfley
Using pointers is essential to pascal, and having an immutability concept that does not involve pointers is basically useless,

You seem lacking C++ distinction between int const * V and int * const V
And i agree, on some occasions i lack it too.

But does it mean that const should be removed from Pascal, because it is not hearmetically sealed?

What cost would it take to implement "perfectly const" objects in Pascal? Those that only consists of "pure functions" and are disallowed to have public writeable members, friend members and all direct and indirect ways to change their inner state.

Basically, for people/problems when this rigor is essential - there are other languages already.

Quote from: Warfley
because it is not applicable to a lot of your code.

I think differently.

A simple problem of a function has similar computations on slightly different data.
Like multi-D array.  Data [a1,a2,a3...a8]

I scan the array over a1 dimension, to select some "optimal" position. Maybe minimum, maybe maximum, maybe close to average, or anything.

Then i copy-paste it to a2, a3, a4... - by a same or different criteria.

Then i try to replace those variables, to avoid stupid typo errors, that - after hidden in the noise of boilkerplate - produce what is akin to data [a1, a2, a3, a4, a2, a6, a7, a8]
You know, those lame and stupid
Code: Pascal  [Select][+][-]
  1.   // here is also some noise about IsNan(x), IsInfinite(x) and other special cases.
  2.   if CurrentValue^ < Min3 then
  3.      Min5 := CurrentValue^;  
  4.   if CurrentValue^ > Max5 then
  5.      Min5 := CurrentValue^;  
  6.  

How could values instead of variables help me? well, simply. Pascal would catch my typoes during compilation. I would no more doing loops over

Code: Pascal  [Select][+][-]
  1. var
  2.   CurrentValue: PDouble;
  3.   CurrentMin, CurrentMax: Double;
  4. val
  5.   Min1, Min2, Min3, ...: Double;
  6.   Max1, Max2, Max3, ...: Double;
  7. begin
  8.  
  9. ....
  10.   CurrentMin := data [a1,a2,a3, Low(Data[a1,a2,a3], Low(Data[a1,a2,a3,1], ... );
  11.   CurrentMax := CurrentMin;
  12.   // just any real cell would do, actually. Or i would have to assign to NaN and make a special IsNan case later
  13.  
  14.   for i4 := Low(Data[a1,a2,a3] to HighData[a1,a2,a3] do
  15.     for i5 := Low(Data[a1,a2,a3, i4] to HighData[a1,a2,a3, i4] do
  16.        ....
  17.          begin
  18.            CurrentValue := @data[ a1,a2,a3, i4, i5, ... i8 ];
  19.            // some aux computations might be added there, usually they actually are.
  20.            CurrentMin := Math.Min(CurrentValue^, CurrentMin);
  21.            CurrentMax := Math.Max(CurrentValue^, CurrentMax);
  22.          end;    
  23.  
  24.   Min4 := CurrentMin;  
  25.   Max4 := CurrentMax;  
  26.  

Now, the amount of copy-paste code that has to be edited after copy-paste became several time less.

Most of the code, if it can not be extracted to a inner funciton, found be copy-pasted almost verbatim.

The most noisy and similarly looking things like Max2/Max5 - are no more a problem.
The language would make sure i did assignment and that i only did one single assignment.

Today a cognitive burden is snowballing. I have in evey single line of code remember, which of MaxN and MinN i still should change and which i should never change again. And if i make mistake - spotting it would be very hard and consequences would be hardly traceable, burried deep into computation dark waters.

With values this burden is magically taken off of me. I just know there is no such kind a mistake again. FPC would stop me if i do. I can focus on more essential problems now.

You said the FPC could became regorous and prohibit me taking pointers to Min3 ? Okay, i did not want to do it. Fine.

Code: Pascal  [Select][+][-]
  1. var p: pointer;
  2. begin
  3.    p :=  @'123456';
  4.    p :=  @'123456'[10];
  5.  

Can i take those poiinters? Maybe i can, maybe i can not, in normal code it is irrelevant, i won't try.
If some place requires "hackerish" code - i can go all the way into jungles including external assembler. But those places are few.

So, if FPC would let me take pointers to values, like it lets me taking pointer to const arguments, well, okay, I know pointers are dangerous and unsafe. Everyone does.
When i take pointers - i know i stepped into calamity zone and thrown off FPC's safety net. But often i can do without them.

It is not about "making unsafe code" impossible. It is about *separation* of safe and unsafe code.

Pointers are always poitners;
Code: Pascal  [Select][+][-]
  1. function  Ptr(const s: string): PChar;
  2. var a: string; b: char; c: PChar;
  3. begin
  4.    c := @s[10];
  5.    a := s;
  6.    c^ := #255;
  7.    
  8.    b := a[10];
  9.    c := @b;
  10.    Result := c;
  11. end;
  12.  
  13. ...
  14. var p: PChar;
  15. begin
  16.   p := Ptr('12345');
  17.  
  18.   //// many lines of code with procedure calls
  19.  
  20.   writeln(p^);
  21. end.
  22.  

Would you try to guess what would be written?
Would you tell "const" is useless, defunct, treacherous keyword that should be removed from Pascal?

I do not want for Pascal to become a jail where doing harm and erros  is absoilutely impossible in no way.
I want Pascal to provide me features to sepparate safe and unsafe coding patterns, and to let me code with "least unsafety principle" (to borrow buzz from "least security principle" of OS design)
Title: Re: Immutable data types
Post by: Arioch on October 02, 2022, 06:05:08 pm
But you don't need to use pointers or take addresses explicitly in most cases: they've been hidden in well-tested units by the RTL/FCL/LCL developers.

Or so it should had been.

But Delphi was always pressed by commercial schedule, so Delphi 2009 was released instead of testing.

And even LTS Delphi XE2 can crash her heap manager just because of using "const string" parameters.
Just optimizations (avoiging multi-CPU bus global-lock for ARC) leaked into abstractions.
And i did not even start on runtime backages, that FPC is blessed with having not, for now :-)
Title: Re: Immutable data types
Post by: AlanTheBeast on October 03, 2022, 12:05:30 am
So, if FPC would let me take pointers to values, like it lets me taking pointer to const arguments, well, okay, I know pointers are dangerous and unsafe. Everyone does.
When i take pointers - i know i stepped into calamity zone and thrown off FPC's safety net. But often i can do without them.

Normally I 'tsk-tsk' at such a statement.  Pointers are a fundamental aspect of Pascal.  Bedrock.

Yet, I found a rarely (once / month) occurring bug literally minutes ago that is pointer related.  Actually related to me "re-purposing" a thread message that came into a thread as a pointer to a data block, but re-purposed to send a simple longword to a dependent/waiting thread.  Of course I used it to pass the message before disposing of the prior data block ... erg.  Embarrassing - but that bug "popped" so rarely that it was hard to trace.  In re-naming the message handles I stumbled on the bug.

So, set up a separate pointer to 'take' the pointer out of the TMessage and "segregate" them...

Pretty lame - (even for me.)  :-[
Title: Re: Immutable data types
Post by: jamie on October 03, 2022, 01:44:34 am
This is why you should get equated with the Interlock..... functions and TcriticalSection types.
Title: Re: Immutable data types
Post by: Warfley on October 03, 2022, 03:41:09 am
When classes are said to be "most simple case" it says a lot :-)
Yes if we are talking about the usage of pointers, classes are the "most simple case" in the sense that it is the most common place. When you create a Lazarus Form application you are bombarded with instances of Buttons, Edits, Forms, etc. So most Lazarus projects use classes everywhere.

But does it mean that const should be removed from Pascal, because it is not hearmetically sealed?
Of course not, this would be really stupid, and I never claimed anything like that. All I did was point out how the immutability concept as introduced in the first post in this thread would not work. And there is a reason for that. As already mentioned above, most FreePascal and Delphi programs are going to be full of classes everywhere. So classes, which basically are nothing but pointers in disguise, are used everywhere. So if an immutability mechanism where to be introduced, if it does not extend to classes (and pointers more generally), it is basically useless, because large parts of most codebases are going to be not covered by this.

This also makes const as it is now in Pascal quite useless as a const mechanism. Take the following example:
Code: Pascal  [Select][+][-]
  1. type
  2.   TIntegerHelper = type helper for Integer
  3.   public
  4.     procedure Inc;
  5.   end;
  6.  
  7. procedure TIntegerHelper.Inc;
  8. begin
  9.   Self += 1;
  10. end;
  11.  
  12. procedure Foo(const i: Integer);
  13. begin
  14.   i.Inc;
  15.   WriteLn(i);
  16. end;
  17.  
  18. begin
  19.   Foo(1);
  20. end.
Result: 2. I just modified a parameter that is "const", just because const does not extend to pointers (the self pointer in this case). Const is a security mechanism, that allows the programmer to create access boundaries to their variables. But, a security mechanism that is not enforcable is useless. This is the programming equivalence from having a key for your front door, while having another key just hanging from the door knob in case you forgot yours.
And the important thing is, here I did it intentionally, but it is easy to see a situation where one person is writing a type (or typehelper) and changes the definition of one method to now alter one of the fields, while the usage, where it is assumed to be constant, is made by a different person. So when the compiler doesn't say: "Hey you are using a method that modifies state on a const object", neither of the developers will probably notice, until they have a very weird bug that they have to spend days in figuring out.

That said, const is not harmfull, it actually has a usecase in that, it allows for optimizations on managed types (like disabling lazy copy checks on strings) when used for parameters. But to enforce immutability of the data it is just plain useless. So no, I don't think it should be removed, I'm just saying that it is pretty useless for enforcing immutability.

Btw, the example above might be categorized as a bug, as you are not supposed to be able to take the pointer of a const, but there is "constref", which basically is a pointer to a constant value, which has the same problem but is more explicelty about it.

What cost would it take to implement "perfectly const" objects in Pascal? Those that only consists of "pure functions" and are disallowed to have public writeable members, friend members and all direct and indirect ways to change their inner state.
No, when I was talking about immutability in this context, I am talking about the immutability of the value of certain data, or you could say to a certain kind of memory (even though this is not necessarily true, as there is not a 1:1 relationship between datatypes in a high level language and memory, just think of register reuse). Basically what the C standard calls an "object" (this term is loaded in Pascal so I try to avoid it).

So no, I don't talk about stateless/pure functions, but that if you have an immutable object, that you cant mutate it. A language that nailed this is actually C++. Consider the following:
Code: C  [Select][+][-]
  1. int j;
  2. struct Test {
  3.   int i;
  4.   void inc() { ++i: }
  5.   void incj() const { ++j; }
  6. };
  7. Test t1 = {1};
  8. Test const t2 = {1};
  9. t1.inc(); // Works fine because t1 is mutable
  10. t2.incj(); // Also works because while t2 is immutable, incj is also immutable
  11. t2.inc(); // error, trying to call non immutable method on immutable data
  12.  
As you can see, incj is not a pure/stateless function, it clearly changes the state of j which is a global variable. But because it is declared as const, the "this" pointer is of type "Test const *", which means it can be called on any Test const object, while inc() changes the state of the "this" pointer and therefore can only be called on a mutable instant of Test.

This has nothing to do with pure functions or anything similar. It is simply that the immutability information is transitively passed through the pointers to this data, in this case the "this" pointer.
If you pass a value as "const", e.g. to another function as parameter, or in any other way, you are saying: "Hey you can read it, but don't touch it, I expect that after you are done this is the exact same value in this data". If you can't trust in this promise, what is it worth?

Quote
I do not want for Pascal to become a jail where doing harm and erros  is absoilutely impossible in no way.
I don't see any merit in allowing things that are clearly errors in any and all cases. For example something like:
Code: Pascal  [Select][+][-]
  1. '1234'[15] := 3.14;
Can never be correct (on multiple levels), and this should never be allowed by the compiler, period. But you are right there are instances in which you want to break the rules, but here I think the important doctrine is, if you do something special, the action to do so should also be special.
Take C++ again, here if you want to access a const object with write, you can, you just need to cast the const away:
Code: C  [Select][+][-]
  1. char const *immutable = str; // Assuming str is mutable so this is not broken
  2. char *mutable = const_cast<char *>(immutable );
By having to take this extra step, you have the programmer explicitly deciding to say "Yes I know I do something dangerous and I am aware of the potential problems".

In my last programming Job we had a rule, whenever a const_cast was in the code, there needed to be an explicit comment explaining exactly why this is necessary. Also the code reviewer would personally ask the programmer about the const_cast and tries to find out if this is really necessary. Only if the code reviewer was satisfied with the answer he would merge the branch into master. If he wasn't satisfied with your answer, you better get working around this.
If you do something special, you better have good reasons to do so

Note that I don't think that C++ does this perfectly, personally I think that immutability by default and mutability annotations (so everything is const unless otherwise specified), is a much better approach. But at least considering what C++ tried to archive, the const mechanism is actually very well thought out and quite useful
Title: Re: Immutable data types
Post by: MarkMLl on October 03, 2022, 08:22:08 am
So if an immutability mechanism where to be introduced, if it does not extend to classes (and pointers more generally), it is basically useless, because large parts of most codebases are going to be not covered by this.

But there's a distinction to be made here, which I believe was touched on a couple of weeks ago when somebody was discussing unit-level properties.

* An instance of a class representing e.g. a form is, of course, writable.

* The variable that contains that instance, which is of course actually a pointer, should be assigned once (at program startup) and treated as immutable until shutdown.

MarkMLl
Title: Re: Immutable data types
Post by: AlanTheBeast on October 03, 2022, 01:53:51 pm
This is why you should get equated with the Interlock..... functions and TcriticalSection types.

If that was addressed to me, the simpler "should" is don't re-purpose TMessages where in one case the .Msg part is cast as a pointer and in the other case cast as a variable.
Title: Re: Immutable data types
Post by: Arioch on October 03, 2022, 09:19:08 pm
And the important thing is, here I did it intentionally, but it is easy to see a situation where one person is writing a type (or typehelper) and changes the definition of one method to now alter one of the fields, while the usage, where it is assumed to be constant, is made by a different person.

Here i have to agree.

However i believe that XE3+ record helpers was abomination anyway. They failed to design proper changes to the compiler and to the type system, so they started to apply what were "ugly hacks for retroactively modifying with closed-source software" to the very foundaiton of RTL.

Whatever is dependent upon simpe type helpers is dependent upon a feature broken by design. You just made one more good case to this.

That said, const is not harmfull, it actually has a usecase in that, it allows for optimizations on managed types

It is. This very optimizatino causes double-free on Delphi XE2 just on conventional long string funciton calls  :-)

Actually there was a long saga between XE2 and XE6 about codegeneration for long string expressons. It was... killing one optimization after another and making simpl Pascal statements produce huge sheets of asm code.

But to enforce immutability of the data it is just plain useless.
So no, I don't think it should be removed, I'm just saying that it is pretty useless for enforcing immutability.

I think i gave you a case.

once-mutable variables to me woul be exactly as much protecting adnd exactly as much leaky as const is.

if you agree that const is - with all the holes - a useful tool to protect you from yourself, then so would be values.

Quote from: Warfley
you are saying: "Hey you can read it, but don't touch it, I expect that after you are done this is the exact same value in this data"

I wonder if you would then try to take pointer to the this->i
Okay, naive attempt would probaly fail, as you would need a "pointer to const" to take i's address (hopefully, don't want to go godbolt). But add a bit of union{...}

All in all, this more fine-grained const distinction in C++ is really nice to had, but retroactively introducing it made their reverse-directin type declaration yet worse...
I can only repeat, there are caseswith D/FPC when i miss it, but those cases are rare.
Title: Re: Immutable data types
Post by: MarkMLl on October 03, 2022, 10:06:31 pm
And the important thing is, here I did it intentionally, but it is easy to see a situation where one person is writing a type (or typehelper) and changes the definition of one method to now alter one of the fields, while the usage, where it is assumed to be constant, is made by a different person.

Here i have to agree.

As do I, for what it's worth. However if I could throw in a recollection from years ago: I was actually at a conference, decades ago, where a somebody presented a paper describing a methodology that he was using to teach Modula-2 (described as some variant of "software hut", if memory serves). It was some variant of:

* Divide a class into teams, each with three subteams.

* Each subteam writes the definition module for part of the assignment.

* A different subteam writes the implementation module.

* The third subteam has the responsibility for integration and debugging.

The language enforced robust equivalence between implementation and definition, and the development environment prevented anybody other than the assigned subteam from changing module content.

So at the very least it's possible to say that there's precedent for having the IDE and compiler detect inconsistencies (and for LARTing students until they appreciate that the tools are trying to help rather than hinder).

MarkMLl
Title: Re: Immutable data types
Post by: Warfley on October 03, 2022, 11:50:55 pm
However i believe that XE3+ record helpers was abomination anyway. They failed to design proper changes to the compiler and to the type system, so they started to apply what were "ugly hacks for retroactively modifying with closed-source software" to the very foundaiton of RTL.

Whatever is dependent upon simpe type helpers is dependent upon a feature broken by design. You just made one more good case to this.
I must admit, I absolutely do not care about Delphi, I last used Delphi over a decade ago. In FreePascal type helpers (something that does not even exist in Delphi) are alive and well. But you can also change this to advanced records to make the same point.

Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = record
  3.     i: Integer;
  4.     procedure Inc;
  5.   end;
  6.  
  7. procedure TTest.Inc;
  8. begin
  9.   Self.i += 1;
  10. end;
  11.  
  12. procedure Foo(const t: TTest);
  13. begin
  14.   t.Inc; // Works
  15.   t.i += 1; // Error: Can't assign values to const variable
  16. end;

If you are coming from Delphi than you might also not know about advanced records, as they are one of the newer features of freepascal, basically a newer spin on old style objects but without inheritance.

if you agree that const is - with all the holes - a useful tool to protect you from yourself, then so would be values.
The problem with const is, it is not reliable. It works if you have primitive datatypes, with no type helpers or non advanced records. But anything else can circumvent the constness. And the main concern here is not just that you can circumvent constness, because there are ways to always circumvent constness. The problem is that the constness is not propagated through, so you loose the constness in a totally different place.

Take again the type helper example from above (or the advanced record if you don't like type helpers), the constness is not lost in the function that gets the const record, it is lost in the function called on the type helper. This function is potentially in a completely different part of the code, written by a completely different person, and from your point of view as a user you can assume it to be a black box.

So you must assume that whenever you call such a method, which looses constness information, that the data could have been altered. The problem is, it only requires one hole to not be reliable anymore. As soon as any hole is introduced, the usage of the functionality with that hole will basically mean that all assumptions of validity are gone.

I wonder if you would then try to take pointer to the this->i
Okay, naive attempt would probaly fail, as you would need a "pointer to const" to take i's address (hopefully, don't want to go godbolt). But add a bit of union{...}
Yes it would be a pointer to a const. Just on a side note about union, actually according to the C standard, the behavior of accessing different fields of a union is undefined, meaning you can only read from the field that you assigned the value to. Shure most compilers simply overlay the memory, but strictly speaking you can't assume that, and the usage of unions to convert data representation is not valid C code. Same holds btw. for pointer casting between types, but you can cast away the const. Standard C does not intend for the programmer to convert between different representations of the same data (as this code is inherently unportable).

C is actually really strict in this respect, but this is a bit off topic I think.

All in all, this more fine-grained const distinction in C++ is really nice to had, but retroactively introducing it made their reverse-directin type declaration yet worse...
I don't propose this, this was just a thought experiment that if one would seriously want to have a concept of constant/assign once data, then this must be incorporated in the typesystem. That said, you can't just change the typesystem of an existing language. FreePascal with the typesystem of C or C++ would not be FreePascal anymore. So maybe this would be interesting for anyone trying to design a new language on the basis of Pascal with some new features, but it's not something that could be added into an existing language.
Title: Re: Immutable data types
Post by: Arioch on October 04, 2022, 01:09:39 am
In FreePascal type helpers (something that does not even exist in Delphi) are alive and well.

But you can also change this to advanced records to make the same point.

I don't see how it is the same point. You just made an incapsulated object(record) with an exlicit mutating method.
Basically, you reiterated that FPC/Delphi does not have "immutable objects" and calling any method might change the internal state.

But we all know it and we do not expect it otherwise. You called a method - you are on its mercy now.
Here is no broken expectations, at least no more than usual.

Quote
from your point of view as a user you can assume it to be a black box.

This!

=====================

On helpers I'll have to see. My current understanding (shallow glance) is that FPC type helpers is the same as Delphi record helpers just renamed. Plus, FPC does not prohibit several different helpers be active at once. And so, i did not look any deeper.

Would those be compiler intrinsics, so that no one but compiler makers could create those helpers - it would've been okay. One compiler magic more, one less, no big deal. But they are not.

With people encouraged to make their own helpers - they have either go into self-censorship, identifying this feature as dangerous, or end up having different helpers in different libraries for the same type.

So they get the unpredictability of big (actually - unit-big) WITH clause. Some place up there  far away, visibly unrelated, included some unit - and suddenly he identifiers you use in your code changed meaning. Hopefully thy would stop compiling, worse if they would compile yet change meaning.

Delphi's restriction "only one helper" pessimizes creating your own helpers for types RTL already mase helpers for. But not prohibit it.
Multiple helpers mode of FPC does not even have it.

Imagine i have two libraries, both interface database of cars, but differnent datasets: one has information for different plate numbers (as road police gathers, "instances" of cars), and other - for different models (as shops/commercials do, "classes" of cars),

While i only had one library, it was cool, fancy and fashionably pythonish.

Code: Pascal  [Select][+][-]
  1.   UsedCar := 'VX-10574-RU'.GetCar;
  2.  

Code: Pascal  [Select][+][-]
  1.   CarModel := 'Volvo X5 Black'.GetCar;
  2.  

Cuule, man, aintah?

But of course, i might want to integrate both datasets.
If my program user looks to buy a used car on esome -bay, then he would benefit from oth reading reviews of the car model, and reading "police story" about this specific instance, maybe it was into accidents.
So, i throw both units, accessing both datasets into my uses clause....

...and what does string.GetCar even mean now?

And here we go back to your earlier statement:

Quote
it is easy to see a situation where one person is writing a type (or typehelper) and changes the definition of one method to now alter one of the fields, while the usage, where it is assumed to be constant, is made by a different person

Type helpers promote sudden and global change of the working code, without actually the sources of that code being touched.

Imagine i was lucky to have a lucky sequency in my uses.

Code: Pascal  [Select][+][-]
  1. uses .... CarHistoryDB, CarMagazineReviews, ....
  2.  
  3. ....
  4.  
  5.   CarModel := 'Volvo X5 Black'.GetCar;
  6.  

Works like a charm.

Then the CarMagazineReviews library team decided they made bad name, ambiguos if not misleading. And they cheanged their helper to be publish GetReviews instead.

Oooops, suddenly my old polished ocde i did not touch for 5 years changed the meaning and (hopefully) compiles no more. But if both heplers were returning IUnknown and being fancy trendy libraries provided OLEAuto runtime-binding interface... Ooooops - it only gives "catastrophic call failure" later in the runtime.

And then there was another string helpers, that also published a function string.GetReviews - but those were reviews of parking lots for tourists.
Those calls suddenly got broken too!

 %)

Good luck with this mess...
Title: Re: Immutable data types
Post by: Warfley on October 04, 2022, 01:47:58 am
I don't see how it is the same point. You just made an incapsulated object(record) with an exlicit mutating method.
Basically, you reiterated that FPC/Delphi does not have "immutable objects" and calling any method might change the internal state.

But we all know it and we do not expect it otherwise. You called a method - you are on its mercy now.
Here is no broken expectations, at least no more than usual.
Yes it broke the expectation, the parameter is declared as const, therefore the expectation is that it is immutable. But the fact that by calling a method it can be mutated is breaking this expectation. And this should not be possible. In fact, with non method functions this is actually the case:
Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = record
  3.     i: Integer;
  4.     procedure Inc;
  5.   end;
  6.  
  7. procedure TTest.Inc;
  8. begin
  9.   Self.i += 1;
  10. end;
  11.  
  12. procedure IncTest(var Self: TTest);
  13. begin
  14.   Self.i += 1;
  15. end;
  16.  
  17. procedure Foo(const t: TTest);
  18. begin
  19.   t.Inc; // Works
  20.   IncTest(t); // Error: Can't assign values to const variables
  21. end;
Conceptually the method and the function should be the same, and infact they are, a method simply is a function with a hidden self var parameter as first parameter. But they work differently. The reason is that the const system works ok-ish for regular functions but does not extend to methods.

If I say that something is immutable with the const keyword, I would expect that I am not able to mutate it. The fact that I can do this, no matter if it's through a method, a function, an operator or by doing a performative dance in front of my pc, breaks this expectation. It's called "const" not "ConstUnlessYouCallAMethodOnIt", so I expect it to be constant

...and what does string.GetCar even mean now?
This is the classical diamond problem from multi inheritance. And the solution is extremely simple, throw an ambiguity error and let the user resolve it manually:
Code: Pascal  [Select][+][-]
  1. TMyHelper1(instance).Method; // Call Method from Helper1
  2. TMyHelper2(instance).Method; // Call Method from Helper2
This works for Multiinheritance in languages like C++. For type helpers specifically, these are a staple of the swift programming language, where this is also resolved similarly. This "problem" was solved decades ago.

Quote
Oooops, suddenly my old polished ocde i did not touch for 5 years changed the meaning and (hopefully) compiles no more. But if both heplers were returning IUnknown and being fancy trendy libraries provided OLEAuto runtime-binding interface... Ooooops - it only gives "catastrophic call failure" later in the runtime.
If the library you are using changes it's interface of course it effects your code. But this is not a property of type helpers, but of any function. Just imagine like:
Code: Pascal  [Select][+][-]
  1. CarModel := GetCar('Volvo X5 Black');
You have exactly the same problem. Typehelpers are just syntactic sugar, which converts functions of the form "Verb(subject, objects)" into the method form of "subject.verb(objects)". Semantically it is identical and therefore, as this is a completely non issue in the former case, it also is in the latter.

I mean yes this is actually sometimes a kindof problem when considering overloaded functions. This is exactly why C does not allow overloaded functions, but C++ does, because while the C developers see the "risk" of allowing this ambiguity to be to high, C++ doesn't. Pascal allows for overloaded functions, and this won't change probalby, so there is no reason to not allow multiple type helpers, because it's the exact same situation just with a different syntax.
Title: Re: Immutable data types
Post by: MarkMLl on October 04, 2022, 08:50:39 am
The problem with const is, it is not reliable. It works if you have primitive datatypes, with no type helpers or non advanced records. But anything else can circumvent the constness. And the main concern here is not just that you can circumvent constness, because there are ways to always circumvent constness. The problem is that the constness is not propagated through, so you loose the constness in a totally different place.

Well that's simple enough: either don't have type helpers etc. or stop calling the language Pascal.

We keep getting told "we won't implement such-and-such since it's not in the spirit of Pascal", but breaking features of the language which have been in there ab initio really is taking things too far.

MarkMLl
Title: Re: Immutable data types
Post by: Warfley on October 04, 2022, 01:29:10 pm
We keep getting told "we won't implement such-and-such since it's not in the spirit of Pascal", but breaking features of the language which have been in there ab initio really is taking things too far.

MarkMLl
Thats actually a good point, const parameters is an original Pascal concept, long before any classes, objects, type helpers advanced records or any other form of method calling syntax. And in this setting it works fine, there still are pointers, but afaik you can't take the pointer from a const param. The problem is that when all this OOP stuff was added (i.e. objects), no one seemed to care about how constness effects those systems, and this design decision went through all the other OOP features added later.

It should also be noted that Pascal does not have a concept of constant data in general, but only constant parameters, so from this angel, a typesystem that incorporates assign once data (like C, or what was originally proposed in the first post of this thread), is not required, because there never was the intention to have constant/assign once variables in pascal to begin with.

That said, I think the current const parameter system could be improved, by simply having the compiler do a static analysis if a method can alter the state of the "self" parameter, and if this method is called on a const, either throw a warning or even an error. This would not require any changes to the language and would rule out the problems I showed above
Title: Re: Immutable data types
Post by: MarkMLl on October 04, 2022, 02:47:39 pm
Thats actually a good point, const parameters is an original Pascal concept, long before any classes, objects, type helpers advanced records or any other form of method calling syntax. And in this setting it works fine, there still are pointers, but afaik you can't take the pointer from a const param. The problem is that when all this OOP stuff was added (i.e. objects), no one seemed to care about how constness effects those systems, and this design decision went through all the other OOP features added later.

I was actually thinking about constant declarations at the start of a program or function (which can be broken if their address can be taken), but const parameters are obviously approximately equivalent (equivalent from the POV of semantics, if not from the memory map).

However as I pointed out earlier in either this thread or a parallel one: Object Pascal already has an inherent problem in that objects (i.e. pointers to instances of classes) are mutable even if the content of the instance is carefully protected at the semantic level.

MarkMLl
Title: Re: Immutable data types
Post by: Warfley on October 04, 2022, 03:16:37 pm
I was actually thinking about constant declarations at the start of a program or function (which can be broken if their address can be taken), but const parameters are obviously approximately equivalent (equivalent from the POV of semantics, if not from the memory map).
Yes, but here it must be distinquished between real consts and the "writable consts" (which is a fun little oxymoron if you ask me) which are just global variables whos scope is restricted to the respective function
Title: Re: Immutable data types
Post by: MarkMLl on October 04, 2022, 03:29:56 pm
I was actually thinking about constant declarations at the start of a program or function (which can be broken if their address can be taken), but const parameters are obviously approximately equivalent (equivalent from the POV of semantics, if not from the memory map).
Yes, but here it must be distinquished between real consts and the "writable consts" (which is a fun little oxymoron if you ask me) which are just global variables whos scope is restricted to the respective function

Yes, agreed. But I don't think that /anybody/ really likes the "writable consts" terminology: even if they feel obliged to defend it from the POV of Borland compatibility.

MarkMLl
Title: Re: Immutable data types
Post by: Warfley on October 04, 2022, 03:41:50 pm
My theory is that this was an april fools joke by someone at borland, who thought the name "writable constants" was a dead givaway, but nobody else noticed it and implemented it anyway.
Title: Re: Immutable data types
Post by: marcov on October 04, 2022, 03:47:30 pm
My theory is that this was an april fools joke by someone at borland, who thought the name "writable constants" was a dead givaway, but nobody else noticed it and implemented it anyway.

Borland Pascal allowed to write structured constants. Keep in mind that it was originally a 16-bit real mode compiler, and probably not as rigid due to memory limitations.

In later 16-bit PM versions, borland implemented $J to govern this, and Delphi also had a long {$writeableconst+/-} option for it.

Title: Re: Immutable data types
Post by: MarkMLl on October 04, 2022, 04:16:24 pm
Borland Pascal allowed to write structured constants. Keep in mind that it was originally a 16-bit real mode compiler, and probably not as rigid due to memory limitations.

8-bit: it was in v3. "A typed constant may be used exactly like a variable of the same type".

I've got nothing against the concept, but continued use of the name in modes that don't claim to be 100% Borland-compatible is indefensible.

MarkMLl
Title: Re: Immutable data types
Post by: lainz on October 04, 2022, 04:31:18 pm
So what's the difference between a typed constant and a var?

It's stored differently in the executable? Is faster? Is cool?
Title: Re: Immutable data types
Post by: MarkMLl on October 04, 2022, 04:37:59 pm
So what's the difference between a typed constant and a var?

It's stored differently in the executable? Is faster? Is cool?

If declared in a function it's persistent across function invocations. As such it behaves like a global variable but with limited scope.

MarkMLl
Title: Re: Immutable data types
Post by: AlanTheBeast on October 04, 2022, 04:49:19 pm

As do I, for what it's worth. However if I could throw in a recollection from years ago: I was actually at a conference, decades ago, where a somebody presented a paper describing a methodology that he was using to teach Modula-2 (described as some variant of "software hut", if memory serves). It was some variant of:

* Divide a class into teams, each with three subteams.

* Each subteam writes the definition module for part of the assignment.

* A different subteam writes the implementation module.

* The third subteam has the responsibility for integration and debugging.

The language enforced robust equivalence between implementation and definition, and the development environment prevented anybody other than the assigned subteam from changing module content.

F-22 avionics integration was allegedly the least problematic of any such integration to that point.

Language:  Ada.  Companies involved trades their program interfaces ("specification") freely and clearly while keeping the implementation to themselves.  No ambiguity.

It's germane to note that between the dozens of avionics vendors on the program, the use of the internet made sure (even in those early days) that everyone had up-to-date information most of the time.  So whether there were several CSCI's in a single computer or talking across the "network" (not really, it was mainly MIL-STD-1553B).[1]

Also where " for i := 1 to 10 do .......... ;" scope limited variables could exist - despite Ada being greatly Pascal influenced.

I actually took an easy "A" class in university: Modula II - but it wasn't taught as you describe - taught the usual "you're on your own" manner.

(Took the Ada class at work).

[1]  (This wasn't the case before that - I had dozens of pre-release ARINC-429 interface descriptions for various programs that still had "author comments" all over them. These were often traded around companies as photocopies ... the h/w interface doc (429) often had higher level docs incorporated and v-v).
Title: Re: Immutable data types
Post by: Bogen85 on October 04, 2022, 04:55:21 pm
So what's the difference between a typed constant and a var?

It's stored differently in the executable? Is faster? Is cool?

const parameters to functions and procedures are still local of course, unlike const in the declaration section.
Title: Re: Immutable data types
Post by: Thaddy on October 04, 2022, 05:21:07 pm
NO. That depends on J-/+. If {J+} is the case they are not stored local, but global, with the provision they are in local SCOPE.
Title: Re: Immutable data types
Post by: Bogen85 on October 04, 2022, 05:39:53 pm
NO. That depends on J-/+. If {J+} is the case they are not stored local, but global, with the provision they are in local SCOPE.

Are you saying that in an argument list
Code: Pascal  [Select][+][-]
  1. procedure some_proc (const a: integer; constref b: someRecordType);
that a and b are not always local to some_proc?

Of course the constref parameter as part of the caller's scope as well as far as access).
Title: Re: Immutable data types
Post by: Bogen85 on October 04, 2022, 05:54:44 pm
NO. That depends on J-/+. If {J+} is the case they are not stored local, but global, with the provision they are in local SCOPE.

https://wiki.freepascal.org/$writableConst

I'm not referring to the const declaration section after the parameter list, which what {$J} seems to be for.

I'm talking about these: https://www.freepascal.org/docs-html/ref/refsu67.html
"In addition to variable parameters and value parameters Free Pascal also supports Constant parameters"
"Specifying a parameter as Constant is giving the compiler a hint that the contents of the parameter will not be changed by the called routine."

It would make little sense for those to be global or static.

Title: Re: Immutable data types
Post by: marcov on October 04, 2022, 06:10:48 pm
However i believe that XE3+ record helpers was abomination anyway. They failed to design proper changes to the compiler and to the type system, so they started to apply what were "ugly hacks for retroactively modifying with closed-source software" to the very foundaiton of RTL.

The opposite is accepting every suggested expansion, a disease that FPC suffers from. Everything in language design is a matter of balance. Quite often between usability in production and design purity

And the usability can be subdivided into various user classes (corporate vs SME)
Title: Re: Immutable data types
Post by: marcov on October 04, 2022, 06:22:50 pm
So at the very least it's possible to say that there's precedent for having the IDE and compiler detect inconsistencies (and for LARTing students until they appreciate that the tools are trying to help rather than hinder).

Or you could see it as a case against using production tools for teaching.

We were pestered with Hatley-Pirbhai top down development (iirc partially because Phillips Medical heavily used it). Good exercise, but as methodology highly specialized, and pretty useless in daily practice where specs are rarely so fixed and up front.

In reality, the chance is higher that my boss tells me to send a customer something, and it turns out the specification process for what to send hasn't even started, and the intake was just the customer venting about his problems without much analysis.
Title: Re: Immutable data types
Post by: circular on October 05, 2022, 04:23:36 am
const parameters to functions and procedures are still local of course, unlike const in the declaration section.
The names are local indeed. I suppose the location of the value can depend though if it is passed by reference or not (this is decided by the compiler or if you use constref keyword). If it is in fact a reference, it is like using the var keyword while avoiding to modify it.
Title: Re: Immutable data types
Post by: circular on October 05, 2022, 04:27:42 am
If declared in a function it's persistent across function invocations. As such it behaves like a global variable but with limited scope.
So it is like the static keyword in C. Or the class var keywords in Pascal classes. I suppose we could call it function var ?
Title: Re: Immutable data types
Post by: Bogen85 on October 05, 2022, 04:38:51 am
const parameters to functions and procedures are still local of course, unlike const in the declaration section.
The names are local indeed. I suppose the location of the value can depend though if it is passed by reference or not (this is decided by the compiler or if you use constref keyword). If it is in fact a reference, it is like using the var keyword while avoiding to modify it.

Agreed. However, there is still "some" compile time enforcement with constref, as with const (with all the caveats already discussed).

Many would consider "some" compile time better than none (as I do), especially with the understanding of how the enforcement could be bypassed, so that this should not be considered the only thing to rely on when dealing with concurrency situations (and possibly some reentrancy situations as well).
Title: Re: Immutable data types
Post by: MarkMLl on October 05, 2022, 08:45:43 am
So it is like the static keyword in C. Or the class var keywords in Pascal classes. I suppose we could call it function var ?

Generally speaking it would be referred to elsewhere in the industry as static. It's a slight extension of ALGOL own variables, since while typed they didn't have an initialiser.

MarkMLl
Title: Re: Immutable data types
Post by: PascalDragon on October 05, 2022, 08:55:47 am
Borland Pascal allowed to write structured constants. Keep in mind that it was originally a 16-bit real mode compiler, and probably not as rigid due to memory limitations.

8-bit: it was in v3. "A typed constant may be used exactly like a variable of the same type".

I've got nothing against the concept, but continued use of the name in modes that don't claim to be 100% Borland-compatible is indefensible.

The concept doesn't differ between the modes and it needs some name. So it can just as well have the name from where it originated from: “typed constants”.

NO. That depends on J-/+. If {J+} is the case they are not stored local, but global, with the provision they are in local SCOPE.

const parameters are not governed by this switch.
Title: Re: Immutable data types
Post by: SymbolicFrank on October 05, 2022, 09:20:59 am
If I want an immutable value, I use a read-only property. But considering this discussion, it seems that I don't get it. So why doesn't that work?
Title: Re: Immutable data types
Post by: MarkMLl on October 05, 2022, 09:39:10 am
If I want an immutable value, I use a read-only property. But considering this discussion, it seems that I don't get it. So why doesn't that work?

It's a comparatively new industry term, and represents a variable which may be written to (precisely) once.

MarkMLl
Title: Re: Immutable data types
Post by: SymbolicFrank on October 05, 2022, 09:51:57 am
It's a comparatively new industry term, and represents a variable which may be written to (precisely) once.

MarkMLl
Yes, I know. From functional languages, but different.

Like querying a read-only database: you can only create new values by mutating old ones. Like, a view with one or more calculated fields that reference other fields. Or, like Lisp.

Generally, if you have a read-only property, you set it once, in the constructor. The constructor has a formula to calculate it from other values. Once.

Sounds like the same thing to me.
Title: Re: Immutable data types
Post by: MarkMLl on October 05, 2022, 09:57:57 am
Yes, I know. From functional languages, but different.

Like querying a read-only database: you can only create new values by mutating old ones. Like, a view with one or more calculated fields that reference other fields. Or, like Lisp.

Generally, if you have a read-only property, you set it once, in the constructor. The constructor has a formula to calculate it from other values. Once.

Sounds like the same thing to me.

(Shrug) it's computersciency crap. However, I'd highlight the particular case of (the pointer to) (the object representing) a form as implemented by Lazarus: creation and initialisation are distinct, and should be allowed to happen once only.

MarkMLl
Title: Re: Immutable data types
Post by: SymbolicFrank on October 05, 2022, 10:13:09 am
I think the main distinction is, that while you can emulate the functionality with a read-only property, that isn't the only use-case for them. Like, you can make a read-only 'Count' property, that returns the amount of values in a list. It's up to the programmer, the compiler doesn't enforce it.

And the idea about using anonymous functions is to emulate how languages like Haskell do it: you store the mutations required to get from the old value(s) to the new one. Then again, you can also use a regular function to do that, of course.
Title: Re: Immutable data types
Post by: Bogen85 on October 05, 2022, 03:04:03 pm
I think the main distinction is, that while you can emulate the functionality with a read-only property, that isn't the only use-case for them. Like, you can make a read-only 'Count' property, that returns the amount of values in a list. It's up to the programmer, the compiler doesn't enforce it.

And the idea about using anonymous functions is to emulate how languages like Haskell do it: you store the mutations required to get from the old value(s) to the new one. Then again, you can also use a regular function to do that, of course.

Agreed. Which is what I stated earlier.

The reason why I say that anonymous functions solves this problem, is because my original source code musings from this initial post can use the following (now supported in FPC) to achive immutable variable assignments that the compiler enforces (but yes, as others have stated, could be bypassed with pointers.)

And it does not always need to be anonymous functions. It can be named nested functions. But the point is that const var parameters satisfy cursory compiled enforced immutability and by leveraging them one can cut down the required number of var declarations overall.

And obviously with top level unit functions. All depends on scoping needs.

Where anonymous functions are useful in this mix is for custom iterator and other types of callbacks where what is passed into the anonymous functions "callback" is received as const var parameters. (As I discussed earlier).
Title: Re: Immutable data types
Post by: Bogen85 on October 05, 2022, 07:36:21 pm
And of course I'm fully aware that the const function parameter compile time checking is shallow, and not deep.
Title: Re: Immutable data types
Post by: Arioch on October 05, 2022, 11:28:57 pm
Generally, if you have a read-only property, you set it once, in the constructor. The constructor has a formula to calculate it from other values. Once.

Sounds like the same thing to me.

...do you by chance create ad hoc object with a modifiable property every time you need a variable?

Sounds like the same thing to me.
Title: Re: Immutable data types
Post by: Warfley on October 06, 2022, 04:39:23 pm
Yes, I know. From functional languages, but different.

Like querying a read-only database: you can only create new values by mutating old ones. Like, a view with one or more calculated fields that reference other fields. Or, like Lisp.
The concept of immutable datatypes as in functional languages, is a bit different than what usually is thought of with immutability in imperative languages. Functional languages like Haskell are stateless (at least the pure ones), where the whole program just consists of expressions. As you don't have single instructions there is no such thing as state that can be changed, and all you can do is take values as input and put them into functions that produce an output.
This is also a common paradigm when working in imperative languages when working with atomic types. Take this example:
Code: Pascal  [Select][+][-]
  1. x := 3 + 4 * 2;
You could rewrite this as:
Code: Pascal  [Select][+][-]
  1. x.SetValue(4);
  2. x.Mul(2);
  3. x.Add(3);
While with type helpers this would be possible, nobody does this, so atomic types in Pascal are mostly immutable, in the sense that you usually don't alter the state of an object but create a new object from an expression. Note that the following is also fllowing this paradigm:
Code: Pascal  [Select][+][-]
  1. x := 1;
  2. for i:=0 to 10 do
  3.   x := x * i;
Even though x is changed, the objects itself are still immutable, because every iteration a new object is created and "just given the same name".

This might seem overcomplicated, or as MarkMLI put it "computersciency crap" when considering atomic types, but this is actually very useful for writing readable code. For example when instead of using integers, we consider the gmp unit. The computation above (3 + 4 * 2) completely using GMP types looks like this:
Code: Pascal  [Select][+][-]
  1. var
  2.   x: mpz_t;
  3. begin
  4.   mpz_init_set_ui(x, 4);
  5.   mpz_mul_ui(x, x, 2);
  6.   mpz_add_ui(x, x, 3);
But the GMP unit also provides wrapper types that are designed to be treated as immutables, by creating temporary objects:
Code: Pascal  [Select][+][-]
  1. var
  2.   x: MPInteger;
  3. begin
  4.   x := MPInteger(4) * 2 + 3;
This is much more intuitive to read than the former, and is completely immutable, MPInteger(4) creates a new object, *2 than takes this object and multiplies it by 2 and writes the result in a new object, which is then added with 3 to the final object that is then written into x. During the whole process no Object was modified after it's creation, and it's arguably better readable code.

Also a lot of the classical pascal functions are not following this paradigm:
Take the following:
Code: Pascal  [Select][+][-]
  1. Inc(x, 3);
  2. // vs
  3. x := x + 3;
  4. // Or
  5. Delete(str, 0, 3);
  6. // vs
  7. str := str.Substring(3);
I personally find this paradigm to produce much more readable code, because the state change is made explicit. When writing str := ... I know just from looking at that, that afterwards str will be something different, while with Delete I have to know that function to know that it changes str. I think that generally var and out parameters should be avoided as much as possible to make any changes explicit and directly spottable.
It also means that there is only one way to change things, if there is a := the variable changes, if not, than you can assume it will still be the same


But these are immutable datatypes, but what the discussion here was about are immutable variabels, to cover also pointer to such variables, immutable references.
Immutable references are different in the sense that you can basically ensure that if you "lend" someone an object, that it will come back unchanged. For example, assume you have a config class, which is used by many other classes to read out things like directories, user config, etc. Simultaniously you might not want to create a copy of said configuration for all cases (e.g. when the user should be able to change said config it would not be wise to have to propagete that change throughout all the classes again).
Therefore if you have a method of making immutable references, you can signal to the code getting this reference that it can be read, but that it is probably not a unique copy and therefore changing it might have other consequences outside the current bit of code. It's a form of documentation, the violation of which is enforced by the compiler.

Of course this can be used with immutable datatypes, but the core difference here is, that you can have an immutable references to mutable object. In the config example above, there might be a class which can actually change the config, e.g. some config editor form or something similar, so the class must be mutable. So we have multiple references to the same object, some mutable, some immutable, while immutable datatypes will always produce immutable.

So I think it is important to distinquish here. Immutable datatypes have their advantages, but they are generally a design paradigm which you have to alter your code around. Immutable references on the other hand are just a security mechanism to enforce assumptions made on the code, but generally the code would look identical if you removed the immutability (e.g. if you have a const variable in c and you remove the const everywhere, the code will work exactly the same, you just have less compiler checking if your ownership assumptions hold true)
Title: Re: Immutable data types
Post by: MarkMLl on October 06, 2022, 05:05:11 pm
This might seem overcomplicated, or as MarkMLI put it "computersciency crap"

At last! The recognition I have always craved!!! :-)

Quote
So I think it is important to distinquish here. Immutable datatypes have their advantages, but they are generally a design paradigm which you have to alter your code around. Immutable references on the other hand are just a security mechanism to enforce assumptions made on the code, but generally the code would look identical if you removed the immutability (e.g. if you have a const variable in c and you remove the const everywhere, the code will work exactly the same, you just have less compiler checking if your ownership assumptions hold true)

The bottom line, however, is that Pascal does purport to still be a systems programming language, and as such to provide datatypes and operations with a relatively straightforward mapping onto the underlying architecture. And while I am obviously aware of various machines with dataflow architectures (the best known and arguably most successful being the HAL Series) and am enthusiastic about the potential of atomic message-passing at the microkernel or hardware level I think the emphasis should be on languages such as Pascal /enabling/ or /supporting/ this style of programming rather than advertising it as a significant and immediately-accessible extension.

And I have already made the point about one area that cries out for immutable references being public storage of (objects representing) Lazarus/LCL forms.

MarkMLl
Title: Re: Immutable data types
Post by: marcov on October 06, 2022, 05:24:00 pm
I think inc() is more an intrinsic that stuck in the language than a real feature.

It would like judging C badly for their ++ and -- operators (that were basically PDP intrinsics).

Intrinsics sometimes get legalized into the language; it happens, but it is never elegant.
Title: Re: Immutable data types
Post by: MarkMLl on October 06, 2022, 05:38:50 pm
It would like judging C badly for their ++ and -- operators (that were basically PDP intrinsics).

Let's face it, very few people defend ++ and -- and arguably they- like most if not all direct pointer manipulation- should really be reserved for competent programmers.

One thing I would add though is that apparently ++ and -- in C predate the PDP series. I can't provide an immediate reference for this, but my recollection is that the first version of UNIX was written in quasi-assembler which included comparable operations despite the target system not implementing them directly, and the result was that when C was presented as a high-level replacement it really had to include the same facilities.

MarkMLl
Title: Re: Immutable data types
Post by: marcov on October 06, 2022, 06:03:45 pm
One thing I would add though is that apparently ++ and -- in C predate the PDP series.

The B language wikipedia page seems to say something like that. One could argue that K&R might have had advance knowledge, etc etc, but lets not go there.

Let's just turn it around, where is the rationale for ++/-- then?
Title: Re: Immutable data types
Post by: MarkMLl on October 06, 2022, 06:27:42 pm
The B language wikipedia page seems to say something like that. One could argue that K&R might have had advance knowledge, etc etc, but lets not go there.

The Wp page on Unix explicitly says that early versions of unix were coded in assembler, but I think I've seen more detail than that somewhere. I'd speculate- without proof- that Thompson had atomic "decrement-compare" and "compare-increment" macros to implement P/V semaphores, and that these had been repurposed for extensive other use.

Quote
Let's just turn it around, where is the rationale for ++/-- then?

The canonical C example codes make much of concise pointer operations with ++ or (less often) --, assumption that nil equates to zero hence false, and so on. But Sven's gone to some trouble to educate me as to how, as the language has been gradually tightened up, those operators have been recognised as problematic.

MarkMLl
Title: Re: Immutable data types
Post by: Bogen85 on October 07, 2022, 04:35:15 am
Quote
Let's just turn it around, where is the rationale for ++/-- then?

The canonical C example codes make much of concise pointer operations with ++ or (less often) --, assumption that nil equates to zero hence false, and so on. But Sven's gone to some trouble to educate me as to how, as the language has been gradually tightened up, those operators have been recognised as problematic.

MarkMLl

Yeah... so people can write:
Code: C  [Select][+][-]
  1. while(*a++ = *b++); // and that is obvious to others what that means???
Back before optimizing compilers.... maybe it was needed to produce optimal code...

Certainly not needed with any decent modern C compiler... (in the past 2-3 decades, maybe longer)
Title: Re: Immutable data types
Post by: 440bx on October 07, 2022, 05:13:40 am
Yeah... so people can write:
Code: C  [Select][+][-]
  1. while(*a++ = *b++); // and that is obvious to others what that means???
Back before optimizing compilers.... maybe it was needed to produce optimal code...

Certainly not needed with any decent modern C compiler... (in the past 2-3 decades, maybe longer)
only *b equal zero will make that loop end, if it isn't zero at the start of the while it will go on until *b overflows (which for a qword, is for practical purposes, close to an infinite loop.)

On a different note, there are plenty of modern C compilers out there but, I've never seen one that comes remotely close to being decent.
Title: Re: Immutable data types
Post by: Bogen85 on October 07, 2022, 05:50:46 am
Yeah... so people can write:
Code: C  [Select][+][-]
  1. while(*a++ = *b++); // and that is obvious to others what that means???
Back before optimizing compilers.... maybe it was needed to produce optimal code...

Certainly not needed with any decent modern C compiler... (in the past 2-3 decades, maybe longer)
only *b equal zero will make that loop end, if it isn't zero at the start of the while it will go on until *b overflows (which for a qword, is for practical purposes, close to an infinite loop.)


But it is better to use the builtin, optimized, and tested strcpy, strncpy, and so forth.

Especially strncpy for the reasons you stated above...

But the above example is just as unsafe as strcpy for copying what is supposed to be a null-terminated string using char pointers...
And even strncpy is not safe... (if the source is not null-terminated...)

And for large copies, just keep your data in struct variables, and pass those around, as the compiler and builtin functions produce better block copy code than trying to roll your own...

Point being that unless doing embedded systems or drivers most of the lower level features of C are not really needed.

But discussing C to this detail is likely a bit offtopic for this forum... (and the subject...)  :) 8-)

Title: Re: Immutable data types
Post by: alpine on October 07, 2022, 09:16:09 am
One thing I would add though is that apparently ++ and -- in C predate the PDP series.

The B language wikipedia page seems to say something like that. One could argue that K&R might have had advance knowledge, etc etc, but lets not go there.

Let's just turn it around, where is the rationale for ++/-- then?
(My apologies for being off-topic)

AFAIK in the early days, when C was still in a development, it turned out that ++x compiled more tightly than x=x+1 and perhaps that was just enough to embrace it. At that time the C syntax wasn't so well established (shorthand assignments were =+, =- instead of +=, -=, typeless variables, etc.). Later, some C statements could be represented by a just a single instruction - e.g. on the PDP-11/VAX/M68K.

For example:
Code: C  [Select][+][-]
  1.   *--ptr; // maps to @-(Rn) addressing mode
  2.   *ptr++; // maps to @(Rn)+ addressing mode
  3.   *a++=*b++; // one instruction: move @(A1)+,@(A2)+
  4.   *--a=*--b; // one instruction: move @-(A1),@-(A2)
  5.   // etc.

Shorter writing is not to be neglected:
Code: C  [Select][+][-]
  1.   while( *a++ = *b++ ) ;
  2.     // IS
  3.   while( *b != NULL )
  4.   {
  5.     *a = *b;
  6.     a = a + 1;
  7.     b = b + 1;
  8.   }
Isn't any of these enough for rationale? Especially in the early seventies.

For somebody it may sound curious, but the first line can be more readable for a C developer than the expanded loop - he will recognize it instantly as he is used to it.
Title: Re: Immutable data types
Post by: MarkMLl on October 07, 2022, 09:18:32 am
But it is better to use the builtin, optimized, and tested strcpy, strncpy, and so forth.

Which had to be written in the first place. We're talking about an early language, contemporaneous with Pascal (which was by no means perfect), designed and implemented by people less steeped in the art than Wirth/Hoare and intended as a standalone replacement for assembler.

However my attitude has long been summed up by one of the Modula-2 compilers' taglines:

"I come to bury C, sir, not to praise him."

MarkMLl
Title: Re: Immutable data types
Post by: Arioch on October 07, 2022, 06:17:57 pm
Okay, old f...riends. Friday time.

So, C is totally NOT predating PDP brand, while it MIGHT do predate PDP-11 computer.
Bacause, you know, PDP-11 computers were produced for maaany years, including after C language was published.
Notice: PDP-x computers were very diverse. PRobably not as diverse, as early IBM computers, but still - verily so.

Quote from: http://www.linfo.org/pdp-7.html
The PDP-7 and the Birth of UNIX

In 1969 Ken Thompson wrote the first version of UNIX in assembly language using an otherwise little-used PDP-7 at Bell Labs, the research arm of AT&T, the former U.S. telecommunications monopoly.
   ....
However, the PDP-7 was already obsolete when it was used for creating the first version of UNIX, and thus in 1970 the UNIX group proposed purchasing a PDP-11 for $65,000. The PDP-11, which had just been launched that year, incorporated some important advances (including greater ease of programming), and it became a highly successful and influential model. It was DEC's first and only 16-bit system.

In 1971, the group used their new PDP-11 to rewrite UNIX in a high-level language, instead of its original assembly language, so that it could more easily be ported to (i.e., transferred to) other types of computers. They briefly tried using Fortran before creating their own language based on BCPL (Basic Combined Programming Language), which they called B. They then extended B to produce the C language, which is still in widespread use today, after which they rewrote the UNIX source code in C. This made it easier to port UNIX to run on new hardware, as all that was needed was a C compiler to convert its C source code into the machine code for the specific type of computer.

If this timeline is correct, then C design was made by persons very fluent in specifically "diff" between PDP-7 and PDP-11 instruciton sets.
That does not mean that PDP engineers invented it. It is quite possible this was a common idea "in the air" that everyone and his dog dreamed about, but lmited hardware and software could not deliver yet. It is plausible PDP-11 engineers were trying to "wire out" at least some of what programmers dreamed off, so the ideas flow was reverse.

Quote from: https://en.wikipedia.org/wiki/TMG_(language)
Thompson used TMG in 1970 as a tool to offer Fortran, but due to memory limitations of PDP-7 ended up creating the B programming language which was much influenced by BCPL
This might be important, or not.
Some "UNIX dialect of TMG" seems ot already have inc/dec operators: https://en.wikipedia.org/wiki/File:Brainfuck_to_C_translator_in_Unix_TMG.png
The allegedly 1972 TMG manual describes those operators in chapter 5.2 - https://amakukha.github.io/tmg/TMG_Manual_McIlroy_1972.html

TMG'66 though seems to lack ++ and -- - https://www.multicians.org/tmg.html

One interesting caveat there is the language having TWO set of arythmetic operators, for 1-complement and 2-complement binaries. This produces some eerie feeling. It is one thing to read about historical concepts, and other seeing them actually "fleshed out" in pracitcal tools.
https://delightlylinux.wordpress.com/2014/10/13/binary-lesson-12-ones-complement-and-twos-complement/


Quote from: wikipedia
The PDP-7 is the third of Digital's 18-bit machines, with essentially the same instruction set architecture as the PDP-4 and the PDP-9.
....
PDP-4 .. was a compromise: "with slower memory and different packaging" than the PDP-1

So, basically, PDP 1/4/7/9 was one lineage of computers, and PDP-11 is different one, a sibling.
Surely, early hackers given a bonanza of two different systems made by mostly same team - were arguinh about differences of them no less than we argue about C vs Pascal. Or how IBM PC introduction probably ignited i8085 vs Z80 vs i8086 assembler flame wars. This perhaps would be closer. PDP-7 vs PDP-11 probably is similar in preseption to Intel 80 vs 86 or Motorole 6800 vs 68k.

There seems to be PDP-8 and PDP 6/10 lines, that were also entirely different designs: https://www.soemtron.org/downloads/decinfo/decdigital1957tothepresent1978102630349.pdf

So what was that PDP-1/4/7 assembler then? The trove gives way too much loot - http://www.bitsavers.org/pdf/dec/pdp7/
I'd highlight four documents.

http://www.bitsavers.org/pdf/dec/pdp7/DIGITAL-7-40-U_MasterTapeDuplicator_Dec64.pdf
Utility listing. The look and feel of the language.
Funny is how the lables seem to be both position-dependent (like in punch-cards Fortran) yet comma-separated (like in freeform post-cards languages). But not all, "LEFT" label is without comma. We seems ot see a convention being crystallized, first informally and not mandatory.

http://www.bitsavers.org/pdf/dec/pdp7/PDP7_Instruction_list.pdf
The name felt very promising, but.... It really is just a set of lists, set of "dictionary tables" with almost no description.
Damn it, even very structure of documentaiton kit is (was) non-orthodox!
Would not mention it, if not for eerie feeling seeing most of the list beingone-purpose commands wired into the processor just for interfacing one specific peripheral device.
I instantly started tinking about RISC-V much touted promicefor eveyr processor to have instrucitons custom-tailored for specific end hardware.
Well i was wrong yet again.


http://www.bitsavers.org/pdf/dec/pdp7/PDP-7_AsmMan.pdf
"PDP-7   SYMBOLIC ASSEMBLER   PROGRAMMING MANUAL"  (c) 1965
Finally getting "close to metal" aren't we?
We aren't !!!
Old DocKits structure bites me again.

This is merely an auxillary, supplementary side-not about how to use alien symbolic language to express well-known and well-inderstood machine codes.
Ouch!
Again, one thing is to vaguely know "there were prehistoric times when people..." and another to see it "in the flesh".
The "assembler manual" has almost no list of instructions and few decriptions of the instruction used.


Caveats, though.

The notation C (A) means contents of A.
Bah! So both un-assembly and un-highlevelly, i had ot struggle to parse it again and again!

...yet the content of the manual, again, not immediately relatable to the increment invention subtopic, is swinging between sheer lunacy and exotically delicious :-)

Would post it later. Too delicious a delirium to skip, yet too massive for the ++-discussion.


http://www.bitsavers.org/pdf/dec/pdp7/PDP-7_Brochure.pdf

The brochure. What should it have?
Well, obviously, glossy....

[ large chunk retroactively removed to a later post, sadly i could not remove the rest, it would've been a spoiler. But, if you feel amused by offtopic and do not like spoilers, scroll down to read brochure musigns sequentially :-) ]

....8. Now, you may call me an idiot. Mark would certainly have his chance at a brash newbie.
But i DID spent quite a few minutes trying to find any "return from subroutine" opcode. What the hell they named it and in which group they sticked it too. Yes, i did.

Caveat: there is NO return instruction. At all.

9. So, how are you supposed to return from a procedure? You are not.
Okay, you are.
You can read JMS instruction descripion. Then again. Then again. Then again.
Until THAT sinks in.
I would not even start to describe HOW wrong and twisted was what it did.

10. Then you go back to page 6 and read that "The PDP-7 Programming System inludes advanced FORTRAN Compiler".  And something starts wrigglign in the darkest corners of your memory. You start to kinda sorta (but not for certain) remembewr that early computers were built to and sold for running Fortran, like the recent computers were sold to run Windows and newer computers are sold to run Android. You kinda-sorta-maybe remember that it Fortran does not allow for nested functions calls. Or was it recursive? Or reentrant? Googles is of little help, sendign you to som manual and tutorials mentioning no this crucial limitation. But Fortran 77 demands you to opt-in recursive funcitons with special "compiler directive", giving away that "decent funcitons do not play with themselves". Wikipedia (this time it was the last thing to refer to, call me an idiot again) is blunt that Fortran the program was invented before hardware vendors invented the stack, and recursive calls were not possible and were not part of the language indeed.

Make it sink.

UNIX was coded on a computer that progibited functions to be called more than once, and that stored the return address in the function body itself.
B/C languages were mostly designed by persons who matured on computers lacking stack and lacking funcitons reentrancy.

Obscene...

BCPL'67 manual was preserved at https://www.bell-labs.com/usr/dmr/www/bcpl.html

Quote
An LVALUE is a bit pattern representing a storage location containing an Rvalue.
An Lvalue is the same size as an Rvalue and is a type in BCPL.
Make of it what you want. They still were struggling to invent the very language to describe ther languages :-)

Quote
Assignment Commands
Syntactic form: L1, L2, ... Ln := R1, R2, ... Rn
Both C and Pascal and other practical imperative languages jetissonned it. Functional language mostly retained it.
found OCR typo there, wanted to drop a notice, but i guess it won't be delivered any more

BCPL'67 is amusing... No wonder K&R loathed idea that procedure and funciton is something different. I would too.
But before i started (rather instead), it explicitly had recursive routines (procedures) and did not have ++ or --

Per wikipedia, B language was developed in 1969, but the report is dated 1972, https://www.bell-labs.com/usr/dmr/www/kbman.pdf
The report stresses language to be recursive, but it also stresses to only appliy to PDP-11 B not PDP-7 B.

If PDP-7 B was recursive, then it had to use ad hoc trampolines, making multi-threading unthinkable of, not just unimplementable.
B language did have ++ and -- operators, both prefix and postfix, so they were madei nto the languages in between 1969 and 1972.

OTOH, this speculation probably should be ignored, as B on PDP-7 was not true compiler: "The B compiler on the PDP-7 did not generate machine instructions, but instead ‘threaded code’ [Bell 72]"  (DMR's "C history")


Side note: BCPL language explicitly claims removing the data types facility from CPL language, as one prohibitively ocmplex to implement. C language later re-introduced data types syntax but NOT data type checks. Data types were tolerated by the compiler by ignoring them. Makes sense from the history of it. "OId C"/Fortran style of retroactively adding type to parameter variables surely was aiding C parser to ignore them.

Back to the sub-topic finally.

Quote from: C History
Thompson went a step further by inventing the ++ and -- operators, which increment or
decrement; their prefix or postfix position determines whether the alteration occurs before or after
noting the value of the operand. They were not in the earliest versions of B, but appeared along
the way.
People often guess that they were created to use the auto-increment and auto-decrement
address modes provided by the DEC PDP-11 on which C and Unix first became popular. This is
historically impossible, since there was no PDP-11 when B was developed. The PDP-7, however,
did have a few ‘auto-increment’ memory cells, with the property that an indirect memory reference through them incremented the cell. This feature probably suggested such operators to Thompson; the generalization to make them both prefix and postfix was his own. Indeed, the
auto-increment cells were not used directly in implementation of the operators, and a stronger
motivation for the innovation was probably his observation that the translation of ++x was
smaller than that of x=x+1.

It is perfectly plausible, that idea of auto-incrementing was "in the air" and everyone spanning from hardware designers to compiler designers dreamed of it.
Then PDP-7 could somewhat implement a very reduced version of that dream in those special memory cells.
Then PDP-11 could implement more of it (still only part) into post-incrementing and pre-decremeniting (if i remember correctly) addressing modes, and thenindependently Thomson would add it to the language, having premonition that "time is right" and the next-gen hardware would soon make this workable.

OTOH the timeline seems to be like this

1965: PDP-7 has only direct and indirect (pointer datatype) addressing, no inc/dec
1966: TMG (B's generator) has no ++
1969: Thomson creates early UNIX in PDP-7 asm, learning of auto-inc memory cells
1969: Thomson drafts early B language. Maybe in paper-only, maybe cross-compiling from GE "real machine". Generated threadded code, based upon TMG parser, having no ++ yet
1970: DEC announces PDP-11 machine. BL(Bell Labs) UNIX group starts lobbying to putchase it. This heavily implies they read through the manuals and were salivating, and where capable to explain with convincing details to money gatekeepers how much better the new system was.
1971: PDP-11 arrives to BL and Thomson immediately jumps into the peers queue to work on it. BTW, the Ritchie's "C History" seems to imply this already happenned in mid-to-late 1970, not even 1971.
1971: NB language (or "embryoni C"). Rithchie: In 1971 I began to extend the B language by adding a character type and also rewrote its
compiler to generate PDP-11 machine instructions instead of threaded code.
1972: B report explicitly quotes both PDP-11-only and ++ operator
1972: TMG (the specialized language used to generate early version of B) has ++ operator
1972: C language is quickly getting shape, but is not suitabl for makign any real program yet
1973: C is born. Ritchie: "By early 1973, the essentials of modern C were complete. The language and compiler were strong enough to permit us to rewrite the Unix kernel for the PDP-11 in C during the summer of that year. (Thompson had made a brief attempt to produce a system coded in an early version of
C (before structures) in 1972, but gave up the effort.)"

Quote
and a stronger motivation for the innovation was probably his observation that the translation of ++x was smaller than that of x=x+1.

Please, demonstrate me in PDP-7 or PDP-11 asm/opcodes that both are met:

* ++int is translated shorter, than int := int + 1
* int++ is NOT translated shorter, than int := int + 1

I believe what DMR reproduced here was blurred old memory of him overhearing some Thomson's hearsay, that was not of immediate interest to him.
I speculate: Thomson, probably, enjoyed bit-feedling much more than Ritchie, who more enjoyed fiddling higher-level concepts.

Quote
Values stored in the cells bound to array and pointer names were the machine addresses,
Ritchie Development of measured in bytes, of the corresponding storage area. Therefore, indirection through a pointer
implied no run-time overhead to scale the pointer from word to byte offset. On the other hand,
the machine code for array subscripting and pointer arithmetic now depended on the type of the
array or the pointer: to compute iarray[ i ] or ipointer+i implied scaling the addend i by
the size of the object referred to.

The whole paragraph dedicated to this, and indeed, this seems to be a big innovation of PDP-11 machine, that Thomsone and Ritchie were transitioning to in 1971. Making copiler for PDP-11 surely required getting to terms with it.

Sorry, with all my respect to Ritchie and his loylty to Thomson - this was wishful thinking and retransmition of hearsay he did not cared much about.

I wonder if Thomson ever gave his own, first hand and detailed report on ++ invention.

I mantain that ++ was either a generalization of what they learnt in 1970 form PDP-11 manuals.

Alternatively it could be a common idea that was known by everyone already in 1960-s, yet materialization in hardware impossible yet, and enshrining in the languages made little sense in "close to metal languages" (late B and C) and "considered harmfull" for high-level languages design (Pascal).
Title: Re: Immutable data types
Post by: Arioch on October 07, 2022, 06:19:27 pm
For those, whom i failed to bore into hibernaiton yet, the supplementary.

http://www.bitsavers.org/pdf/dec/pdp7/PDP-7_AsmMan.pdf
"PDP-7   SYMBOLIC ASSEMBLER   PROGRAMMING MANUAL"  (c) 1965

TL;DR

Notable is how octal opcodes are first class citizens, only optionally subtstituted by symbolic mnemo-codes.
In later Intel 86 assembler you would have to have a special "DB" or "DW" or "DD" psaudo-command to stream in raw data.
In PDP-11 MACRO it would be ".WORD" or ".DWORD" if i remember right.
In PDP-7 you would just key digits in.

This clearly coming from times when primary mean to write a program was exactyly a self-made memory dump (like Inel .HEX files, but octal)

Quote
The Assembler has, in its permanent symbol table, definitions of the symbols for all the PDP-7
operation codes, operate commands, and many lOT commands

Keywords? What keywords??? Just #define-like constants/macros!

...could only be made so in an "assembler" where octal literals were primary means to code instucitons, and symbolic ones were optional add-ons.

Quote
The user may also request the Assembler to assign variable storage for him by placing a # within
the first six characters of the variable. A symbol which includes this character is automatically
assigned a register at the end of the program, and a zero is placed in that register.

1. We see # symbol already havng a special place in the hearts oif DEC engineers aearly on. But the meaning of the symbol was very different yet. On PDP-11 it would mean "absolute non-relocatable constant, not using PC register current value as its origin"
2. We see the word "register" mentioned but isn't it use confusing?  Oh, my...

Quote
The Assembler also handles literals for the user. The value of the expression contained in parentheses is called a constant and is automatically placed in an unused location by the Assembler. The address of that location is inserted into the instruction. In the example below, the address of the register containing 1 is substituted in place of (1).

Literal constant was assigned an address and placed forever into a register.
How's that to you, Pascal guys? Literal is no distinct from initialized variable :-D
This is a bit exoitic even from i8086 assembler perspective.

Quote
Variables
The user may request the Assembler to assign storage registers for him. These registers, whose
value may be changed while the program is running, are designated as variables. A symbol
(permanent symbols and pseudo-instructions must not be used) containing # or $ as one of its
first six characters, which is not explicitly defined elsewhere by use of a comma or equal
sign, is a variable. A symbol may contain a # any number of times without becoming multidefined, but this character is required only once, not necessarily on the first occurrence of the variable.

...any place...
...not necessarily on the first occurrence...

If i parse it right, i might user some VARNAME many times in the prohram, then just mention anywhere VARNA#ME once (yes, infix!) to have its "storage class" altered. But would i use VARNAME# or VARNAM#E instead, and - oh, the dreaded Off By One - it is silently ignored, with no errors emitted.

Times, they are a changing!

Quote
A storage register whose contents remain the same throughout the running of a program is designated as a constant. A constant can be represented by using a literal: an element or expression contained in parentheses. This type of element causes a register to be reserved in the constants table by the Assembler.

....a register to be reserved in the constants table...

My head goes PUFFF!!!

But so, the "Register"" term in 1960-s hardware meant just a memory location designated to have some specific data. Todayt we would call it a global variable.

Man, i feel lucky not trying to search for something like "list of registers of PDP-7 processor" - because neither processors nor registers (as we undertand the terms) existed yet!!!
Which, again, is common as a theoretic background knowledge, but running into it like into a wall leaves quite an aftertaste.

Quote
Currently unassigned variables are defined as the addresses of successive registers at the occurrence of the pseudo-instruction VARIABLES. The pseudo-instruction VARIABLES may be used repeatedly in a program.
Quote
If the pseudo-instruction VARIABLES is not used, the table of variables is automatically assigned
a storage block at the end of the program. Upon loading the program, the contents of all locations assigned by use of the variable facil ity are zeros.

Proto-DATA segment !!!  But on that old hardware there was no distinction between RW and R/O data yet.

Quote
A variable containing $ causes a multiple number of registers to be reserved. The number of
registers to be reserved may be controlled by the pseudo-instruction BAR n. The element or
expression  N specifies how many locations are to be allocated for $ variables. N is initially
set to three.

Proto-static-array !!!

Also feesls like predating BASIC's notion of declaring variable datata type by the postfix symbol.

Quote
If any symbols remain undefined at the termination of assembly, they are automatically defined
as the addresses of successive registers following the variables storage block and their definitions printed. That is, they are treated as variables containing #, but the user is informed of the assignment.

....when i was coming from school BASIC and "pen and paper ForTran" to Pascal - the rude demand to make varible declaations was infuriating. That daaamn stupid gatekeeper having nothing better to do than asking me to show my Variable IDs again and again? Go get some real job to feel your time!!!

LAC TST  -  loads the AC with the contents of register 477
LAC (TST  -  loads the AC with the value 477


NOT a typo, there is no "close parenthesis" concept yet! Parenthesis is a prefix, nothing more. Making a "recursive parser" to recognize paretheses was wastefully complex for then hardware.

...Except for snippets like (1) creatign a constant register. HEre we suddenly need both open and closing parentheses and parser does care. C is for consistency.

EXIT = JMP I 20

Remember me being lost about a stack and nested calls above? :-)

Oh, the "i" there...

Quote
Indirect Addressing
If bit 4 in the binary representation of a memory reference instruction is 1, instead of taking
the memory location referenced as the effective address of the operand, the contents of the
location referenced are taken as the effective address of the operand. Indirect addressing is
represented in the source language by the character I following the operation symbol thus:
LAC I 500

Not sure if i should cry "unconventional" or see it as precursor to MASM type qualifiers like MOV AX, QWORD PTR [BX].
Assembler guys would hardly recognize "indirect addressing" as data type, but replace "indirect addressing" with "pointer type" and it is.

Still, PDP-11 MACRO made a clean break, and used really different mnemonics for addressing modes.

Quote
No error message is printed if a defined symbol is redefined in a parameter assignment
unless it is a permanent symbol

Proto-variables. Or should i call it preprocessor macro-variables?


...now, let's look at the slash symbol.

Talk about functional overloading (as in chess-like way too much burden, not as subroutine polymorphism).

Quote
COMMENTS
If the character slash / occurs, not immediotely preceded by an element or expression, all
characters between the slosh and the next carriage return are ignored. Illegal characters (see
page 7) are permitted within comments. Parity errors are ignored within comments also.

Examples:

/THIS IS A COMMENT
LAC A  /AS IS THIS
400/ 0 /lAND THIS ALSO!

So what that 400 was and why?  Scroll up to the very very beginning:

Quote
THE LOCATION COUNTER
In general, statements generate 18-bit binary words which are placed into consecutive memory
locations. The location counter is a register used by the PDP-7 Assembler to keep track of the
next memory location available. It is updated after processing each statement.

Note how "register" gets yet another meaning here. And internal "index" or "pointer" varable in the compiler itself. I beleive today it would be seen to avoid using a term in two different meanings within a manual.

Quote
The location counter may be explicitly set by an element or expression followed by a slash. The element or
expression preceding a slash sets the location counter to the value of that element or expression.
Subsequent instructions are assembled into subsequent locations.
Example:
100/ The next instruction is placed in location 100.

So, was it an element or an expression (and should it even make difference here? don't we today think of single constants ase a special, degeneartae case of expressions?)?

Sidenote: actually "element" there meant what we would name a token or an dientifier today. But not quite, not quite...
Quote
Any group of letters, digits, and parentheses which represent binary values less than 2^18 are elements. Values are assigned to elements by the Assembler or the Loader.
Talk about self-recursive declarations. I can not figure otu of this whether values go first or elements do.

Lurk more the slash though...

Quote
LOCATION ASSIGNMENT
The use of a slash V), if immediately preceded by an element or expression, sets the location
counter equal to the value of that element or expression.
Examples:
300/ LAC (56
BEG-240+A/ LAC (56     -  The instruction is stored in location number BEG-240+A.


So, if you would think how ALGOL got the //-comments - this is probably the birthplace  of that bad habbit.
BASIC's use of apostrophe-comments and intel assembler use of semicolon-comments look pristine and perfectly justified attempts at clean break here. But dirty ALGOL prevailed. In modern Pascal too.

Now, commas.

Quote
SYMBOLIC ADDRESS TAG
An element or expression which represents a location in memory may be assigned a value in a
number of ways. The user could utilize the parameter assignment feature thus:
A=. ADD 100
The symbol A is assigned the value equal to the location in which the instruction ADD 100 is
placed by the Assembler. If the symbol already has a definition, it would be redefined. The
user can reference this location later by the symbol A:
JMP A

Redefining goto labels!!! How's THAT to you, Niklaus Wirth? What you consider harmful now, Dijkstra???

Folks, isn't it all sheer delirium? I wish i had a time machine and a big stick (bigger than logrules), to have some softly spoken words with them who designed THIS...

Quote
The simplest way to assign a value to a tag is by use of the comma.
A, ADD 100
The value of A would be the same as in the first case; the only difference would occur if A
had previously been defined, which would result in the diagnostic MDT.

...and strangely we made a full circle and came back to the notion of once-assigned (bound, almosat-immutable) variables, as a syntax construct, saving programmer from accidental typos, but not seeking to prohibit all cases of intentionally unsafe overrides.

Quote
A single undefined symbol or an expression containing only one undefined
symbol preceding the comma has its value set equal to the current location.

Setting value to expressions? My head, my head, please don't leave me yet!..

It is explained later, though.

Quote
An expression preceding the comma which contains more than one undefined symbol causes the
error print TUA. If the expression to the left of the comma contains no undefined symbols but
is equal in value to the current location, it is ignored; otherwise the error print MDT occurs.
This feature is useful for verifying table lengths.

Examples:

A,
B+1,     -  Where A and B are previously undefined symbols.
101,     -  Where the number is the same as the current value of the location counter.
GEORGE+HARRY-4,  -  Where either GEORGE or HARRY are previously undefined symbols.

A nifty trick... But Mel would do without it.

I still left puzzled, if the "LEFT" label in the listing above - and it was a listing for "master tape duplicator" - a CRUCIALLY important utility which errors could brick the super-expensive machine - was buggy and never could be actually "compiled".  Well, today all the books are printed from digital data, so probably some typography boy dropped the comma glyph when pre-mastering.

============

http://www.bitsavers.org/pdf/dec/pdp7/PDP-7_Brochure.pdf

The brochure. What should it have?
Well, obviously, glossy photos with perhydrole maidens, and lots of shallow buzz-talk, aiming to drive you out of your wallet before you started thinking what it specifically was what yujsut bought.

Glossy photos there are, but instead of maidens there is some man with black beard instead. And they say those were years of sexism and ageism, LOL.

The shock is though having found the processors commands explained there, between those advertizing photos! Not in the asm manual, not in instrucitons list, but in the commercial !!!

Page 8 (and a bit on page 7).

1. "Memory referencing instructions" - oh, the classification!
Rememeber what written about "Irish stew" (the Jerome's report edition) and also an eastern's prior art (https://en.wikipedia.org/wiki/Celestial_Emporium_of_Benevolent_Knowledge)

It, perhaps, made practical sense in the days when core memory was the most limited and slow sectin of the machine, but still, what a stew!

2. Good luck figuring out was "xct Y" is supposed to do

3. rather peculiar isz Y instruction. If i got it right then it was, in Pascal terms, for i := -10 do with eveyr loop ending as the variable reached zero. Sounds simple, right? Exotic, yet simple.

4. The primary operation, add, used now extinct 1-complement negatives. And only the aux, supplementary tad used now pervasive 2-complements.
Which details makes the aforementioned isz ascend to a "fridge horror" estate - so WHAT encoding was supposed to be used there?

5. No conditional jump instructions, but plethora of "skip-if-X" opcodes, today we would've called them conditional prefixes.
So, if you ever wondered where the established Brittish gentleman ARM contracted his dirty habits of conditional execution - you know now. Yankees did it.
Granted, from human perspective this is pretty intuitive and logical, branching and conditioning seems to belong to different boxes of building blocks. The history decided otherwise, though.

6. Exotic "deposit" instead of store/save/write. Yet common "Jump" instead of PDP-11 later "branch". Go figure

7. cal Y - call the subtourint, Y is ignored - "feel joy, Feel Joy, FEEL JOY!!!!" (from a Slient Hill concerto. Or should i rather quote "Rejoice!!!" from Girl Genius?)
Well, i hope this abomination does not exist "in silicium" and is imagnary ghost created by assembler translator. In the years when every bit costed more than it's weight ion gold making a special instruction to have yet ignore the operand!!! OTOH, both Intel 8086 and MACRO-11 assemblers are now to have special distinct mnemonics for specific cases of more general opcodes, so probably thius was not a real command.

8. Now, you may call me an idiot. Mark would certainly have his chance. But i DID spent quite a few minutes trying to find return opcode. What the hell they named it and in which group they sticked it too.Yes, i did.

Caveat: there is NO return instruction. At all.

9. So, how are you supposed to return from a procedure? You are not.
Okay, you are.
You can read JMS instruction descripion. Then again. Then again. Then again.
Until THAT sinks in.
I would not even start to describe HOW wrong and twisted was what it did.

10. Then you go back to page 6 and read that "The PDP-7 Programming System inludes advanced FORTRAN Compiler".  And something starts wrigglign in the darkest corners of your memory. You start to kinda sorta (but not for certain) remembewr that early computers were built to and sold for running Fortran, like the recent computers were sold to run Windowsand newer computers are sold to run Android. You kinda-sorta-maybe remember that the Fortran firstly did not allow for nested functions calls. Or was it recursive? Or reentrant? Googles is of little help, sendign you to som manual and tutorials mentioning no this crucial limitation. Deecades later Fortran 77 "invents" them but demands you to opt-in recursive funcitons with special "compiler directive", giving away that "decent funcitons do not play with themselves". Wikipedia (this time it was the last thing to refer to, call me an idiot again) is blunt that Fortran the program was invented before hardware vendors invented the stack, and recursive calls were not possible and were not part of the language indeed.

Make it sink.

UNIX was coded on a computer that prohibited functions to be called more than once, and that stored the return address in the function body itself.
B/C languages were mostly designed by persons who matured on computers lacking stack and lacking functions reentrancy.

Obscene...

11. There seems to be no INC/DEC opcodes in PDP-7, nor SUBTRACT

12. There seems to be COMPLEMENT opcode, which more traditionally should be code NEGATE, but - it does not specify if it  is 1complement or 2-complement. I suspect it was 1-complement.
If true, this would make implementing out traditional 2-complement subtraction into a real disaster!!!

Code: [Select]
x := x - y

SUB X, Y ; x86

SUB @#Y, @#X ; PDP-11

LAC Y
COM Y
TAD (1)
TAD X
DAC X

eeek....

For people who grew with such instruction sets, the CISC should look an earthly paradise and RISC a conspiracy against mankind
Title: Re: Immutable data types
Post by: Bogen85 on October 07, 2022, 09:00:23 pm
For those, whom i failed to bore into hibernaiton yet, the supplementary.
....
For people who grew with such instruction sets, the CISC should look an earthly paradise and RISC a conspiracy against mankind

I'm far from bored, even though much of this discussion took a different path from my initial topic.

(but is still relevant to it, albeit sometimes indirectly).

This is a rather intriguing and detailed history lesson and sheds light on a lot of things.

I entered this whole arena a bit late (mid 80s) but all of this very good stuff to know.

And yes, I've always seen CISC as human friendly, and RISC as yeah, maybe very machine friendly, but often not so human friendly...

Though there are some aspects that RISC instruction sets seem to enforce (but not always) is orthogonality and consistent opcode/operand patterning.

But many CISC instruction sets are as well (or seem to try to be), so those obviously are not RISC specific qualities.

But overall I'd agree with that CISC vs RISC sentiment, especially when the latter is taken to extreme with VLIW or juxtaposed instructions that need to follow some complex set of rules or have a lot of NOP insertions if those rules can't be satisfied.



Title: Re: Immutable data types
Post by: Bogen85 on October 07, 2022, 09:38:13 pm
I entered this whole arena a bit late (mid 80s) but all of this very good stuff to know.

Around '83/'84 when I got into programming....
(And a work colleague of my parents gave me some cardboard boxes full of Byte magazines going back several years, so I could "catch up" some)

But I'd already been exposed to PDP11 descendants in VT100 terminal cases at my parents workplaces as far back as 1981. (They were not programmers, just end users).

When I was first exposed to PC clones with my limited knowledge at the time they seemed inferior to me in many ways compared to the PDP11 descendants I'd already used.
(Primarily because MS-DOS seemed very primitive to me compared to what I'd used prior, especially VT2xx terminals or dial-up connected to Unix machines I never saw).

Title: Re: Immutable data types
Post by: MarkMLl on October 07, 2022, 10:26:26 pm
I think that I chose my words badly when I suggested that C (or at least ++ etc.) predated the PDP series. I thank Arioch for his scholarship, even though I'm sure that nobody has digested it in its entirety.

When I was first exposed to PC clones with my limited knowledge at the time they seemed inferior to me in many ways compared to the PDP11 descendants I'd already used.
(Primarily because MS-DOS seemed very primitive to me compared to what I'd used prior, especially VT2xx terminals or dial-up connected to Unix machines I never saw).

PCs undoubtedly took a long time to catch up with minis, in the same way that minis took a long time catching up with mainframes. while I think it's fairly safe to say that minis have been obsoleted, the jury's still out on mainframes since there are some things designed in early (e.g. process migration between machines) which their successors never really got.

If I could add one historical observation: CP/M, hence DOS, had low-level system calls to direct I/O to and from an "AUX:" device, which was normally a serial port. IBM VM/370 on mainframes had something comparable for a card reader and punch. Either IBM or the retro community has successfully repurposed that for inter-user communication, so it you want to send something to a different user (or to a background task running in its own VM etc.) you simply use the punch API with a header card.

MarkMLl
Title: Re: Immutable data types
Post by: lainz on October 08, 2022, 03:40:29 am
I think the purpouse of immutable data types is to prevent bugs.

If you use threads making a variable read only sounds good.

Rust is immutable by default?

Kotlin suggest me at every variable to set it as immutable if it determines that is not edited later. (Yes Android Studio is the best IDE ever. Not bug free I must say but makes coding so easy.)
Title: Re: Immutable data types
Post by: Bogen85 on October 08, 2022, 04:52:36 am
I think the purpose of immutable data types is to prevent bugs.

To prevent some types of bugs, yes. Runtime mechanisms can be used but in some cases this is added overhead and many require extra debugging to eliminate lock up conditions. (semaphores, mutex, spinlocks) (And these have been discussed in this topic thread)

Hiding data using a read only property (as discussed in this topic thread as well) prevents casual or careless reassignment as no write method was specified, so that is enforced at compile time.
I say casual reassignment, one could still use the instance pointer and calculate how to overwrite the variable in question. But it satisfies many cases where immutability is required for certain types of data, because of the lengths one must go to intentionally modify the data.


If you use threads making a variable read only sounds good.

Yes. Which is why on languages without deep compile time enforcement of immutability I often use forks (fairly cheap, due to copy on write forking, not as cheap as threads) and message pass.
Windows can't fork efficiently and does not provide any fork mechanism by default, so such software can't run on Windows. However, WSL blurs the line about what can run on Windows, so that is not as much an issue any more.
(As it also was on systems that had expensive non copy on write forks).

Immutability when using COW forking is really only preventing a child process from mutating it's parent data that was passed to it.
It does not prevent the child from modifying it's own data, it just prevents the possibility of corrupting parent data through direct memory access.
If the child was not supposed to be running a different copy than the parent, that is not enforced.

However, one could use MMap/MProtect to mark regions read only, so that could be enforced.
Still a runtime check, but low overhead. Since a runtime check, bugs have to be exposed and caught later, and some might slip through the cracks if unit/functional testing is not comprehensive enough, which is why compile time checking is preferable.

But if using MMap/MProtect to mark the regions read only, there is no need to have forked, one could have just spawned another thread.

Rust is immutable by default?

Yes, as is V and most functional languages. (And Nim, if you use let instead of var)

Kotlin suggest me at every variable to set it as immutable if it determines that is not edited later. (Yes Android Studio is the best IDE ever. Not bug free I must say but makes coding so easy.)

I usually take the stance if the language supports immutable variables and that is enforced at compile time, I'll use them everywhere to have the compiler tell me where I messed up and tried to mutate something. (unless I know of course it needs to be mutable).
Then I can make the determination whether or not it needs to be modified, and let it be mutable, or just rewrite that particular code to not need to modify it (within reason, there could be some validity in it being mutable, depends on the overall purpose of what that particular code is supposed to do).

For instance, tracking persistent state of something may be more straight forward with mutable data in memory, rather than resorting to using some data-store external to the program just to wantonly say you had no mutable variables in your program.
Also, but not limited to, long term counter, serial id, etc. There are ways to do those functionally, but that might not always be the most maintainable or desirable way of doing it, or efficient, depending on the particular application.
Title: Re: Immutable data types
Post by: MarkMLl on October 08, 2022, 08:44:32 am
To prevent some types of bugs, yes. Runtime mechanisms can be used but in some cases this is added overhead and many require extra debugging to eliminate lock up conditions. (semaphores, mutex, spinlocks) (And these have been discussed in this topic thread)

And I'd point out that having the compiler take steps to avoid problems is very much "the Pascal way".

MarkMLl
Title: Re: Immutable data types
Post by: Arioch on October 08, 2022, 04:13:29 pm
A.S. by "the PDP11 descendants" one cam mean 32-bit ST-VAX series. Which originally used to mean "PDP-11 with Virtual Address Extension", but this would be quite a stretch. Like 80386/80486 were "the 8086 descendants" but obviously they were a whole different class.

So, by "PDP11 descendants" then i would assume non-DEC PDP-11 clones.

Around '83/'84 when I got into programming....
(And a work colleague of my parents gave me some cardboard boxes full of Byte magazines going back several years, so I could "catch up" some)

But I'd already been exposed to PDP11 descendants in VT100 terminal cases at my parents workplaces as far back as 1981. (They were not programmers, just end users).

I was exposed to them in late 1980-s and PDP11 was standard setup in Soviet schools (there were also Apple-like Z80 machines, and MSX machines, but the mainstream i believe were two PDP11 based setups).

No, MS-DOS definitely was more advanced than RT11-SJ/CD.
RT11-FB (cooperative two-tasking) could be seen a bit more advanced (*only* in F/B aspect!) than MS-DOS, but not than Concurrent DR-DOS.
RT11-XM could not be actually run on available hardware, just like MS-DOS + EMM could not be run on available second-hand wstern machines without EMM add-on cards.

There was the driver that turned 2/3 of video-memory into RAM DISK (KD.SYS) - but this made videocard badly flicker (probabl the same reason as the infamous CGA "snow" which i never saw personally).
However at it's ideal imaginary condition it would be the same conceptually as MS-DOS + XMS (any 286+ CPU).

More so, even the most el cheapo second-handed  PC286 had 1MB of RAM. If no UMB hardware/ssoftware was available - it would be 640KB main RAM + 64KB High RAM + 320 KB XMS buffer.
RT11-SJ/CD + KD.SYS gave you 64KB RAM + 96KB ex-video buffer (i believe MS-0511 video RAM were 3x48Kb, but my memory may be at fault here).

Among other things that meant MS-DOS had more or less free memory allocation (Memory Arenas AKA MCBs), while RT-11 could not spare scarce RAM on this and only had single pointer of "top user app memory" with all above considered resident core of OS.

MS-DOS has TSR API, compensating for lack of native multitasking.
RT11-SJ had neither (but had loadable device drivers, which could be used as resident programs without any user interface).

And not let me even start about filenames.

If you come from "how much could they cram into 56KB of RAM (the rest was reserved for I/O ports) - then RT11 was a very impressive OS.
But it never provided a share of MS-DOS services. And could not, having such a huge hardware handicap.

Perhaps the only service that RT-11 provided that DOS/Windows/NT did not - was command line templates, which helped to make 3-rd party CLI utilitiees share "look and feel" with standard OS commands. Yet even that was not complete IMHO - the other side of the coin should had been the standard error/warning/request UI, and this RT-11 did not expose (Windows CommonControls does, MS-DOS did not)

PDP-11/RT11 was absolutely impressive when you think against what hardware limits they had to work, "bang forthe buck". But in absolute numbers, the "bang" was veyr much less than any PC+MS-DOS offered.
Title: Re: Immutable data types
Post by: Arioch on October 08, 2022, 04:21:50 pm
still out on mainframes since there are some things designed in early (e.g. process migration between machines) which their successors never really got.

For waht i heard, lineages like AS/400 are not jsut "big iron" with "fail tolerance" - they are reallt build of different primitives.
Granted, i do not have immediate experience, albeit IBM is said to offer free AS/400 remote access today.

For what i heard, AS/400 is more akin to OOP-OS / Capability-based OS, with their executive environment more resembling what we would expect from JVM/.Net runtimes rather than OS.
Title: Re: Immutable data types
Post by: MarkMLl on October 08, 2022, 05:03:31 pm
RT11-FB (cooperative two-tasking) could be seen a bit more advanced (*only* in F/B aspect!) than MS-DOS, but not than Concurrent DR-DOS.

There ain't no such animal. CDOS (formerly CCP/M-86, latterly MDOS) and DR-DOS were completely separate codebases.

For waht i heard, lineages like AS/400 are not jsut "big iron" with "fail tolerance" - they are reallt build of different primitives.
Granted, i do not have immediate experience, albeit IBM is said to offer free AS/400 remote access today.

For what i heard, AS/400 is more akin to OOP-OS / Capability-based OS, with their executive environment more resembling what we would expect from JVM/.Net runtimes rather than OS.

AS/400 is more a supermini than anything else, taking over from unit-record equipment and ultimately tabulating machines. Its execution model is pretty much unlike anything else, and it purports to be based on persistent objects rather than files etc.

However, the AS/400 is a /long/ way removed from actual IBM mainframes.

But all of this is /way/ off-topic.

MarkMLl
TinyPortal © 2005-2018