#1038 Programming: Part 1
Expand Messages
Lynn H. Maxson
Jul 25, 2004
We have some confusion about the necessity for and level of
programming experience to participate in this project. We
ought to put them to rest, to increase the comfort zones of all
participants, and to lower the reticence to participate.
We can only make one type of error in programming: logic. In
logic we only have two ways to make an error: commission (in
what and how we say it) and omission (what we fail to say).
You will see that this applies to areas in which we engage in
or otherwise use logic.
In programming we have two logic areas. One relates to the
definition of data;the other, to its processing. We cannot
engage in "black-box" programming as John Baker suggests
without logically defining the data, the inputs and outputs
separated and joined by the processing. We may have only a
general, somewhat informal definition of the logic, but we
have an absolutely detailed definition of the data.
So to pursue this "black-box" approach we may put off to
some later date the definition of the logic, but we cannot
formally specify the data without programming it in detail, in
the writing of that specification or data definition. If you
want to "bring up" your programming skills as well as
participate in the documentation of the design stage, I suggest
that you "master" defining data.
You cannot define data without also choosing without
choosing a specific language. If it is not a programming
language, only a specification-only language, then at some
point you have to respecify it in a programming language. As I
assert that we should choose PL/I and its fourth-generation
derivative, PL/E (SL/I), instead of C and its derivatives, I
would like to offer just in the area of data definitions a few
of the thousands of reasons for such a choice.
Remember we have to do this for any API, which consists of
three parts: (1) a return code, (2) an API name, and (3) a
parameter list. To offer what I would hope comes down as a
"convincer" I would suggest we begin with "DosOpen".
From the "OS/2 Control Program Guide and Reference" we
have the following for DosOpen:
Opens a new file, an existing file, or a replacement for an
existing file. An open file can have extended attributes.
#define INCL_DOSFILEMGR
#include <os2.h>
PSZ pszFileName; /* Address of the ASCIIZ path name of
the file or device to be opened */
PHFILE pHf; /* Address of the handle for the file */
PULONG pulAction; /* Address of the variable that receives
the variable that receives the value
that specifies the action */
ULONG cbFile; /* New logical size of the file (end of
data, EOD), in bytes */
ULONG ulAttributes: /* File attribute information */
ULONG fsOpenFlags; /* The action to be taken depending on
whether the file exists or does not
*/
ULONG fsOpenMode; /* The mode of the open function,.
Possible values are show in the
following list: */
PEAOP2 peaop2; /* Extended attributes */
APIRET ulrc; /* Return code. */
ulrc = DosOpen(pszFileName, pHf, pulAction,
cbFile, ulAttribute, fsOpenFlags,
fsOpenMode, peaop2);
While I have no desire to unfairly compare C and its
derivatives to PL/I or PL/E, I do have a desire to let the facts
speak for themselves. I remember how the adrenaline flowed
when I first read K(ernighan) and R(itchie)'s "The C
Programming Language". I couldn't believe the distortions and
false claims they made. While the ethernet had a Cisco come
along to compensate for its weaknesses, C and its derivatives
have had no such savior.
However, for comparison purposes you need to understand in
some detail what lies within and behind the C code of
DosOpen. With that understanding then, along with the
weaknesses it exposes, you will better appreciate that
languages differ and those differences matter.
So in the second part of this message we will explain in detail
the definitions in C of DosOpen.
Part 35 - Jul 23 2004
Re: [osFree] Programming: Part 2
#1039 Re: [osFree] Programming: Part 2
Expand Messages
Lynn H. Maxson
Jul 26, 2004
Copied directly from the OS/2 Control Program Programming for
"DosOpen--Syntax" adjusted for editor word wrap.
Opens a new file, an existing file, or a replacement for an existing
file. An open file can have extended attributes.
#define INCL_DOSFILEMGR
#include <os2.h>
PSZ pszFileName; /* Address of the ASCIIZ path name of
the file or device to be opened. */
PHFILE pHf; /* Address of the handle for the file. */
PULONG pulAction; /* Address of the variable that
receives the value that specifies the
action taken by the DosOpen function. */
ULONG cbFile; /* New logical size of the file (end of data,
EOD), in bytes. */
ULONG ulAttribute; /* File attribute information. */
ULONG fsOpenFlags; /* The action to be taken depending on
whether the file exists or does not exist.
*/
ULONG fsOpenMode; /* The mode of the open function. Possible
values are shown in the following list: */
PEAOP2 peaop2; /* Extended attributes. */
APIRET ulrc; /* Return Code. */
ulrc = DosOpen(pszFileName, pHf, pulAction,
cbFile, ulAttribute, fsOpenFlags,
fsOpenMode, peaop2);
This function (API) gets invoked within an "assignment" statement
which consists of two parts separated by the '=' character. This
follows the "standard" form of most third generation HLLs (High Level
Language). The variable to the left of the '=' sign, in this instance
'ulrc', receives the value of evaluating the expression to the right of
the '=' sign, in this instance the invocation of the DosOpen API.
In general then the assignment statement has the form of "left-hand =
right-hand" or "variable = expression" or "target = source". In every
instance the "evaluation" of the expression (source), the result, gets
placed into the variable (target), in this instance 'ulrc'.
Remember our interest in this API lies not in using it in an application,
but in providing it for an application's use. In practice we have an API
layer which functions as an interface between the "replacement"
kernel and the applications.
Perhaps we should begin with 'ulrc' and the values, the return codes,
it can have. Again we take from the OS/2 Control Program manual:
ulrc (APIRET) - returns
Return Code.
DosOpen returns one of the following values:
0 NO_ERROR
2 ERROR_FILE_NOT_FOUND
3 ERROR_PATH_NOT_FOUND
4 ERROR_TOO_MANY_OPEN_FILES
5 ERROR_ACCESS_DENIED
12 ERROR_INVALID_ACCESS
26 ERROR_NOT_DOS_DISK
32 ERROR_SHARING_VIOLATION
36 ERROR_SHARING_BUFFER_EXCEEDED
82 ERROR_CANNOT_MAKE
87 ERROR_INVALID_PARAMETER
99 ERROR_DEVICE_IN_USE
108 ERROR_DRIVE_LOCKED
110 ERROR_OPEN_FAILED
112 ERROR_DISK_FULL
206 ERROR_FILENAME_EXCED_RANGE
231 ERROR_PIPE_BUSY
If you refer back to the definition of Dos Open at the start of this
message, you will see that 'ulrc' has the data type 'APIRET'. Here you
have one of the "cutesy" things in C, which resolves itself into 'ulong',
which in turn means "unsigned long binary integer", which is a 32-bit
binary integer. Now we know it is 'ulong' due to another "cutesy"
habit in C of prefixing variable names with an abbreviated data type,
in this instance the 'ul' of 'ulrc'. So 'ulrc' is a 32-bit binary integer
which can only assume zero and positive values.
It remains important for us in our specification and writing of DosOpen
in our OS/2 replacement kernel to "control" the values that get
assigned to 'ulrc'. To do this we will create a unique name for this
variable, DosOpen_rc. In the specification (and code) we will have a
data definition of 'ulong DosOpen_rc;'.
Now we take a break. In PL/I, another third generation language, we
would write 'ulong DosOpen_rc" as 'dcl DosOpen_rc fixed bin (32)
unsigned;'. In PL/E, a fourth generation language, we would write
'ulong DosOpen_rc' as 'dcl DosOpen_rc fixed bin (32) unsigned range
(0, 2, 3, 4, 5, 12, 26, 32, 36, 82, 87, 99, 108, 110, 112, 206, 231);'.
The 'range' attribute specifies the possible values that the defined
variable may receive as a "target", i.e. on the lefthand side of an
assignment statement. Any attempt to "assign" it a value not on the
'range' list raises a "RANGE error exception".
In logic programming terminolgoy this 'range' attribute constitutes a
"rule" which the software tool "enforces". In effect any assignment
statement involving 'DosOpen_rc', e.g. 'DosOpen_rc = value;', functions
as an "assertion" to the software tool which checks its "truth" or
"falsity". That means it checks, or rather "pre-checks" the resulting
value before assigning it to see if it is among the list of values. If it is,
the proof process returns "true" and the assignment occurs. If it is
not, the proof process returns "false" and raises the "RANGE error"
exception.
The net result of this is that the software tool insures that no
"invalid" return code ever gets passed to an invoking application. In
this particular instance of the source code for the API, it would not
allow its compilation. It would not produce executable code unless
"all" lefthand or target instances of 'DosOpen_rc" were "true".
Now consider the two possible forms of a righthand expression that
provides a value to 'DosOpen_rc'.
In the first form the righthand side consists of a constant: 'DosOpen_rc
= 32;'. Here the software tool can "immediately" prove its truth or
falsity, in this instance "true". If, however, we have something like
'DosOpen_rc = a + b + c;' where 'a, b, and c' are themselves variable
names, the software tool cannot prove the truth or falsity of the
result, the expression evaluation, until it has completed. That means
injecting the source code for the proof process after expression
evaluation and before assigning it.
That means through the use of this fourth generation software tool it
is "impossible" for a programmer to write source code which will pass
an invalid value to 'DosOpen_rc'. Remember there are only two forms
of logical errors possible, commission and omission. Here we have a
means of "preventing" or "catching" a subset of commission errors
automatically within software before they ever get to an application.
The truth is that we have hundreds and possible thousands of rules
which, one, a programmer must know, and, two, a programmer must
unerringly follow in writing source code. In a fourth generation
language like PL/E with the 'range' attribute we can express these
rules as part of the data definition. In that manner if a programmer
doesn't know or forgets a rule, the software always knows and never
forgets.
Due to the fact that the software when necessary will inject the
necessary "proof" source code, meaning the programmer will not have
to write it, and will "remind" the programmer through raising an
exception condition of any violation of the "data rules", you have
some idea why I refer to the software tool as the "Developer's
Assistant".
Even if you don't consider yourself a programmer, I hope you begin to
see what fourth generation tools bring to the table over third
generation toward improving software quality. At this point in our
software history no one should be writing either operating systems or
applications in third generation programming languages. Thus the
need for something like PL/E (SL/I) and the 'range' data attribute.
Expand Messages
Lynn H. Maxson
Jul 26, 2004
Copied directly from the OS/2 Control Program Programming for
"DosOpen--Syntax" adjusted for editor word wrap.
Opens a new file, an existing file, or a replacement for an existing
file. An open file can have extended attributes.
#define INCL_DOSFILEMGR
#include <os2.h>
PSZ pszFileName; /* Address of the ASCIIZ path name of
the file or device to be opened. */
PHFILE pHf; /* Address of the handle for the file. */
PULONG pulAction; /* Address of the variable that
receives the value that specifies the
action taken by the DosOpen function. */
ULONG cbFile; /* New logical size of the file (end of data,
EOD), in bytes. */
ULONG ulAttribute; /* File attribute information. */
ULONG fsOpenFlags; /* The action to be taken depending on
whether the file exists or does not exist.
*/
ULONG fsOpenMode; /* The mode of the open function. Possible
values are shown in the following list: */
PEAOP2 peaop2; /* Extended attributes. */
APIRET ulrc; /* Return Code. */
ulrc = DosOpen(pszFileName, pHf, pulAction,
cbFile, ulAttribute, fsOpenFlags,
fsOpenMode, peaop2);
This function (API) gets invoked within an "assignment" statement
which consists of two parts separated by the '=' character. This
follows the "standard" form of most third generation HLLs (High Level
Language). The variable to the left of the '=' sign, in this instance
'ulrc', receives the value of evaluating the expression to the right of
the '=' sign, in this instance the invocation of the DosOpen API.
In general then the assignment statement has the form of "left-hand =
right-hand" or "variable = expression" or "target = source". In every
instance the "evaluation" of the expression (source), the result, gets
placed into the variable (target), in this instance 'ulrc'.
Remember our interest in this API lies not in using it in an application,
but in providing it for an application's use. In practice we have an API
layer which functions as an interface between the "replacement"
kernel and the applications.
Perhaps we should begin with 'ulrc' and the values, the return codes,
it can have. Again we take from the OS/2 Control Program manual:
ulrc (APIRET) - returns
Return Code.
DosOpen returns one of the following values:
0 NO_ERROR
2 ERROR_FILE_NOT_FOUND
3 ERROR_PATH_NOT_FOUND
4 ERROR_TOO_MANY_OPEN_FILES
5 ERROR_ACCESS_DENIED
12 ERROR_INVALID_ACCESS
26 ERROR_NOT_DOS_DISK
32 ERROR_SHARING_VIOLATION
36 ERROR_SHARING_BUFFER_EXCEEDED
82 ERROR_CANNOT_MAKE
87 ERROR_INVALID_PARAMETER
99 ERROR_DEVICE_IN_USE
108 ERROR_DRIVE_LOCKED
110 ERROR_OPEN_FAILED
112 ERROR_DISK_FULL
206 ERROR_FILENAME_EXCED_RANGE
231 ERROR_PIPE_BUSY
If you refer back to the definition of Dos Open at the start of this
message, you will see that 'ulrc' has the data type 'APIRET'. Here you
have one of the "cutesy" things in C, which resolves itself into 'ulong',
which in turn means "unsigned long binary integer", which is a 32-bit
binary integer. Now we know it is 'ulong' due to another "cutesy"
habit in C of prefixing variable names with an abbreviated data type,
in this instance the 'ul' of 'ulrc'. So 'ulrc' is a 32-bit binary integer
which can only assume zero and positive values.
It remains important for us in our specification and writing of DosOpen
in our OS/2 replacement kernel to "control" the values that get
assigned to 'ulrc'. To do this we will create a unique name for this
variable, DosOpen_rc. In the specification (and code) we will have a
data definition of 'ulong DosOpen_rc;'.
Now we take a break. In PL/I, another third generation language, we
would write 'ulong DosOpen_rc" as 'dcl DosOpen_rc fixed bin (32)
unsigned;'. In PL/E, a fourth generation language, we would write
'ulong DosOpen_rc' as 'dcl DosOpen_rc fixed bin (32) unsigned range
(0, 2, 3, 4, 5, 12, 26, 32, 36, 82, 87, 99, 108, 110, 112, 206, 231);'.
The 'range' attribute specifies the possible values that the defined
variable may receive as a "target", i.e. on the lefthand side of an
assignment statement. Any attempt to "assign" it a value not on the
'range' list raises a "RANGE error exception".
In logic programming terminolgoy this 'range' attribute constitutes a
"rule" which the software tool "enforces". In effect any assignment
statement involving 'DosOpen_rc', e.g. 'DosOpen_rc = value;', functions
as an "assertion" to the software tool which checks its "truth" or
"falsity". That means it checks, or rather "pre-checks" the resulting
value before assigning it to see if it is among the list of values. If it is,
the proof process returns "true" and the assignment occurs. If it is
not, the proof process returns "false" and raises the "RANGE error"
exception.
The net result of this is that the software tool insures that no
"invalid" return code ever gets passed to an invoking application. In
this particular instance of the source code for the API, it would not
allow its compilation. It would not produce executable code unless
"all" lefthand or target instances of 'DosOpen_rc" were "true".
Now consider the two possible forms of a righthand expression that
provides a value to 'DosOpen_rc'.
In the first form the righthand side consists of a constant: 'DosOpen_rc
= 32;'. Here the software tool can "immediately" prove its truth or
falsity, in this instance "true". If, however, we have something like
'DosOpen_rc = a + b + c;' where 'a, b, and c' are themselves variable
names, the software tool cannot prove the truth or falsity of the
result, the expression evaluation, until it has completed. That means
injecting the source code for the proof process after expression
evaluation and before assigning it.
That means through the use of this fourth generation software tool it
is "impossible" for a programmer to write source code which will pass
an invalid value to 'DosOpen_rc'. Remember there are only two forms
of logical errors possible, commission and omission. Here we have a
means of "preventing" or "catching" a subset of commission errors
automatically within software before they ever get to an application.
The truth is that we have hundreds and possible thousands of rules
which, one, a programmer must know, and, two, a programmer must
unerringly follow in writing source code. In a fourth generation
language like PL/E with the 'range' attribute we can express these
rules as part of the data definition. In that manner if a programmer
doesn't know or forgets a rule, the software always knows and never
forgets.
Due to the fact that the software when necessary will inject the
necessary "proof" source code, meaning the programmer will not have
to write it, and will "remind" the programmer through raising an
exception condition of any violation of the "data rules", you have
some idea why I refer to the software tool as the "Developer's
Assistant".
Even if you don't consider yourself a programmer, I hope you begin to
see what fourth generation tools bring to the table over third
generation toward improving software quality. At this point in our
software history no one should be writing either operating systems or
applications in third generation programming languages. Thus the
need for something like PL/E (SL/I) and the 'range' data attribute.
Re: [osFree] Programming: Part 2
#1040 Re: [osFree] Programming: Part 2
Expand Messages
Daniel Caetano
Jul 26, 2004
On Mon, 26 Jul 2004 08:15:51 -0700 (PDT), Lynn H. Maxson wrote:
> Copied directly from the OS/2 Control Program Programming for
> "DosOpen--Syntax" adjusted for editor word wrap.
(...)
>Even if you don't consider yourself a programmer, I hope you begin to
>see what fourth generation tools bring to the table over third
>generation toward improving software quality. At this point in our
>software history no one should be writing either operating systems or
>applications in third generation programming languages. Thus the
>need for something like PL/E (SL/I) and the 'range' data attribute.
Interesting theoretical concept. Anyway, the only reason IBM defined
a common set of error messages and a common type for return codes is
the fact we can use one error message display function only.
Of course your approach do not break the "common set of error
messages". I don't know PL/I or PL/E (besides a few texts I
read about in internet) but if you define them as different types
in C, you'll fall into nonsense type casts.
Also, I think the approach must be at least somewhat pratical.
I understand and love the value of projects and do understand the
need to be strict when designing an API, but there is the need
to not forget about "how things will be used".
Even if there is no problem between using different types in
PL/I or PL/E, users will use it to program whatever language
they wants to... and in C different types will cause some trouble.
In the worst case, it'll be bizarre to declare fifteen variables
for error codes because you use fifteen system calls that returns
error codes from a different sort.
I understand that fixating the values available for return error
codes will difficult bugs, but this is the reason we never should
use the numerical values, but their defined macros instead. It's
just a matter of pratice. And if there will be an API layer that
will make those different types act like original APIRET ulong
in a "seamless" fashion, what is the point of using it?
As a conclusion, I do not think that the APIRET type being a
simple unsigned long is a real problem. At least not if the
programmer really knows (and test) whatever functions he is
programming (bottom-up test aproach). Anyway, if it will be
implemented, then the API layer must make it work just as the
original APIs, or we'll run into trouble when compiling old
apps (sort of "cannot convert from DosOpen_rc to APIRET"
and so on, forcing the use of type casts).
But of course this is only my 2 cents about this topic and,
of course, I may be wrong. In that case, please forget about
this message.
[]'s
Daniel Caetano
daniel@...
http://www.caetano.eng.br/
Expand Messages
Daniel Caetano
Jul 26, 2004
On Mon, 26 Jul 2004 08:15:51 -0700 (PDT), Lynn H. Maxson wrote:
> Copied directly from the OS/2 Control Program Programming for
> "DosOpen--Syntax" adjusted for editor word wrap.
(...)
>Even if you don't consider yourself a programmer, I hope you begin to
>see what fourth generation tools bring to the table over third
>generation toward improving software quality. At this point in our
>software history no one should be writing either operating systems or
>applications in third generation programming languages. Thus the
>need for something like PL/E (SL/I) and the 'range' data attribute.
Interesting theoretical concept. Anyway, the only reason IBM defined
a common set of error messages and a common type for return codes is
the fact we can use one error message display function only.
Of course your approach do not break the "common set of error
messages". I don't know PL/I or PL/E (besides a few texts I
read about in internet) but if you define them as different types
in C, you'll fall into nonsense type casts.
Also, I think the approach must be at least somewhat pratical.
I understand and love the value of projects and do understand the
need to be strict when designing an API, but there is the need
to not forget about "how things will be used".
Even if there is no problem between using different types in
PL/I or PL/E, users will use it to program whatever language
they wants to... and in C different types will cause some trouble.
In the worst case, it'll be bizarre to declare fifteen variables
for error codes because you use fifteen system calls that returns
error codes from a different sort.
I understand that fixating the values available for return error
codes will difficult bugs, but this is the reason we never should
use the numerical values, but their defined macros instead. It's
just a matter of pratice. And if there will be an API layer that
will make those different types act like original APIRET ulong
in a "seamless" fashion, what is the point of using it?
As a conclusion, I do not think that the APIRET type being a
simple unsigned long is a real problem. At least not if the
programmer really knows (and test) whatever functions he is
programming (bottom-up test aproach). Anyway, if it will be
implemented, then the API layer must make it work just as the
original APIs, or we'll run into trouble when compiling old
apps (sort of "cannot convert from DosOpen_rc to APIRET"
and so on, forcing the use of type casts).
But of course this is only my 2 cents about this topic and,
of course, I may be wrong. In that case, please forget about
this message.
[]'s
Daniel Caetano
daniel@...
http://www.caetano.eng.br/
Re: [osFree] Programming: Part 2
#1041 Re: [osFree] Programming: Part 2
Expand Messages
Lynn H. Maxson
Jul 26, 2004
First, let me thank Daniel Caetano for his remarks. Sometimes
you're not sure if anyone reads long messages, especially
those offered as tutorials.
You will note in his comments the number of references to
"programmer checking" as if all a programmer had to do was
check his code and in that process uncover "all" his errors. No
way, Jose. If you want to be practical, that has serious
imperfections. Otherwise software quality and reliability
would not remain as priority issues.
> I don't know PL/I or PL/E (besides a few texts I
>read about in internet) but if you define them as different
types
>in C, you'll fall into nonsense type casts.
No, C has already in the "false" name of extensible
programming created "non-sense" data types, of which
APIRET is only one amongst many.
If you want to write multi-platform applications, multiple
hardware, multiple software, then you want data types that
transfer unchanged across them. You particularly want it if
you are exchanging data files from one to another. Now I
don't want to go into the history of C and UNIX, but I will point
to its record of pure "crap" early on in terms of software and
data portability.
Now why? It comes down to 'int' the devil, probably the
biggest mistake ever introduced into programming. Now for
the non-programmers reading this 'int' stands for 'binary
integer" whose precision in number of bits represented was
implementation defined by K&R, the authors of C. That meant
if you had a 16-bit machine, 'int' was 16-bits; 32-bit machine,
32 bits; 18-bit machine, 18 bits, 24-bit machine, 24 bits; etc..
That it means 32-bit only today, i.e. that we have a defined
standard, came from the shock within the UNIX community
when they discovered not only did they not have the
expected portability in their C source code, but they also
didn't have it in their data. Those users had come from an IBM
mainframe environment, based on the S/360 with its multiple
models and operating systems, in which such portability did
exist.
Now why? Because both COBOL and PL/I supported
programmer-defined precision. Thus if a PL/I programmer
defined a variable as 'fixed bin (31);' it didn't make any
difference the "word size" of the machine as the software
compensated for it. It certainly leads to data portability.
Besides the "stupidity" of 'int' we also have the "arrogance": it
assumes only the existence of binary integers. Yet the Intel
processors in the machines used by those reading this have
instruction support for decimal integers...which C does not
offer as a "native" data type, only in libraries. Thus the
statement 'x = 2;' where x is defined as 'int' says assign a
decimal constant to a binary integer instead of saying '0010b'.
The arrogance extends to the failure to support binary "real"
numbers, i.e. those with fractional components. That says if
you have measurements in 1/2, 1/4, 1/8, 1/16, 1/32, or 1/64 of
an inch, the programmer has to write the interpretive code
maintaining the binary point in output with no means of
defining it in the data. In PL/I to define measurements down
to a 1/64 of an inch you would simply define the variable as
'fixed bin (31, 6);". The same software and the same data
would port.
If you wanted to use decimal integers, you can't in C without
library support: it's not a "native" C data type. For the same
reason you can't use decimal real numbers, those with
fractional portions.
So in PL/I if I have a 24-bit word machine, I can define a
variable as 'fixed bin (24);' whose source and data will port to
any machine supporting PL/I. That, my friend, is portability.
The only "portability" defined by K&R lay in crippling a
language so badly that anyone could write a compiler in it for
any machine. That made it convenient as an exercise for
computer science students in a semester class.
As we go on we will run into other significant differences that
eventually you will look at C and its derivatives with disgust,
realizing the unnecessary cost and grief they bring to writing
software.
I would remind everyone that here we are talking about the
source code that appears in replacement kernel. It's within
that shell that we use a variable name 'DosOpen_rc', not in
any application that invokes it. Thus no incompatibility with
existing applications exists. They lie above the API layer,
while we are in the process of defining that which lies below.
Expand Messages
Lynn H. Maxson
Jul 26, 2004
First, let me thank Daniel Caetano for his remarks. Sometimes
you're not sure if anyone reads long messages, especially
those offered as tutorials.
You will note in his comments the number of references to
"programmer checking" as if all a programmer had to do was
check his code and in that process uncover "all" his errors. No
way, Jose. If you want to be practical, that has serious
imperfections. Otherwise software quality and reliability
would not remain as priority issues.
> I don't know PL/I or PL/E (besides a few texts I
>read about in internet) but if you define them as different
types
>in C, you'll fall into nonsense type casts.
No, C has already in the "false" name of extensible
programming created "non-sense" data types, of which
APIRET is only one amongst many.
If you want to write multi-platform applications, multiple
hardware, multiple software, then you want data types that
transfer unchanged across them. You particularly want it if
you are exchanging data files from one to another. Now I
don't want to go into the history of C and UNIX, but I will point
to its record of pure "crap" early on in terms of software and
data portability.
Now why? It comes down to 'int' the devil, probably the
biggest mistake ever introduced into programming. Now for
the non-programmers reading this 'int' stands for 'binary
integer" whose precision in number of bits represented was
implementation defined by K&R, the authors of C. That meant
if you had a 16-bit machine, 'int' was 16-bits; 32-bit machine,
32 bits; 18-bit machine, 18 bits, 24-bit machine, 24 bits; etc..
That it means 32-bit only today, i.e. that we have a defined
standard, came from the shock within the UNIX community
when they discovered not only did they not have the
expected portability in their C source code, but they also
didn't have it in their data. Those users had come from an IBM
mainframe environment, based on the S/360 with its multiple
models and operating systems, in which such portability did
exist.
Now why? Because both COBOL and PL/I supported
programmer-defined precision. Thus if a PL/I programmer
defined a variable as 'fixed bin (31);' it didn't make any
difference the "word size" of the machine as the software
compensated for it. It certainly leads to data portability.
Besides the "stupidity" of 'int' we also have the "arrogance": it
assumes only the existence of binary integers. Yet the Intel
processors in the machines used by those reading this have
instruction support for decimal integers...which C does not
offer as a "native" data type, only in libraries. Thus the
statement 'x = 2;' where x is defined as 'int' says assign a
decimal constant to a binary integer instead of saying '0010b'.
The arrogance extends to the failure to support binary "real"
numbers, i.e. those with fractional components. That says if
you have measurements in 1/2, 1/4, 1/8, 1/16, 1/32, or 1/64 of
an inch, the programmer has to write the interpretive code
maintaining the binary point in output with no means of
defining it in the data. In PL/I to define measurements down
to a 1/64 of an inch you would simply define the variable as
'fixed bin (31, 6);". The same software and the same data
would port.
If you wanted to use decimal integers, you can't in C without
library support: it's not a "native" C data type. For the same
reason you can't use decimal real numbers, those with
fractional portions.
So in PL/I if I have a 24-bit word machine, I can define a
variable as 'fixed bin (24);' whose source and data will port to
any machine supporting PL/I. That, my friend, is portability.
The only "portability" defined by K&R lay in crippling a
language so badly that anyone could write a compiler in it for
any machine. That made it convenient as an exercise for
computer science students in a semester class.
As we go on we will run into other significant differences that
eventually you will look at C and its derivatives with disgust,
realizing the unnecessary cost and grief they bring to writing
software.
I would remind everyone that here we are talking about the
source code that appears in replacement kernel. It's within
that shell that we use a variable name 'DosOpen_rc', not in
any application that invokes it. Thus no incompatibility with
existing applications exists. They lie above the API layer,
while we are in the process of defining that which lies below.
Re: [osFree] Programming: Part 2
#1042 Re: [osFree] Programming: Part 2
Expand Messages
Daniel Caetano
Jul 26, 2004
On Mon, 26 Jul 2004 10:52:53 -0700 (PDT), Lynn H. Maxson wrote:
>Now why? It comes down to 'int' the devil, probably the
>biggest mistake ever introduced into programming. Now for
>the non-programmers reading this 'int' stands for 'binary
>integer" whose precision in number of bits represented was
>implementation defined by K&R, the authors of C. That meant
>if you had a 16-bit machine, 'int' was 16-bits; 32-bit machine,
>32 bits; 18-bit machine, 18 bits, 24-bit machine, 24 bits; etc..
Since long I have been using types like (u)int8, (u)int16, (u)int32...
Which are defined differently for each hardware. No 12, 18 or 24 bits,
though... But I agree this is a "patch" for a possible mistake under
the language's design. In fact, maybe a design option, not a real
mistake.
Anyway, someone will need to provide free (and probably open)
and simple to use compilers for PL/I or PL/E, and also a easy
course and documents.
There is no use in a perfect project using those languages (or any
others) if we do not have compilers and, more important, people
able to use them.
I'm a programmer since I was a kid. I know several languages and
know many reasons to use one or another for different tasks. But until
today no one was able to convince me that "the programmer do not
need to think... let the compiler do it" and then you have stupid
and tied languages that doesn't allow the *real* programmer (the
one that really knows what he is doing) do nothing.
And I'm not talking about major decisions only.
>Now why? Because both COBOL and PL/I supported
>programmer-defined precision. Thus if a PL/I programmer
>defined a variable as 'fixed bin (31);' it didn't make any
>difference the "word size" of the machine as the software
>compensated for it. It certainly leads to data portability.
I heard once the idea of int was the largest type of data the
processor could work operate totally by hardware. I don't know
when this concept was introduced, nor if it is real. But I simply
avoid using int. That's all.
>Besides the "stupidity" of 'int' we also have the "arrogance": it
>assumes only the existence of binary integers. Yet the Intel
>processors in the machines used by those reading this have
>instruction support for decimal integers...which C does not
>offer as a "native" data type, only in libraries. Thus the
>statement 'x = 2;' where x is defined as 'int' says assign a
>decimal constant to a binary integer instead of saying '0010b'.
If the programmer knows what is happening, no problem.
The misinformation is much more problematic than "not supporting"
something.
>The arrogance extends to the failure to support binary "real"
>numbers, i.e. those with fractional components. That says if
>you have measurements in 1/2, 1/4, 1/8, 1/16, 1/32, or 1/64 of
>an inch, the programmer has to write the interpretive code
>maintaining the binary point in output with no means of
>defining it in the data. In PL/I to define measurements down
>to a 1/64 of an inch you would simply define the variable as
>'fixed bin (31, 6);". The same software and the same data
>would port.
Anyone told you that C was not meant to do what Cobol or
FORTRAN do? For that uses we already had Cobol or FORTRAN.
C is for general programing, not mathematical (or any other
'specific' programing).
The real problem is people want to program without knowing
the hardware... Everyone knows general hardware knows that
"decimal integers" are uncommon to actual hardwares and real
numbers are a fiction. I do not think is wise to cover those
aspects from the programmer, when we are talking of such
low-level programming as an operating system. My optinion
though.
You talk always in cost and easy to write. I have been reading
this talk for years. I will always talk about best code, fastest
possible code, the better optimized and low level approach, while
keeping *most* portability. It is of no use "optimized for nowhere"
kind of programming.
There is no point in a language supporting things that are
not present in common hardwares, unless it is so commonly used
that we cannot live without them. That is not the case with
generic precision numbers, neither decimal integer and even
less with real numbers. Man! We are talking about Operating
System, not about physics or mathematical programming!
>I would remind everyone that here we are talking about the
>source code that appears in replacement kernel. It's within
>that shell that we use a variable name 'DosOpen_rc', not in
>any application that invokes it. Thus no incompatibility with
>existing applications exists. They lie above the API layer,
>while we are in the process of defining that which lies below.
And then the above API layer makes the native applications
behave just like nowadays ODIN applications. Nice. A microkernel
without the advantages of microkernel.
Well, my last message on this topic. People talk a lot more
than necessary. You like one thing. I like other. Since I'll
not have much time to work on this project (and I hope you
will), do it your way. But keep in mind there is no panacea.
Every approach has advantages and drawbacks. This is real for
C, this is real for PL/n.
[]'s
Daniel Caetano
daniel@...
http://www.caetano.eng.br/
Expand Messages
Daniel Caetano
Jul 26, 2004
On Mon, 26 Jul 2004 10:52:53 -0700 (PDT), Lynn H. Maxson wrote:
>Now why? It comes down to 'int' the devil, probably the
>biggest mistake ever introduced into programming. Now for
>the non-programmers reading this 'int' stands for 'binary
>integer" whose precision in number of bits represented was
>implementation defined by K&R, the authors of C. That meant
>if you had a 16-bit machine, 'int' was 16-bits; 32-bit machine,
>32 bits; 18-bit machine, 18 bits, 24-bit machine, 24 bits; etc..
Since long I have been using types like (u)int8, (u)int16, (u)int32...
Which are defined differently for each hardware. No 12, 18 or 24 bits,
though... But I agree this is a "patch" for a possible mistake under
the language's design. In fact, maybe a design option, not a real
mistake.
Anyway, someone will need to provide free (and probably open)
and simple to use compilers for PL/I or PL/E, and also a easy
course and documents.
There is no use in a perfect project using those languages (or any
others) if we do not have compilers and, more important, people
able to use them.
I'm a programmer since I was a kid. I know several languages and
know many reasons to use one or another for different tasks. But until
today no one was able to convince me that "the programmer do not
need to think... let the compiler do it" and then you have stupid
and tied languages that doesn't allow the *real* programmer (the
one that really knows what he is doing) do nothing.
And I'm not talking about major decisions only.
>Now why? Because both COBOL and PL/I supported
>programmer-defined precision. Thus if a PL/I programmer
>defined a variable as 'fixed bin (31);' it didn't make any
>difference the "word size" of the machine as the software
>compensated for it. It certainly leads to data portability.
I heard once the idea of int was the largest type of data the
processor could work operate totally by hardware. I don't know
when this concept was introduced, nor if it is real. But I simply
avoid using int. That's all.
>Besides the "stupidity" of 'int' we also have the "arrogance": it
>assumes only the existence of binary integers. Yet the Intel
>processors in the machines used by those reading this have
>instruction support for decimal integers...which C does not
>offer as a "native" data type, only in libraries. Thus the
>statement 'x = 2;' where x is defined as 'int' says assign a
>decimal constant to a binary integer instead of saying '0010b'.
If the programmer knows what is happening, no problem.
The misinformation is much more problematic than "not supporting"
something.
>The arrogance extends to the failure to support binary "real"
>numbers, i.e. those with fractional components. That says if
>you have measurements in 1/2, 1/4, 1/8, 1/16, 1/32, or 1/64 of
>an inch, the programmer has to write the interpretive code
>maintaining the binary point in output with no means of
>defining it in the data. In PL/I to define measurements down
>to a 1/64 of an inch you would simply define the variable as
>'fixed bin (31, 6);". The same software and the same data
>would port.
Anyone told you that C was not meant to do what Cobol or
FORTRAN do? For that uses we already had Cobol or FORTRAN.
C is for general programing, not mathematical (or any other
'specific' programing).
The real problem is people want to program without knowing
the hardware... Everyone knows general hardware knows that
"decimal integers" are uncommon to actual hardwares and real
numbers are a fiction. I do not think is wise to cover those
aspects from the programmer, when we are talking of such
low-level programming as an operating system. My optinion
though.
You talk always in cost and easy to write. I have been reading
this talk for years. I will always talk about best code, fastest
possible code, the better optimized and low level approach, while
keeping *most* portability. It is of no use "optimized for nowhere"
kind of programming.
There is no point in a language supporting things that are
not present in common hardwares, unless it is so commonly used
that we cannot live without them. That is not the case with
generic precision numbers, neither decimal integer and even
less with real numbers. Man! We are talking about Operating
System, not about physics or mathematical programming!
>I would remind everyone that here we are talking about the
>source code that appears in replacement kernel. It's within
>that shell that we use a variable name 'DosOpen_rc', not in
>any application that invokes it. Thus no incompatibility with
>existing applications exists. They lie above the API layer,
>while we are in the process of defining that which lies below.
And then the above API layer makes the native applications
behave just like nowadays ODIN applications. Nice. A microkernel
without the advantages of microkernel.
Well, my last message on this topic. People talk a lot more
than necessary. You like one thing. I like other. Since I'll
not have much time to work on this project (and I hope you
will), do it your way. But keep in mind there is no panacea.
Every approach has advantages and drawbacks. This is real for
C, this is real for PL/n.
[]'s
Daniel Caetano
daniel@...
http://www.caetano.eng.br/
Re: [osFree] Programming: Part 2
#1043 Re: [osFree] Programming: Part 2
Expand Messages
Frank Griffin
Jul 26, 2004
Both Lynn and Daniel have valid points here.
Lynn's point about programmer-defined datatypes is well-taken. As
Daniel points out, the hardware-specific datatypes in C were designed to
avoid separating programmers from the realization of the fact that some
datatypes work a whole lot better than others on particular hardware.
As Lynn points out, PL/x allows the programmer to say exactly what he
means and let the compiler and/or the runtime worry about how close that
is to the hardware. Whether that's a blessing or a curse depends on the
quality of the compiler and the object of the programmer.
In the case of application programming, portability is king. You want,
in the words of our Java brethren, to "write once, run anywhere". If
somebody builds a platform on which your datatypes are unsupported or
supported less-optimally than others, then you expect that the runtime
will make this transparent to you. If performance suffers, well, then,
the idiots who chose this system can just choose something with better
hardware support, and all will be right.
In the case of Operating System programming, you're a little less
tolerant of bad runtimes. In most cases, a significant part of the OS
cannot deal with a runtime, and has no option but to deal with the
actual datatypes supported by the hardware. In this case, the compiled
code has to reference native datatypes.
The APIRET case does not strike me as bad design. The underlying idea
is that you will have a set of header files specific to each platform on
which the OS is implemented, and these header files will map APIRET to a
native type appropriate to that platform. Your contract with the API
that implements APIRET is that you expect it to contain unsigned integer
values, and the API implementation determines whether this is mapped to
a ULONG or an "unsigned long long" or an "unsigned int", or whatever.
Lynn's defense of the PL/x richness in defining abstract numerical
models such as BINARY FIXED and DECIMAL FIXED is accurate, but as he
also says (perhaps without meaning to), this is an *application*
consideration. Few operating system components deal with fractional
integers or floating-point values, which is why languages popular with
OS developers give them short shrift.
There's another facet of Lynn's argument that bothers me more, and
that's the "let the compiler catch everything" mentality. That is very
workable for applications programming, but it falls apart if the
language in question is to be used to produce server or middleware code.
The most recent example of this was Enterprise Java.
Sun's original take on Java was that *everything* had to be specified at
compile time, and that the compiler would catch everything. This fell
apart when people started trying to build Enterprise Java (J2EE)
containers, because the container code had to deal with arbitrary
application classes. It had to be able to load them, and dynamically
interrogate them as to their methods (functions), calling signatures,
return values, and so forth. Most importantly, it had to be able to
intercept calls to these methods.
To see an equivalent example in C, suppose we have a platform with two
floating point implementations: float and double. Now suppose that the
runtime ("container") wants to intercept all cosine calls and log them.
One way is to write several versions of the intercept, one for each of
the method signatures, e.g. one for "float cos( float x )" and one for
"double cos( double x )". Another would be to provide an
introspection mechanism that allows a single intercept to say "here's a
function, tell me the parameter types and return type". This intercept
would then set up the proper parameter types, call the real function,
take the return value, create the log entry, and return the return value
to the caller.
This second technique isn't feasible if the compiler requires that you
know what your return datatype is when you compile the code. It also
requires that you know the parameter types for the method you are
calling. This just doesn't work if you need to use the language for
anything that requires these things to be determined dynamically at runtime.
Now take this example and expand it to an arbitrary function. It my be
that you need to trace entry and exit to all functions without modifying
the function source code, or something like that. You might be able to
handle the cosine case with "case" logic, but that is impossible for the
general case.
Java figured this out around version 1.3, which is why every J2EE
container implementation requires a minimum of 1.3 to run. 1.3 added
support for "proxies" (dynamically constructed intercepts which could
mimic the calling signature of any class/method).
To get back to the PL/x versus C issue, neither one allows this sort of
thing. You can only do this by requiring your compiler to record method
or function calling signature information in the object code, and then
requiring the runtime to support an API which allows you to query this
information. C certainly does not do this, but then, to my knowledge,
neither does PL/x. PL/x *does* allow function overloading (a la C++)
where the compiler will choose among multiple functions of the same name
based on the calling signature used in the calling code, but that's
hardwired into the library runtime and is not the same as providing true
introspection.
I'm not saying that lack of introspection ought to disqualify PL/x as an
implementation language for this project (although the fact that it
costs a considerable sum of money and is probably no longer generally
available might). I'm just saying that once you get into writing OS or
middleware code in higher-level languages, there is never an ideal
choice. C has achieved acceptance because, in spite of the theoretical
distaste it leaves in some people's mouths, it is flawed enough to allow
those with requirements beyond its capabilities to work around the
limitations. I'm not sure that you can say the same about PL/x.
Expand Messages
Frank Griffin
Jul 26, 2004
Both Lynn and Daniel have valid points here.
Lynn's point about programmer-defined datatypes is well-taken. As
Daniel points out, the hardware-specific datatypes in C were designed to
avoid separating programmers from the realization of the fact that some
datatypes work a whole lot better than others on particular hardware.
As Lynn points out, PL/x allows the programmer to say exactly what he
means and let the compiler and/or the runtime worry about how close that
is to the hardware. Whether that's a blessing or a curse depends on the
quality of the compiler and the object of the programmer.
In the case of application programming, portability is king. You want,
in the words of our Java brethren, to "write once, run anywhere". If
somebody builds a platform on which your datatypes are unsupported or
supported less-optimally than others, then you expect that the runtime
will make this transparent to you. If performance suffers, well, then,
the idiots who chose this system can just choose something with better
hardware support, and all will be right.
In the case of Operating System programming, you're a little less
tolerant of bad runtimes. In most cases, a significant part of the OS
cannot deal with a runtime, and has no option but to deal with the
actual datatypes supported by the hardware. In this case, the compiled
code has to reference native datatypes.
The APIRET case does not strike me as bad design. The underlying idea
is that you will have a set of header files specific to each platform on
which the OS is implemented, and these header files will map APIRET to a
native type appropriate to that platform. Your contract with the API
that implements APIRET is that you expect it to contain unsigned integer
values, and the API implementation determines whether this is mapped to
a ULONG or an "unsigned long long" or an "unsigned int", or whatever.
Lynn's defense of the PL/x richness in defining abstract numerical
models such as BINARY FIXED and DECIMAL FIXED is accurate, but as he
also says (perhaps without meaning to), this is an *application*
consideration. Few operating system components deal with fractional
integers or floating-point values, which is why languages popular with
OS developers give them short shrift.
There's another facet of Lynn's argument that bothers me more, and
that's the "let the compiler catch everything" mentality. That is very
workable for applications programming, but it falls apart if the
language in question is to be used to produce server or middleware code.
The most recent example of this was Enterprise Java.
Sun's original take on Java was that *everything* had to be specified at
compile time, and that the compiler would catch everything. This fell
apart when people started trying to build Enterprise Java (J2EE)
containers, because the container code had to deal with arbitrary
application classes. It had to be able to load them, and dynamically
interrogate them as to their methods (functions), calling signatures,
return values, and so forth. Most importantly, it had to be able to
intercept calls to these methods.
To see an equivalent example in C, suppose we have a platform with two
floating point implementations: float and double. Now suppose that the
runtime ("container") wants to intercept all cosine calls and log them.
One way is to write several versions of the intercept, one for each of
the method signatures, e.g. one for "float cos( float x )" and one for
"double cos( double x )". Another would be to provide an
introspection mechanism that allows a single intercept to say "here's a
function, tell me the parameter types and return type". This intercept
would then set up the proper parameter types, call the real function,
take the return value, create the log entry, and return the return value
to the caller.
This second technique isn't feasible if the compiler requires that you
know what your return datatype is when you compile the code. It also
requires that you know the parameter types for the method you are
calling. This just doesn't work if you need to use the language for
anything that requires these things to be determined dynamically at runtime.
Now take this example and expand it to an arbitrary function. It my be
that you need to trace entry and exit to all functions without modifying
the function source code, or something like that. You might be able to
handle the cosine case with "case" logic, but that is impossible for the
general case.
Java figured this out around version 1.3, which is why every J2EE
container implementation requires a minimum of 1.3 to run. 1.3 added
support for "proxies" (dynamically constructed intercepts which could
mimic the calling signature of any class/method).
To get back to the PL/x versus C issue, neither one allows this sort of
thing. You can only do this by requiring your compiler to record method
or function calling signature information in the object code, and then
requiring the runtime to support an API which allows you to query this
information. C certainly does not do this, but then, to my knowledge,
neither does PL/x. PL/x *does* allow function overloading (a la C++)
where the compiler will choose among multiple functions of the same name
based on the calling signature used in the calling code, but that's
hardwired into the library runtime and is not the same as providing true
introspection.
I'm not saying that lack of introspection ought to disqualify PL/x as an
implementation language for this project (although the fact that it
costs a considerable sum of money and is probably no longer generally
available might). I'm just saying that once you get into writing OS or
middleware code in higher-level languages, there is never an ideal
choice. C has achieved acceptance because, in spite of the theoretical
distaste it leaves in some people's mouths, it is flawed enough to allow
those with requirements beyond its capabilities to work around the
limitations. I'm not sure that you can say the same about PL/x.
Re: [osFree] Programming: Part 2
#1044 Re: [osFree] Programming: Part 2
Expand Messages
Lynn H. Maxson
Jul 26, 2004
Again I would like to thank Daniel Caetano for his comments.
In particular for "There is no point in a language supporting
things that are not present in common hardwares, unless it is
so commonly used that we cannot live without them. That is
not the case with generic precision numbers, neither decimal
integer and even less with real numbers. Man! We are talking
about Operating System, not about physics or mathematical
programming!"
I would say we are talking about programming. We have real
world situations, the problem set. We encode them in
software, our solution set. Our objective is to have our
solution set map into the problem set. If the solution set, in
this instance C, does not contain the "native" data types of
the problem set, then it forces us into an aberration of
essentially modifying reality in order to fit in our fantasy.
We talk about operating systems. Some talk about
accounting, accounts receivable, payable, payroll, inventory,
order entry, physics, botany, chemistry, applied mathematics,
process control, simulation, and more. It's programming. It's
mapping the solution set, the software you write, into the
problem set, the reality with which we deal. We should never
have to change reality in order to satisfy our solution set.
I'm not here selling PL/I, PL/E, fourth generation languages, or
logic programming. I'm here to write, develop and maintain,
an OS/2 replacement package, beginning with the kernel, at a
par and at least equal to the pace that Linux, M$, IBM, Apple
and others maintain. Because I have neither the money nor
the number of people they commit to this, the last thing on
earth I want to do is use the same tools they do. If I do, then
I am committed to their level of resource consumption.
Now the reason they require the resources they consume, the
schedules they set, and the organization they manage all
derive from the language choices they have made as well as
the implementation of those choices. You just have to get
used to the idea that in order to compete with them in the
marketplace using the same languages and tools they do, then
you must commit to the same or more resources.
Now if I don't appear in a hurry to start coding in the same
language and with the same tools that they use, don't act
surprised. I'm not saying it can't be coded in C, C++, or C#.
I'm saying with the resources available to you, you can't
afford to code it in them. Frankly you can't afford to code it
in PL/I. In short you can't afford to code it in any third
generation language whose implementation does not involve
the two-stage proof engine of logic programming.
Then you have to face up to the awful truth that though you
know the characteristics of the requisite tools, they do not
exist. Some years ago after IBM began its painful withdrawal
from OS/2 many clamored for IBM to release the source code
to open source. Had it done so we would have come face to
face with raising and organizing the resources needed. If IBM
decided against the hundreds of millions of dollars annually it
spent on OS/2 support, what hope did a using community,
disorganized, disparate, and in disagreement have?
So if you know you can't compete at their level, then you
have to find the ability to compete at a level you can support.
If you understand the impact of tools and methods in common
use in software development and maintenance, then you
know you need to look elsewhere. That's what I did. It
resulted in the Warpicity Proposal at Warpstock '98 in Chicago.
You can do a google search on "Warpicity" (not Warpcity) and
read about that. The solution lies in logic programming. It's
that simple. It lies in a programming language based on logic
programming not crippled by using C as its starting point,
specifically 'int' as its choice for a binary variable.
I can't walk away from this when someone touts the
importance of optimized code generation and high
performance. To speak of either in the same breath as C
commits a contradiction. To add to it C++ or any OO language
which relies on incompatible class libraries makes it even
worse. You simply don't understand the impact on
optimization and performance that reliance on libraries
produces.
I have been programming in PL/I now for nearly 40 years. In
all that time I have never had to resort to assembly language
or a library to perform any operation capable of any
instruction on any machine. You simply do not understand just
how complete as a programming language PL/I is.
There's no reason to tolerate "code bloat" period. There's no
way in hell you can program in C or its derivatives without
acquiring it. The answer lies in logic programming. The only
thing a programming language, your means of writing the
solution set, has to do is to support the operators (processes)
and operands (data) of the reality, the problem set. It gets
somewhat difficult to keep a straight face when someone
touts a programming language that has no capability for bit
strings which is a "native" data type in every processor out
there.
Expand Messages
Lynn H. Maxson
Jul 26, 2004
Again I would like to thank Daniel Caetano for his comments.
In particular for "There is no point in a language supporting
things that are not present in common hardwares, unless it is
so commonly used that we cannot live without them. That is
not the case with generic precision numbers, neither decimal
integer and even less with real numbers. Man! We are talking
about Operating System, not about physics or mathematical
programming!"
I would say we are talking about programming. We have real
world situations, the problem set. We encode them in
software, our solution set. Our objective is to have our
solution set map into the problem set. If the solution set, in
this instance C, does not contain the "native" data types of
the problem set, then it forces us into an aberration of
essentially modifying reality in order to fit in our fantasy.
We talk about operating systems. Some talk about
accounting, accounts receivable, payable, payroll, inventory,
order entry, physics, botany, chemistry, applied mathematics,
process control, simulation, and more. It's programming. It's
mapping the solution set, the software you write, into the
problem set, the reality with which we deal. We should never
have to change reality in order to satisfy our solution set.
I'm not here selling PL/I, PL/E, fourth generation languages, or
logic programming. I'm here to write, develop and maintain,
an OS/2 replacement package, beginning with the kernel, at a
par and at least equal to the pace that Linux, M$, IBM, Apple
and others maintain. Because I have neither the money nor
the number of people they commit to this, the last thing on
earth I want to do is use the same tools they do. If I do, then
I am committed to their level of resource consumption.
Now the reason they require the resources they consume, the
schedules they set, and the organization they manage all
derive from the language choices they have made as well as
the implementation of those choices. You just have to get
used to the idea that in order to compete with them in the
marketplace using the same languages and tools they do, then
you must commit to the same or more resources.
Now if I don't appear in a hurry to start coding in the same
language and with the same tools that they use, don't act
surprised. I'm not saying it can't be coded in C, C++, or C#.
I'm saying with the resources available to you, you can't
afford to code it in them. Frankly you can't afford to code it
in PL/I. In short you can't afford to code it in any third
generation language whose implementation does not involve
the two-stage proof engine of logic programming.
Then you have to face up to the awful truth that though you
know the characteristics of the requisite tools, they do not
exist. Some years ago after IBM began its painful withdrawal
from OS/2 many clamored for IBM to release the source code
to open source. Had it done so we would have come face to
face with raising and organizing the resources needed. If IBM
decided against the hundreds of millions of dollars annually it
spent on OS/2 support, what hope did a using community,
disorganized, disparate, and in disagreement have?
So if you know you can't compete at their level, then you
have to find the ability to compete at a level you can support.
If you understand the impact of tools and methods in common
use in software development and maintenance, then you
know you need to look elsewhere. That's what I did. It
resulted in the Warpicity Proposal at Warpstock '98 in Chicago.
You can do a google search on "Warpicity" (not Warpcity) and
read about that. The solution lies in logic programming. It's
that simple. It lies in a programming language based on logic
programming not crippled by using C as its starting point,
specifically 'int' as its choice for a binary variable.
I can't walk away from this when someone touts the
importance of optimized code generation and high
performance. To speak of either in the same breath as C
commits a contradiction. To add to it C++ or any OO language
which relies on incompatible class libraries makes it even
worse. You simply don't understand the impact on
optimization and performance that reliance on libraries
produces.
I have been programming in PL/I now for nearly 40 years. In
all that time I have never had to resort to assembly language
or a library to perform any operation capable of any
instruction on any machine. You simply do not understand just
how complete as a programming language PL/I is.
There's no reason to tolerate "code bloat" period. There's no
way in hell you can program in C or its derivatives without
acquiring it. The answer lies in logic programming. The only
thing a programming language, your means of writing the
solution set, has to do is to support the operators (processes)
and operands (data) of the reality, the problem set. It gets
somewhat difficult to keep a straight face when someone
touts a programming language that has no capability for bit
strings which is a "native" data type in every processor out
there.
Re: [osFree] Programming: Part 2
#1045 Re: [osFree] Programming: Part 2
Expand Messages
Michal Necasek
Jul 26, 2004
Hide message history
On Mon, 26 Jul 2004 18:47:43 -0700 (PDT), Lynn H. Maxson wrote:
I'm sure Lynn will effectively ignore all I have to say, just like
he effectively ignores everything others say, but I thought I'd
point out some things anyway and then unsubscribe, because this is
really a waste of time.
>I would say we are talking about programming. We have real
>world situations, the problem set. We encode them in
>software, our solution set. Our objective is to have our
>solution set map into the problem set. If the solution set, in
>this instance C, does not contain the "native" data types of
>the problem set, then it forces us into an aberration of
>essentially modifying reality in order to fit in our fantasy.
>
So instead you're creating a complete fantasy? When writing an
operating system, the data types defined eg. by C match the problem
set perfectly. It may have escaped you, but CPU registers, addresses,
offsets etc. correspond to C types amazingly well - because C types
were modeled after them!
>I have been programming in PL/I now for nearly 40 years. In
>all that time I have never had to resort to assembly language
>or a library to perform any operation capable of any
>instruction on any machine. You simply do not understand just
>how complete as a programming language PL/I is.
>
How many operating systems have you written in PL/I? How many
operating systems have other people written in PL/I? How do I know
that it's even doable?
Also please point out one operating system for the x86 platform
(or some other common hardware) that was written entirely without
assembly language. I would be greatly surprised if such a thing
existed.
>There's no reason to tolerate "code bloat" period. There's no
>way in hell you can program in C or its derivatives without
>acquiring it. The answer lies in logic programming.
>
Proof? Please point us to successful operating systems written in
PL/I.
Or why don't you just show something relatively trivial? Say, an
OS boot loader written in PL/I. I think something like that would
greatly help convince people that PL/I is viable. If however you
can't produce such a thing, it might help convince people that PL/I
is just so much hot air.
Thank you for your attention, now I'm outta here.
Expand Messages
Michal Necasek
Jul 26, 2004
Hide message history
On Mon, 26 Jul 2004 18:47:43 -0700 (PDT), Lynn H. Maxson wrote:
I'm sure Lynn will effectively ignore all I have to say, just like
he effectively ignores everything others say, but I thought I'd
point out some things anyway and then unsubscribe, because this is
really a waste of time.
>I would say we are talking about programming. We have real
>world situations, the problem set. We encode them in
>software, our solution set. Our objective is to have our
>solution set map into the problem set. If the solution set, in
>this instance C, does not contain the "native" data types of
>the problem set, then it forces us into an aberration of
>essentially modifying reality in order to fit in our fantasy.
>
So instead you're creating a complete fantasy? When writing an
operating system, the data types defined eg. by C match the problem
set perfectly. It may have escaped you, but CPU registers, addresses,
offsets etc. correspond to C types amazingly well - because C types
were modeled after them!
>I have been programming in PL/I now for nearly 40 years. In
>all that time I have never had to resort to assembly language
>or a library to perform any operation capable of any
>instruction on any machine. You simply do not understand just
>how complete as a programming language PL/I is.
>
How many operating systems have you written in PL/I? How many
operating systems have other people written in PL/I? How do I know
that it's even doable?
Also please point out one operating system for the x86 platform
(or some other common hardware) that was written entirely without
assembly language. I would be greatly surprised if such a thing
existed.
>There's no reason to tolerate "code bloat" period. There's no
>way in hell you can program in C or its derivatives without
>acquiring it. The answer lies in logic programming.
>
Proof? Please point us to successful operating systems written in
PL/I.
Or why don't you just show something relatively trivial? Say, an
OS boot loader written in PL/I. I think something like that would
greatly help convince people that PL/I is viable. If however you
can't produce such a thing, it might help convince people that PL/I
is just so much hot air.
Thank you for your attention, now I'm outta here.
Methodology
#1046 Methodology
Expand Messages
John P Baker
Jul 26, 2004
Lynn, et. al.,
I have been following your comments with a great deal of interest, and I would like to offer my position in respect to some of the ideas expressed.
First, though I am primarily an IBM mainframe assembler programmer, I have been programming in PL/I for over 30 years. PL/I is great!
However, the question we have to ask at this point is in defining an API, do we yet need to map data types to specific machine representations?
I would argue that such mappings are not required at this point. I would also argue that some type of object-oriented methodology may be in order.
That is not to say that I advocate C++, C#, or Java. I find the syntax of C++ to be abhorrent, and all three languages suffer from extreme code bloat.
Of course, those of us who grew up programming in machine language consider all high-level languages to be machine hogs (and yes, for the purposes of discussing pork, C is a high-level language).
I would say that for each API entry point, we should identify the inputs (what is needed to perform the requested operation), the outputs (what we need to receive back in order to continue processing), and what exceptional conditions may occur while performing the requested operation.
Let us consider general file processing.
First, if control blocks are maintained in user storage, then you are faced with the possibility of corruption of the control block by the user application.
So, I would argue that control blocks should reside in protected storage, and should be accessible to the application program only by a “handle”. I am not at this point going to specify the representation of a “handle”. It is not really relevant to the API.
Continuing on, I would suggest that our API begin with a “DosAcquireHandle” function. This API would allocate to the calling task a handle which can then be associated with a control block residing in protected storage.
Now, the system should globally place a limit on the total number of tasks, the total number of handles, and the maximum number of handles per task. The limits should not be hard-coded, but should rather be specified in a system configuration space.
What might the “DosAcquireHandle” function actually do? Successful processing would return to the caller a handle to be subsequently associated with a control block residing in protected storage. Exception processing would return no data to the caller. The exception which could occur would report that the maximum number of handles per task has been exceeded or that the maximum number of handles in the system has been exceeded.
Let us now assume that we have acquired a “handle” for a control block.
Now, let us next consider a “DosOpen” function. This API would allocate a control block in a protected storage, associate that control block with an “unused” handle provided by the caller, and attempt to associate that control block with some external media, specified separately by the caller. So, the parameters passed to the “DosOpen” API would include a “handle”, an “external name”, and a set of processing indicators. We will have to consider what processing indicators are appropriate.
Every API function can be specified in this manner. It is not necessary to map data types to specific machine representation at this point.
We need to consider what level of compatibility should be maintained. Do we wish to maintain binary compatibility? If so, that imposes a number of constraints. Do we wish to maintain source compatibility? If so, that imposes a slightly different set of constraints. Do we merely wish to maintain perceptual compatibility (in simple terms, command syntax and user-interface compatibility)? If so, that imposes the least constraints on our creativity.
John P Baker
Software Engineer
Expand Messages
John P Baker
Jul 26, 2004
Lynn, et. al.,
I have been following your comments with a great deal of interest, and I would like to offer my position in respect to some of the ideas expressed.
First, though I am primarily an IBM mainframe assembler programmer, I have been programming in PL/I for over 30 years. PL/I is great!
However, the question we have to ask at this point is in defining an API, do we yet need to map data types to specific machine representations?
I would argue that such mappings are not required at this point. I would also argue that some type of object-oriented methodology may be in order.
That is not to say that I advocate C++, C#, or Java. I find the syntax of C++ to be abhorrent, and all three languages suffer from extreme code bloat.
Of course, those of us who grew up programming in machine language consider all high-level languages to be machine hogs (and yes, for the purposes of discussing pork, C is a high-level language).
I would say that for each API entry point, we should identify the inputs (what is needed to perform the requested operation), the outputs (what we need to receive back in order to continue processing), and what exceptional conditions may occur while performing the requested operation.
Let us consider general file processing.
First, if control blocks are maintained in user storage, then you are faced with the possibility of corruption of the control block by the user application.
So, I would argue that control blocks should reside in protected storage, and should be accessible to the application program only by a “handle”. I am not at this point going to specify the representation of a “handle”. It is not really relevant to the API.
Continuing on, I would suggest that our API begin with a “DosAcquireHandle” function. This API would allocate to the calling task a handle which can then be associated with a control block residing in protected storage.
Now, the system should globally place a limit on the total number of tasks, the total number of handles, and the maximum number of handles per task. The limits should not be hard-coded, but should rather be specified in a system configuration space.
What might the “DosAcquireHandle” function actually do? Successful processing would return to the caller a handle to be subsequently associated with a control block residing in protected storage. Exception processing would return no data to the caller. The exception which could occur would report that the maximum number of handles per task has been exceeded or that the maximum number of handles in the system has been exceeded.
Let us now assume that we have acquired a “handle” for a control block.
Now, let us next consider a “DosOpen” function. This API would allocate a control block in a protected storage, associate that control block with an “unused” handle provided by the caller, and attempt to associate that control block with some external media, specified separately by the caller. So, the parameters passed to the “DosOpen” API would include a “handle”, an “external name”, and a set of processing indicators. We will have to consider what processing indicators are appropriate.
Every API function can be specified in this manner. It is not necessary to map data types to specific machine representation at this point.
We need to consider what level of compatibility should be maintained. Do we wish to maintain binary compatibility? If so, that imposes a number of constraints. Do we wish to maintain source compatibility? If so, that imposes a slightly different set of constraints. Do we merely wish to maintain perceptual compatibility (in simple terms, command syntax and user-interface compatibility)? If so, that imposes the least constraints on our creativity.
John P Baker
Software Engineer
RE: [osFree] Programming: Part 2
#1047 RE: [osFree] Programming: Part 2
Expand Messages
John P Baker
Jul 26, 2004
MULTICS was run for many years by the military, and was written wholly in PL/I.
John P Baker
Software Engineer
Expand Messages
John P Baker
Jul 26, 2004
MULTICS was run for many years by the military, and was written wholly in PL/I.
John P Baker
Software Engineer
From: Michal Necasek [mailto:michaln@...]
Sent: Monday, July 26, 2004 22:25
To: osFree@yahoogroups.com
Subject: Re: [osFree] Programming: Part 2
On Mon, 26 Jul 2004 18:47:43 -0700 (PDT), Lynn H. Maxson wrote:
I'm sure Lynn will effectively ignore all I have to say, just like
he effectively ignores everything others say, but I thought I'd
point out some things anyway and then unsubscribe, because this is
really a waste of time.
>I would say we are talking about programming. We have real
>world situations, the problem set. We encode them in
>software, our solution set. Our objective is to have our
>solution set map into the problem set. If the solution set, in
>this instance C, does not contain the "native" data types of
>the problem set, then it forces us into an aberration of
>essentially modifying reality in order to fit in our fantasy.
>
So instead you're creating a complete fantasy? When writing an
operating system, the data types defined eg. by C match the problem
set perfectly. It may have escaped you, but CPU registers, addresses,
offsets etc. correspond to C types amazingly well - because C types
were modeled after them!
>I have been programming in PL/I now for nearly 40 years. In
>all that time I have never had to resort to assembly language
>or a library to perform any operation capable of any
>instruction on any machine. You simply do not understand just
>how complete as a programming language PL/I is.
>
How many operating systems have you written in PL/I? How many
operating systems have other people written in PL/I? How do I know
that it's even doable?
Also please point out one operating system for the x86 platform
(or some other common hardware) that was written entirely without
assembly language. I would be greatly surprised if such a thing
existed.
>There's no reason to tolerate "code bloat" period. There's no
>way in hell you can program in C or its derivatives without
>acquiring it. The answer lies in logic programming.
>
Proof? Please point us to successful operating systems written in
PL/I.
Or why don't you just show something relatively trivial? Say, an
OS boot loader written in PL/I. I think something like that would
greatly help convince people that PL/I is viable. If however you
can't produce such a thing, it might help convince people that PL/I
is just so much hot air.
Thank you for your attention, now I'm outta here.