There are two ways (to my knowledge) how to initialize objects of limited types. Either the limited type has some discriminants
type DLT (Width, Length : Positive) is tagged limited private;
Obj : DLT (3, 5);
or I can provide a constructor function that takes corresponding parameters
type LT (<>) is tagged limited private;
function Make (Width, Length : Positive) return LT;
Obj : LT := Make (3, 5);
Do you recommend one way over the other? Why? Is it possible to combine both methods (discriminants plus constructor)?
There are two ways (to my knowledge) how to initialize objects of limited types. Either the limited type has some discriminants
type DLT (Width, Length : Positive) is tagged limited private;
Obj : DLT (3, 5);
or I can provide a constructor function that takes corresponding parameters
type LT (<>) is tagged limited private;
function Make (Width, Length : Positive) return LT;
Obj : LT := Make (3, 5);
Do you recommend one way over the other? Why? Is it possible to combine both methods (discriminants plus constructor)?
On 2022-05-04 11:02, R R wrote:
There are two ways (to my knowledge) how to initialize objects of limited
types. Either the limited type has some discriminants
type DLT (Width, Length : Positive) is tagged limited private;
Obj : DLT (3, 5);
or I can provide a constructor function that takes corresponding
parameters
type LT (<>) is tagged limited private;
function Make (Width, Length : Positive) return LT;
Obj : LT := Make (3, 5);
Do you recommend one way over the other? Why? Is it possible to combine
both methods (discriminants plus constructor)?
Two more ways are allocators and limited aggregates.
There is no good way to safely initialize a limited object because Ada
lacks proper constructors and because the initialization model is
inherently unsafe with regard or exceptions, task components, self-referential discriminants (AKA Rosen's trick).
But there are numerous hacks and workarounds depending on the objective.
Those cases that you worry are "unsafe" seem to me to only occur because of "hacks and workarounds". There's no good reason to do any of those things intentionally unless you are using a "hack" to do something dubious in the first place.
There are two ways (to my knowledge) how to initialize objects of limited types. Either the limited type has some discriminants
type DLT (Width, Length : Positive) is tagged limited private;
Obj : DLT (3, 5);
or I can provide a constructor function that takes corresponding parameters
type LT (<>) is tagged limited private;
function Make (Width, Length : Positive) return LT;
Obj : LT := Make (3, 5);
Do you recommend one way over the other? Why? Is it possible to combine both methods (discriminants plus constructor)?
RREE
On 2022-05-05 01:49, Randy Brukardt wrote:
Those cases that you worry are "unsafe" seem to me to only occur because
of
"hacks and workarounds". There's no good reason to do any of those things
intentionally unless you are using a "hack" to do something dubious in
the
first place.
Is a task component a hack? Well so long there is no tagged task types aggregation is the only way.
Controlled types are hacks, yes, but there is no alternative. You admit
that all tagged types (and I would say all types) should be controlled = support user-defined initialization hooks.
Dispatching from Initialize is a double hack, but again, if you need it,
the alternative, client-side manual initialization is way worse.
Exception propagation upon initialization? There is no enforceable
exception contracts to fight it.
So I say, if hacks occur, then because of the language problems, not only because some lazy programmer does something stupid.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:t4vsfi$jtb$1@gioia.aioe.org...
On 2022-05-05 01:49, Randy Brukardt wrote:
Those cases that you worry are "unsafe" seem to me to only occur because >>> of
"hacks and workarounds". There's no good reason to do any of those things >>> intentionally unless you are using a "hack" to do something dubious in
the
first place.
Is a task component a hack? Well so long there is no tagged task types
aggregation is the only way.
Task objects look like a cool language feature, but use beyond the simplest patterns will get one into a load of trouble (deadlocks, erroneous use of objects, etc.)
Almost all of the correct uses of seen are implementations of
the "pool of workers" scheme.
So, very much like writing your own basic data structures from scratch, this is a feature which is mostly OBE. (Embedded uses are mostly Ravenscar, and that doesn't allow dynamically started tasks in the first place.)
Controlled types are hacks, yes, but there is no alternative. You admit
that all tagged types (and I would say all types) should be controlled =
support user-defined initialization hooks.
I don't think I said anything about controlled types being hacks.
Exception propagation upon initialization? There is no enforceable
exception contracts to fight it.
But you don't (or shouldn't) care what happens to an object when an
exception is raised during initialization. The program is wrong, it needs to be fixed, end of story.
On top of that, the language guarentees that all
(controlled) objects that are initialized will get finalized (even when exceptions happen),
You can handle the exception and
retry.
What you can't do is use the object that failed for anything, so the
fact that you don't know if it was fully initialized is irrelevant.
You have a strange idea of language problems. (But of course I've known that for a long while; I've written these responses for the benefit of lurkers
and the OP, not so much to convince you of anything. :-)
On 2022-05-06 03:53, Randy Brukardt wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:t4vsfi$jtb$1@gioia.aioe.org...
On 2022-05-05 01:49, Randy Brukardt wrote:
Those cases that you worry are "unsafe" seem to me to only occur
because
of
"hacks and workarounds". There's no good reason to do any of those
things
intentionally unless you are using a "hack" to do something dubious in >>>> the
first place.
Is a task component a hack? Well so long there is no tagged task types
aggregation is the only way.
Task objects look like a cool language feature, but use beyond the
simplest
patterns will get one into a load of trouble (deadlocks, erroneous use of
objects, etc.)
Because the task type is not composable.
Almost all of the correct uses of seen are implementations of
the "pool of workers" scheme.
That is a pretty much trivial case. I am talking about things like implementation of extensible types of active objects.
So, very much like writing your own basic data structures from scratch,
this
is a feature which is mostly OBE. (Embedded uses are mostly Ravenscar,
and
that doesn't allow dynamically started tasks in the first place.)
Which in many practical cases renders it unusable.
That is an awful and unsustainable design, because you consider initialization magically separated from construction.
On top of that, the language guarentees that all
(controlled) objects that are initialized will get finalized (even when
exceptions happen),
Here you contradict yourself.
You can handle the exception and
retry.
This is not the intended use of exceptions. Exception per definition
indicate a program state that cannot be handled locally.
The importance of failed construction can be easily seen of the design of
Ada 83 File_Type. Since Ada 83 library uses initialization never fails approach, File_Type must have unusable states, like when the file is not
open and dozens of operations to deal with these states in the client code
as well as all sorts of additional errors coming with it as a "bonus."
An alternative design would exclude unopened files but require object construction faults, e.g. when the file does not exist. Having such file a component will require construction faults of the container type,
parameter passing etc.
Merits of each design is always up to the programmer. The language must support him whatever design he pursues, which is unfortunately not the
case.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:t52nd4$vj2$1@gioia.aioe.org...
Because the task type is not composable.
Irrelevant. A task can only be safe if it never interacts with no other objects outside of itself.
Exactly. The idea that everyone wants but simply will not work. At least in any sort of imperative language (it might work in a functional language, but it's unclear how one gets to globals like networking in such languages - if you can't do that safely, you're going nowhere anyway.).
...
That is an awful and unsustainable design, because you consider
initialization magically separated from construction.
"Construction" is something that the compiler does (allocating the needed memory for the object). It doesn't have anything to do with the (logical) contents.
...
On top of that, the language guarentees that all
(controlled) objects that are initialized will get finalized (even when
exceptions happen),
Here you contradict yourself.
No, I'm just pointing out that retrying is safe by the language. You seem to think it is not. Servers need such guarentees to avoid denial-of-service issues.
You can handle the exception and
retry.
This is not the intended use of exceptions. Exception per definition
indicate a program state that cannot be handled locally.
"Retry" at a high level. For instance, in the web server, it means trying to process another connection.
One doesn't necessary retry the same operation
(that doesn't make a ton of sense anyway, since it is likely to fail the
same way). An exception doesn't necessarily mean the program is failed, only the one operation.
...
The importance of failed construction can be easily seen of the design of
Ada 83 File_Type. Since Ada 83 library uses initialization never fails
approach, File_Type must have unusable states, like when the file is not
open and dozens of operations to deal with these states in the client code >> as well as all sorts of additional errors coming with it as a "bonus."
Yup. That's pretty much the only way to design such things.
An alternative design would exclude unopened files but require object
construction faults, e.g. when the file does not exist. Having such file a >> component will require construction faults of the container type,
parameter passing etc.
That makes an interesting thought experiment, but it doesn't work in practice.
Besides forcing virtually all objects to be allocated (since most
objects have to live longer than a single subprogram), it doesn't handle cases where outside forces close the object (common in file systems, GUIs, etc.). You still end up with those error states, you've just complicated creating an object for no benefit at all.
We initially trying designing Claw Window objects that way, but one had to handle the case where the user clicks the 'X' (close button) while a routine is working on the window. Windows closes the window almost immediately, and the GUI library has to deal with the consequences.
There's little point in obsessing about designs that only work in academic exercises. Ada tries too hard already to accomadate designs like yours.
On 2022-05-07 05:26, Randy Brukardt wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:t52nd4$vj2$1@gioia.aioe.org...
Because the task type is not composable.
Irrelevant. A task can only be safe if it never interacts with no other
objects outside of itself.
Not when other objects are tasks, protected objects, active objects,
atomic objects.
No, I'm just pointing out that retrying is safe by the language. You seem
to
think it is not. Servers need such guarentees to avoid denial-of-service
issues.
No, the contradiction is that you claim that safe construction is fundamentally impossible and yet concede that it is safe nonetheless.
Because the language lacks obvious abstractions like user-defined discriminants. Otherwise I see no logical reason why:
F : File_Type ("My_Ada_file.adb");
should not work.
Besides forcing virtually all objects to be allocated (since most
objects have to live longer than a single subprogram), it doesn't handle
cases where outside forces close the object (common in file systems,
GUIs,
etc.). You still end up with those error states, you've just complicated
creating an object for no benefit at all.
I simplified creating the object by removing states when the object is unusable for no other reason than language design. Cases when the file can
be unreadable because of I/O errors have nothing to do with the case when
the programmer did not open it.
We initially trying designing Claw Window objects that way, but one had
to
handle the case where the user clicks the 'X' (close button) while a
routine
is working on the window. Windows closes the window almost immediately,
and
the GUI library has to deal with the consequences.
Windows sends WM_CLOSE first.
There's little point in obsessing about designs that only work in
academic
exercises. Ada tries too hard already to accomadate designs like yours.
On the contrary, it is a very practical software design problem to reduce error sources as much as possible.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:t5619r$10bc$1@gioia.aioe.org...
On 2022-05-07 05:26, Randy Brukardt wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:t52nd4$vj2$1@gioia.aioe.org...
Because the task type is not composable.
Irrelevant. A task can only be safe if it never interacts with no other
objects outside of itself.
Not when other objects are tasks, protected objects, active objects,
atomic objects.
That's a common fallacy. Such objects are safe only if there is exactly one in your system. If there is more than one, various forms of failure are possible even if everything is supposely safe by itself. To make them fully safe, you have to have strong access ordering (for instance, the onion skin model), which no programming language and probably no static tool can enforce. (Proof of safety requires verification that no possible program
flow can cause a race condition.)
Because the language lacks obvious abstractions like user-defined
discriminants. Otherwise I see no logical reason why:
F : File_Type ("My_Ada_file.adb");
should not work.
It does "work", but such designs put major restrictions on your clients.
A common way my programs are structured is something like:
declare
Output_File : Some_File_Type;
begin
Create_or_Open_Output (Output_File);
Write_Output (Output_File, Data);
...
Close_Output (Output_File);
end;
You can't use such a structure with your design, because you can't pass in the unopened file object to open it appropriately (which can take multiple attempts depending upon options,
Discriminants are only useful for memory management (to size arrays, to make components conditional). Other uses are purely mistakes.
I simplified creating the object by removing states when the object is
unusable for no other reason than language design. Cases when the file can >> be unreadable because of I/O errors have nothing to do with the case when
the programmer did not open it.
There is no difference between the state of an object before it is opened
and the one it is in after it is closed. (And these aren't just error cases, as noted by the Windows example.)
We initially trying designing Claw Window objects that way, but one had
to
handle the case where the user clicks the 'X' (close button) while a
routine
is working on the window. Windows closes the window almost immediately,
and
the GUI library has to deal with the consequences.
Windows sends WM_CLOSE first.
Sure, but the only thing you can do at that point is make the object
invalid.
There's little point in obsessing about designs that only work in
academic
exercises. Ada tries too hard already to accomadate designs like yours.
On the contrary, it is a very practical software design problem to reduce
error sources as much as possible.
An imaginary reduction of errors, since you have the same states that occur in other usage scenarios.
On 2022-05-08 04:32, Randy Brukardt wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:t5619r$10bc$1@gioia.aioe.org...
On 2022-05-07 05:26, Randy Brukardt wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:t52nd4$vj2$1@gioia.aioe.org...
Because the task type is not composable.
Irrelevant. A task can only be safe if it never interacts with no other >>>> objects outside of itself.
Not when other objects are tasks, protected objects, active objects,
atomic objects.
That's a common fallacy. Such objects are safe only if there is exactly one >> in your system. If there is more than one, various forms of failure are
possible even if everything is supposely safe by itself. To make them fully >> safe, you have to have strong access ordering (for instance, the onion skin >> model), which no programming language and probably no static tool can
enforce. (Proof of safety requires verification that no possible program
flow can cause a race condition.)
You are talking about a very low level and tightly coupled design.
Higher level object is supposed to prevent that so that there would be
no need in taking several mutexes or in chains of external entry calls,
at least not explicitly. So an active object is entirely safe.
Anyway, we do not discuss safety of using objects, we discuss safety of >composing new objects out of existing ones. It would be silly to argue
that since Positive is unsafe due to existence of the unary minus
operation, it shall not be used a component of a record type.
Because the language lacks obvious abstractions like user-defined
discriminants. Otherwise I see no logical reason why:
F : File_Type ("My_Ada_file.adb");
should not work.
It does "work", but such designs put major restrictions on your clients.
No, you cannot have this syntax. At best you must use Pickwickian >pseudo-functions:
F : File_Type := Open ("My_Ada_file.adb");
A common way my programs are structured is something like:
declare
Output_File : Some_File_Type;
begin
Create_or_Open_Output (Output_File);
Write_Output (Output_File, Data);
...
Close_Output (Output_File);
end;
You can't use such a structure with your design, because you can't pass in >> the unopened file object to open it appropriately (which can take multiple >> attempts depending upon options,
It is surprisingly easy when the type system is used as it should be.
Just derive a new type from Some_File_Type and provide a new constructor
for it.
Note, that your code is already unsafe because nobody knows if >Create_or_Open_Output always opens the file and because there is no
guarantee that the file is closed, while the design
declare
Output_File : Some_File_Type;
begin
Write_Output (Output_File, Data);
end;
is 100% safe.
Discriminants are only useful for memory management (to size arrays, to make >> components conditional). Other uses are purely mistakes.
Nope. Discriminant is a parameter, the semantic of must be up to the >programmer. The problem with Ada is that it enforces a certain extremely >limited implementation of discriminants, so that even that limited use
you claimed is actually incorrect:
type X (Size : Natural) is record
S : String (1..Size + 1); -- Tell me about memory management!
end record;
I simplified creating the object by removing states when the object is
unusable for no other reason than language design. Cases when the file can >>> be unreadable because of I/O errors have nothing to do with the case when >>> the programmer did not open it.
There is no difference between the state of an object before it is opened
and the one it is in after it is closed. (And these aren't just error cases, >> as noted by the Windows example.)
Surely, in the design where you deal with open files only, you could not >explicitly close one. The object does not have these states. Compare it
with Ada 83's Standard_Input. You are not supposed to open or close it.
We initially trying designing Claw Window objects that way, but one had >>>> to
handle the case where the user clicks the 'X' (close button) while a
routine
is working on the window. Windows closes the window almost immediately, >>>> and
the GUI library has to deal with the consequences.
Windows sends WM_CLOSE first.
Sure, but the only thing you can do at that point is make the object
invalid.
That is crude.
It depends on the design but normally Windowed GUI objects are allocated
on the stack and are blocking. So WM_CLOSE should simply make an exit
from some hidden loop in something like:
declare
Dialog : Dialog_Box;
begin
Dialog.Run; -- Note, ugliness of lacking constructors again!
Non-modal stuff is a part of some container and thus WM_CLOSE must go to
the parent which then explicitly kills the child object. No problem.
You do not need half-backed objects even in GUI. Such states can be
hidden in most cases.
There's little point in obsessing about designs that only work inOn the contrary, it is a very practical software design problem to reduce >>> error sources as much as possible.
academic
exercises. Ada tries too hard already to accomadate designs like yours. >>>
An imaginary reduction of errors, since you have the same states that occur >> in other usage scenarios.
No they do not. The goal is to eliminate non-functional states. Things
like closed file, uninitialized variable, null pointer etc are artifacts
of the design. There is nothing in the physical world that requires them.
On Sun, 8 May 2022 10:37:48 +0200, "Dmitry A. Kazakov"
Note, that your code is already unsafe because nobody knows if
Create_or_Open_Output always opens the file and because there is no
guarantee that the file is closed, while the design
declare
Output_File : Some_File_Type;
begin
Write_Output (Output_File, Data);
end;
is 100% safe.
Not in the case that your data space is exhausted, in that case
Write_Output will fail, because you have no checks of free space
before writing.
On 2022-05-08 19:19, Doctor Who wrote:
On Sun, 8 May 2022 10:37:48 +0200, "Dmitry A. Kazakov"
Note, that your code is already unsafe because nobody knows if
Create_or_Open_Output always opens the file and because there is no
guarantee that the file is closed, while the design
declare
Output_File : Some_File_Type;
begin
Write_Output (Output_File, Data);
end;
is 100% safe.
Not in the case that your data space is exhausted, in that case
Write_Output will fail, because you have no checks of free space
before writing.
Upon a write error an exception will propagate and Output_File will go
out of the scope. The file will be closed and the exception will
continue its way. This is a safe behavior. Randy's code is unsafe and >requires catching and re-raising I/O exceptions in the client code.
This is an illustration why exceptions should be allowed to propagate
out of Finalize. When Finalize is used to close a handle this may fail
and there is no way to retry inside Finalize. The choice in Ada is
between aborting the program or sweeping the problem under the rug
ignoring the problem. A more sane approach would be to propagate
exception out of Finalize indicating the problem.
On 2022-05-08 19:19, Doctor Who wrote:
On Sun, 8 May 2022 10:37:48 +0200, "Dmitry A. Kazakov"
Note, that your code is already unsafe because nobody knows if
Create_or_Open_Output always opens the file and because there is no
guarantee that the file is closed, while the design
declare
Output_File : Some_File_Type;
begin
Write_Output (Output_File, Data);
end;
is 100% safe.
Not in the case that your data space is exhausted, in that case
Write_Output will fail, because you have no checks of free space
before writing.
Upon a write error an exception will propagate and Output_File will go
out of the scope. The file will be closed and the exception will
continue its way. This is a safe behavior. Randy's code is unsafe and requires catching and re-raising I/O exceptions in the client code.
This is an illustration why exceptions should be allowed to propagate
out of Finalize. When Finalize is used to close a handle this may fail
and there is no way to retry inside Finalize. The choice in Ada is
between aborting the program or sweeping the problem under the rug
ignoring the problem. A more sane approach would be to propagate
exception out of Finalize indicating the problem.
On 2022-05-08 21:00, Dmitry A. Kazakov wrote:
On 2022-05-08 19:19, Doctor Who wrote:
On Sun, 8 May 2022 10:37:48 +0200, "Dmitry A. Kazakov"
Note, that your code is already unsafe because nobody knows if
Create_or_Open_Output always opens the file and because there is no
guarantee that the file is closed, while the design
declare
Output_File : Some_File_Type;
begin
Write_Output (Output_File, Data);
end;
is 100% safe.
Not in the case that your data space is exhausted, in that case
Write_Output will fail, because you have no checks of free space
before writing.
Upon a write error an exception will propagate and Output_File will go
out of the scope. The file will be closed and the exception will
continue its way. This is a safe behavior. Randy's code is unsafe and
requires catching and re-raising I/O exceptions in the client code.
This is an illustration why exceptions should be allowed to propagate
out of Finalize. When Finalize is used to close a handle this may fail
and there is no way to retry inside Finalize. The choice in Ada is
between aborting the program or sweeping the problem under the rug
ignoring the problem. A more sane approach would be to propagate
exception out of Finalize indicating the problem.
That might work if the scope that is being left has only one local
object that needs finalization. But what if there are several, and the
first Finalize propagates an expection? Should the remaining
finalizations be skipped?
That would not be right. But if the remaining
finalizations are also attempted, their execution may run into problems because the first Finalize was not completed.
Moreover, the remaining
finalizations might also propagate one or more exceptions, so now there
could be a whole set of different exception instances being propagated
from all these finalizations. Which exception(s) should be retained and handled?
The term "can of worms" comes to mind...
On 2022-05-08 19:19, Doctor Who wrote:
On Sun, 8 May 2022 10:37:48 +0200, "Dmitry A. Kazakov"
Note, that your code is already unsafe because nobody knows if
Create_or_Open_Output always opens the file and because there is no
guarantee that the file is closed, while the design
declare
Output_File : Some_File_Type;
begin
Write_Output (Output_File, Data);
end;
is 100% safe.
Not in the case that your data space is exhausted, in that case
Write_Output will fail, because you have no checks of free space
before writing.
Upon a write error an exception will propagate and Output_File will go
out of the scope. The file will be closed and the exception will
continue its way. This is a safe behavior. Randy's code is unsafe and >requires catching and re-raising I/O exceptions in the client code.
This is an illustration why exceptions should be allowed to propagate
out of Finalize. When Finalize is used to close a handle this may fail
and there is no way to retry inside Finalize. The choice in Ada is
between aborting the program or sweeping the problem under the rug
ignoring the problem. A more sane approach would be to propagate
exception out of Finalize indicating the problem.
if you do a check before write there is no need to rise and propagate
an exception.
On 2022-05-09 12:19, Doctor Who wrote:
if you do a check before write there is no need to rise and propagate
an exception.
A bad idea.
First it is an unnecessary overhead, because ultimately the check will
be repeated.
Secondly it is technically impossible to do for a huge number of reasons:
1. Too complex to do. The modern hardware and software does a lot of >bookkeeping, indexing, replicating, compression, encryption, the stuff
almost impossible to estimate or predict in advance.
2. Unreliable due to racing conditions, volatile network states etc.
3. Very expensive, e.g. in the case of a networking file system or a
database and the asynchronous nature of modern I/O subsystems. When you
write a file you normally do not wait for the operation completion.
Actual writing continues asynchronously to your application going
through dozens of stacks, caches, buffers, buses. If that fails you will >learn that later in a consequent operation unless you explicitly call
Flush or equivalent. Checking the state is a synchronous operation that
would have hundred- to thousandfold performance penalty.
4. Just impossible like when dealing with a stream, a pipe line etc.
As general design rules regarding exceptions consider:
1. Never use return code.
2. Never check anything non-trivial in advance.
On Mon, 9 May 2022 13:15:54 +0200, "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote:
On 2022-05-09 12:19, Doctor Who wrote:
if you do a check before write there is no need to rise and propagate
an exception.
A bad idea.
First it is an unnecessary overhead, because ultimately the check will
be repeated.
Secondly it is technically impossible to do for a huge number of reasons:
1. Too complex to do. The modern hardware and software does a lot of
bookkeeping, indexing, replicating, compression, encryption, the stuff
almost impossible to estimate or predict in advance.
2. Unreliable due to racing conditions, volatile network states etc.
3. Very expensive, e.g. in the case of a networking file system or a
database and the asynchronous nature of modern I/O subsystems. When you
write a file you normally do not wait for the operation completion.
Actual writing continues asynchronously to your application going
through dozens of stacks, caches, buffers, buses. If that fails you will
learn that later in a consequent operation unless you explicitly call
Flush or equivalent. Checking the state is a synchronous operation that
would have hundred- to thousandfold performance penalty.
4. Just impossible like when dealing with a stream, a pipe line etc.
As general design rules regarding exceptions consider:
1. Never use return code.
2. Never check anything non-trivial in advance.
we are talking about files, in a local disk right?
No they do not. The goal is to eliminate non-functional states. Things
like closed file, uninitialized variable, null pointer etc are artifacts
of the design. There is nothing in the physical world that requires them.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:t57vgs$4su$1@gioia.aioe.org...
...
No they do not. The goal is to eliminate non-functional states. Things
like closed file, uninitialized variable, null pointer etc are artifacts
of the design. There is nothing in the physical world that requires them.
Most such things are artifacts of the target system. Closed files and closed windows happen because of events that occur outside of your program. You can't design them away.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 379 |
Nodes: | 16 (2 / 14) |
Uptime: | 44:36:31 |
Calls: | 8,141 |
Calls today: | 4 |
Files: | 13,085 |
Messages: | 5,858,054 |