December 07, 2022
On 11/27/22 14:35, kdevel wrote:

>> I think it is definition and comes from convention:
>>
>> - Errors represent situations where the program should not continue

I still agree with myself there: Errors should be used (trivially with assert) to specify the cases where the program cannot continue.

> If the programmer who implements a function/method is not the author of
> `main` he most certainly is not in charge of deciding on the fate of the
> running program.

That's agreeable. Error represents aborting mission but there are safety-critical applications where continuing with sane steps is better than aborting mission. One example given is autonomous-driving software aborting driving because of a thrown Error. It should not happen. The software should decide to drive slowly to the side.

See... Even that's wishful thinking. When an assertion fails, we don't know whether the execution of that slowly driving to the side will do that. The absurd state we caught ourselves in may be due to a hardware issue where steering to the right may skid the vehicle out of control.

For that reason, perhaps the software still aborts but another unit takes over.

Sure... but these are outside of a program deciding to abort mission:

  int[] arr;
  populateArrar(arr);
  assert(arr.length > 7);

What should the program do? Attempt to add arbitrary items to the array? What if that fails as well? If it could work, then it should not be assert in that last line, rather the following:

  if (arr.length <= 7) {
      log(/* ... */);
  }
  // continue

> For me, it
> makes no difference if the file name is user input, taken from the
> command line, or hardcoded into the program. Well, actually I would not
> code such a check at all. I would let the open function throw the
> exception (also to avoid TOCTTOU).

I may do that as well. However, the following does not strike me as wrong at all:

    createConfigFile("foo");
    assert(exists("foo"));

Even though another actor in the system may delete the file after the check, it is also conceivable that a newly-created file should exist.

> The decision if the program can continue without configuration file is
> probably taken in `main` or in the consumer of that configuration data:
> If there is a default configuration dataset the program might continue.

I was imagining the file that should exist as the default configuration.

I wonder whether my example is wrong or whether it is always conceivable that all assert checks can be removed.

>> Having said that, that assert means this to me: "For this program to
>> be able continue, the value of 'a' must be 0."
>
> In order not to convey that meaning in real programms I only use asserts
> in unittests.

That's the approach some safety-critical software takes: asserts exist only in test phase. There should be no assert left in the final product. All assertion failures should be caught during development phase.

>> The way I see it, valid state is defined by the collection of
>> assertion checks,
>
> But sometimes you don't believe what the code says. If it does not match
> up with your presuppositions you testify validity though the assert
> checks fail. But then isn't that invalid state a non-local or even a
> subjective thing?

I don't understand. But the way I see it, yes, invalid state is completely subjective. The programmer defines it with assert checks and the program ensures it stays in that state.

Ali

December 08, 2022
On 11/26/22 13:32, Ali Çehreli wrote:
> - If configFile was a string input by the user, then the following is appropriate:
> 
>    enforce(exists(configFile), /* ... */)
> 
> - If configFile was a string representing a file e.g. that has just been generated by the same program, then the following is appropriate:
> 
>    assert(exists(configFile), /* ... */)
> 
> Only the programmer knows.

I don't think it is appropriate to assert that the filesystem is not being changed concurrently by some other process unless the OS provides explicit functionality to enforce it.
December 08, 2022
On Thu, Dec 08, 2022 at 04:03:35PM +0100, Timon Gehr via Digitalmars-d wrote:
> On 11/26/22 13:32, Ali Çehreli wrote:
> > - If configFile was a string input by the user, then the following is appropriate:
> > 
> >    enforce(exists(configFile), /* ... */)
> > 
> > - If configFile was a string representing a file e.g. that has just been generated by the same program, then the following is appropriate:
> > 
> >    assert(exists(configFile), /* ... */)
> > 
> > Only the programmer knows.
> 
> I don't think it is appropriate to assert that the filesystem is not being changed concurrently by some other process unless the OS provides explicit functionality to enforce it.

Yeah, for checking the existence of a file I'd use enforce, not assert. Assert is for catching logic errors in the program; a missing file in the filesystem isn't a logic error, it's either an installation problem (your program wasn't installed properly) or an incidental issue (user removed the drive for whatever reason, or hardware is failing).


T

-- 
Study gravitation, it's a field with a lot of potential.
December 08, 2022

On Friday, 25 November 2022 at 16:48:39 UTC, kdevel wrote:

> >

I don't think the example above has a correct condition; so let's focus on correct ones.

The condition is false. That is what I want to discuss. My question is: What does that mean? Does it mean more than writing a comment

// I solemnly swear a is zero.

"A is always zero when this line is passed by the program. I have thought it through, and when I have tested it, it always has been so. Therefore, if a ever is something else when the program gets here, by definition I have no idea what is happening, and what could have caused it. It follows I can say nothing what would happen if you let this program to continue, so it must be aborted immediately."

December 08, 2022
On Friday, 25 November 2022 at 12:29:26 UTC, Ali Çehreli wrote:
> On 11/25/22 00:12, Dukc wrote:
>
> >> Discussion: As I commented on the bug report as well, D is
> already in
> >> the best-effort business when it comes to Errors:
> >
> > What does "best-effort" mean?
>
> (I am not sure whether I should have used the word "hopeful" here. I will continue with best-effort.)
>
> [snip]

Thanks for the explanation.

> Add to the above another observation which I had after Patrick Schluter's message: Even the execution of the assert check is best-effort because if the check will fail, the program has been in invalid state for an unknow amount of time anyway.

I disagree slightly. Yes, the higher level purpose the assertion is accomplishing is best-effort - that is, shutting down a program overtaken by a bug. But I think the implementation itself should not be so. If we assert that x is 5, catching x being 4 must happen every single time, not on best effort basis, unless the assertion is disabled by a compiler switch.
December 08, 2022

On Friday, 25 November 2022 at 09:33:21 UTC, Quirin Schroll wrote:

>

On Friday, 25 November 2022 at 08:12:41 UTC, Dukc wrote:

>

What does "best-effort" mean?

Generally, it means that a procedure is not a complete or ideal solution and won’t always give you what you need (even if it theoretically could), but still covers common and simple cases and maybe some of the uncommon or complicated ones. Usually, best-effort solutions are subject to incremental improvements.

[snip]

In fact it's this explanation that made it clear to me. Thanks.

December 08, 2022
On 12/8/22 08:22, Dukc wrote:

>> Add to the above another observation which I had after Patrick
>> Schluter's message: Even the execution of the assert check is
>> best-effort because if the check will fail, the program has been in
>> invalid state for an unknow amount of time anyway.
>
> I disagree slightly. Yes, the higher level purpose the assertion is
> accomplishing is best-effort - that is, shutting down a program
> overtaken by a bug. But I think the implementation itself should not be
> so. If we assert that x is 5, catching x being 4 must happen every
> single time, not on best effort basis, unless the assertion is disabled
> by a compiler switch.

The program may be affected by undefined behavior, which may cause x to have any value.

I used "hopeful" and then "best-effort" but a better phrase might have been "wishful": A program is trying to catch itself in good (or bad) state by executing some code, code usually related to the bad state that is about to be caught:

  assert(o.foo == o.bar * 2);

foo and bar may be member functions of an object in bad state. It is wishful thinking because I am using the same object to catch its bad state.

Let's say I am happy I caught the program in bad state and am happy to about to abort the program: That's only happy thinking because I should have aborted when the invariant was broken in the first place, not when I haphazardly realized at a later time. What damage has already been done? If I am happy I am aborting, am I sadder I am aborting only now, some time later than that unknown time when the invariant was broken?

I got into this theoretical discussion but I don't feel well equipped. Maybe we use assert to catch both logic errors as well as hardware issues. Perhaps we need to classify error categories better first. I don't know enough.

Ali

December 09, 2022
On Thursday, 8 December 2022 at 16:02:14 UTC, H. S. Teoh wrote:

[...]

> Yeah, for checking the existence of a file I'd use enforce, not assert.

You would write

   enforce (exists (filename));
   auto text = readText (filename);

instead of simply

   auto text = readText (filename);  // (1)

? I mean isn't the enforce redundant and also prone to TOCTTOU?

> Assert is for catching logic errors in the program; a missing file in the filesystem isn't a logic error,

Logic error means that a program does not implement the specification.
Example: The program should read the config file "config.cfg" which is
misspelled as in

   enum config_filename = "config.cgf";
   auto config = readText (config_filename);

When the program is started a FileException is thrown. Nobody is forced
to catch logic errors with asserts.

Now: A file system is essentially a key/value store. (1) can also be
written as

   auto value = readText (key);

With a nicer notation this becomes

   auto value = obj [key]; // (2)

where obj is an instance of some Filesystem class. IMNSHO it is hard
to explain, why in case of

   string[string] obj;
   string key;

or

   string [] obj;
   int key;

the expression (2) throws an Error (capital E) while in the case of

   Filesystem obj;
   string key;

it throws an Exception.
December 09, 2022
On Fri, Dec 09, 2022 at 02:36:04PM +0000, kdevel via Digitalmars-d wrote:
> On Thursday, 8 December 2022 at 16:02:14 UTC, H. S. Teoh wrote:
> 
> [...]
> 
> > Yeah, for checking the existence of a file I'd use enforce, not assert.
> 
> You would write
> 
>    enforce (exists (filename));
>    auto text = readText (filename);
> 
> instead of simply
> 
>    auto text = readText (filename);  // (1)
> 
> ? I mean isn't the enforce redundant and also prone to TOCTTOU?
[...]

No, I meant, *if* I wanted to check for the existence of a file, I would use enforce rather than assert.  If I was going to just read the file, I might as well just catch the exception when opening it failed. It would be pointless to use .enforce to check for its existence.


T

-- 
"Holy war is an oxymoron." -- Lazarus Long
December 09, 2022
On 12/9/22 15:36, kdevel wrote:
> 
> Now: A file system is essentially a key/value store. (1) can also be
> written as
> 
>     auto value = readText (key);
> 
> With a nicer notation this becomes
> 
>     auto value = obj [key]; // (2)
> ...

You mean with a deliberately more confusing notation.

> where obj is an instance of some Filesystem class. IMNSHO it is hard
> to explain, why in case of
> 
>     string[string] obj;
>     string key;
> 
> or
> 
>     string [] obj;
>     int key;
> 
> the expression (2) throws an Error (capital E) while in the case of
> 
>     Filesystem obj;
>     string key;
> 
> it throws an Exception.
It is not hard to explain. The version throwing an error is a more fundamental building block that can be used to implement the version throwing an exception. The file system does not expose the more low-level operation because the file system is shared between multiple processes and it also does not expose primitives to synchronize file system accesses. I.e., you can't be sure the programmer is to blame for attempting to e.g. open an nonexistent file.

It could be different, but that would require a OS design that supports some sort of filesystem ownership semantics.