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]
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.
How the compiler will know it the value locked , you need to add AI 8)
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:
for i: integer= 0 to 9 do begin ... end;
...and we all know what the developers think about /that/ sort of thing.
MarkMLl
It is exists also D lang , and they have good examples https://dlang.org/spec/const3.html
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.
{$mode delphi} {$modeswitch anonymousfunctions} {$modeswitch functionreferences} program readonlyloop; type tLoop = reference to procedure (const i, limit: integer); procedure main; var L1: tLoop; begin L1 := procedure (const i, limit: integer) begin if i >= limit then exit; writeln ('at local iteration ', i); L1 (i+1, limit); end; L1 (10, 25); end; begin main; end.
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.
No AI involved.
Just a Pascal range, likeHere I is not an index, but content. The index itself is a good example of immutable. You can't even see it... :D
program test; type Tmyrange = 0..9; var i: Tmyrange; begin for i in Tmyrange do writeln(i); end.
And it's final value accessible after the loop, and still actually modifiable.
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.
People got on for IONS before Zero...
https://en.wikipedia.org/wiki/0
Feel free to also donate to wikipedia..
People got on for IONS before Zero...And even aeons before that >:D
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 seenSyntax before and didn’t know it was even possible.
procedure something (const a, b, c: integer); begin procedure (const d, e, f: integer) begin
Is this part the anonymous procedure calling itself?Yes
end(a+4, b+5, c+6);
What keeps this procedure from being called more than once?
They are not in the current release of FPC, but will be available in the next release.
We will see... Worst case @PascalDragon sees a lot of issue reports from me on anonymous method closure problems... (and related).
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 seenSyntax before and didn’t know it was even possible.
procedure something (const a, b, c: integer); begin procedure (const d, e, f: integer) begin
Is this part the anonymous procedure calling itself?
end(a+4, b+5, c+6);
What keeps this procedure from being called more than once?
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
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).
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.
x := 23; // i does not exist for i in Tmyrange do // i exists! (and probably in a nice snug, fast register) begin GreatBallofFire [i] := x + i -1; end; // i ceases to exist i := i + 1; // compiler go boo!
But how can this be done in Free Pascal?
But how can this be done in Free Pascal?
It can't.
But (IMO) it should.
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.)
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.
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
Well, the thing is that you need to use pointers in pascal. Most simple case is classes
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.
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.
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.
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.
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).
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:
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 is why you should get equated with the Interlock..... functions and TcriticalSection types.
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.
That said, const is not harmfull, it actually has a usecase in that, it allows for optimizations on managed types
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.
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"
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.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.
Whatever is dependent upon simpe type helpers is dependent upon a feature broken by design. You just made one more good case to this.
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.
I wonder if you would then try to take pointer to the this->iYes 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).
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 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.
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.
from your point of view as a user you can assume it to be a black box.
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
I don't see how it is the same point. You just made an incapsulated object(record) with an exlicit mutating method.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:
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.
...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:
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:
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.
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.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.
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.
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
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
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.
So what's the difference between a typed constant and a var?
It's stored differently in the executable? Is faster? Is cool?
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 what's the difference between a typed constant and a var?
It's stored differently in the executable? Is faster? Is cool?
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.
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.
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.
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).
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.
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 ?
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.
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 ?
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.
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.
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.Yes, I know. From functional languages, but different.
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.
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.
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.
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.
Yes, I know. From functional languages, but different.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.
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.
This might seem overcomplicated, or as MarkMLI put it "computersciency crap"
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)
It would like judging C badly for their ++ and -- operators (that were basically PDP intrinsics).
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?
QuoteLet'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: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.)Back before optimizing compilers.... maybe it was needed to produce optimal code...
while(*a++ = *b++); // and that is obvious to others what that means???
Certainly not needed with any decent modern C compiler... (in the past 2-3 decades, maybe longer)
Yeah... so people can write: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.)Back before optimizing compilers.... maybe it was needed to produce optimal code...
while(*a++ = *b++); // and that is obvious to others what that means???
Certainly not needed with any decent modern C compiler... (in the past 2-3 decades, maybe longer)
(My apologies for being off-topic)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?
But it is better to use the builtin, optimized, and tested strcpy, strncpy, and so forth.
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.
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 BCPLThis might be important, or not.
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
An LVALUE is a bit pattern representing a storage location containing an Rvalue.Make of it what you want. They still were struggling to invent the very language to describe ther languages :-)
An Lvalue is the same size as an Rvalue and is a type in BCPL.
Assignment CommandsBoth C and Pascal and other practical imperative languages jetissonned it. Functional language mostly retained it.
Syntactic form: L1, L2, ... Ln := R1, R2, ... Rn
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.
and a stronger motivation for the innovation was probably his observation that the translation of ++x was smaller than that of x=x+1.
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 Assembler has, in its permanent symbol table, definitions of the symbols for all the PDP-7
operation codes, operate commands, and many lOT commands
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.
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).
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.
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.
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.
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.
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.
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.
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
No error message is printed if a defined symbol is redefined in a parameter assignment
unless it is a permanent symbol
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!
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.
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.
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.
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.
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
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.
A single undefined symbol or an expression containing only one undefined
symbol preceding the comma has its value set equal to the current location.
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.
x := x - y
SUB X, Y ; x86
SUB @#Y, @#X ; PDP-11
LAC Y
COM Y
TAD (1)
TAD X
DAC X
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 entered this whole arena a bit late (mid 80s) but all of this very good stuff to know.
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).
I think the purpose 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.)
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)
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).
still out on mainframes since there are some things designed in early (e.g. process migration between machines) which their successors never really got.
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.
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.