Another philosophical question of error handling?

Hello,
First of all, I have been practicing very regularly for almost a year now, sometimes for whole days… Sometimes for whole nights;)
I understand better the memory management, the philosophy, I read a lot on the subject, I see the extent of the ZIG compiler, frankly, I am impressed by the team as well as their work,

My question :
what I haven’t quite figured out yet is how or why and to what extent, should we write try unrechables etc. and is that implied.
Throw like that there is a lot to say :wink: :wink:

a) In a closed software, I mean who uses functions of which we are sure, should we put the hypothetical error management in place.
Even if there is a possibility of data inconsistency, but managed upstream.
put tries everywhere ??? with in !void etc…

b)
Writing functions which are normally generated (the code) by the project, but which can be coded manually, providing for error handling is understandable to me, but to what extent,

  1. the compilation error, there, I don’t care,
  2. the error due to the inconsistency of data which implies the non-functioning of the program?? if I dare to say that you define a window in there that she, you cannot write is a nonsense; even if the window is displayed; ???
  3. why am I asking his questions, when you provide the user with functions and you type a string of try or catch unreachable; etc
  4. I’m not talking about real error handling, but theoretical error assumptions;

writing this, I have part of my answer, but I would like to have another point of view

There are error handling videos and links that explain how to handle, but no philosophy of why and how and how far to go and what not to go.

1 Like

There is not going to be a one-size-fits-all philosophy… Maybe a once-size-fits-most

There are going to be some special cases where the decision is made for you based on the domain you’re working in. If you’re making software for something like an elevator or a medical device, there is no room for errors. If you’re making software for something like an IoT mesh, there are only errors (dropped packets, network changes, transmission rate change, so so much that will go wrong) and you’re best off embracing that everything will fail and design with failure in mind.

Most software falls somewhere in the middle, there are some kinds of possible errors that you can take for granted, or not even know about, and it’s fine. You may never actually encounter them or never need to handle them beyond something like a retry strategy.

I have a good example of a case where unreachable made sense.

There was a member function that only returned a bool - let’s call it foo. Let’s assume that foo cannot be changed.

The function foo needed to update a piece of information about an array that it contained, and then reinsert that item into a different position (it had to remain sorted).

An easy way to do this was to use a pop method and then re-append the item that was popped. In pseudo code:

item = mystruct.pop(index) // remove the item from the list
new_item = update(item) // update the item’s properties
mystruct.orderedInsert(new_item) // insert the item at the proper location

The problem here is that orderedInsert() can fail because it may have to do an allocation (inserting into an empty list, for instance). However, we know that we have the capacity to reinsert the item because we just popped it off. So in this case, we know that the “OutOfMemory” error will never be used because we did not change the capacity of the list.

In this case, that code that creates the “OutOfMemory” will never be reached - it is effectively unreachable.

To summarize, in the case where we do not know if we have the capacity to do something, the error is absolutely reachable. In the case where we do know that we always have the capacity, out of memory is a meaningless expression because we already have that memory in hand.

Now, you could argue that this entails we should rewrite the whole thing. I think this is a reasonable objection - on principle you could argue that unreachable code is just code that should not be written. In practical cases where we have to work with existing API’s, it at least expresses that something is not logically reachable, even if the calling function is built to believe that it is.

– edited to add a conclusion –

I will try to restate that example more generally. If a function has preconditions that can be violated, we can provide an error to signal that we failed to meet the preconditions. In that case, the precondition is that we have enough memory on our machines to do the required work. If I can prove that the preconditions are already met, the error becomes a meaningless expression.

2 Likes

BTW if you want to get more philosophical, check out these classic talks from Joe Armstrong:

This is the philosophy used by ~50% of the world’s mobile device infrastructure.

If you want some influence from the more rigid/principled side, look for papers and talks on formal verification. I don’t know if I have a single specific thing to recommend on this front, but some of the work around HOL Light has been fascinating to me.

1 Like

I leave, a posteriori that there should be no error in the internal soup and that everything must be managed, on the other hand, all actions coming from the outside must involve error management.

So the internal code of a generator (from source) will not produce an error, but all manual actions of adding or modifying the generated code must be under control.

Here is my code of conduct for the generated code, in fact these are not functional modification actions, but data that I must control. Ensure data consistency and report when this happens
ex: I want to update a field on the screen that does not exist (spelling error) fld.index( panel, fieldname) !usize {…
There I will put an error handler
however, there will be no code intrusion.
Or else, we close everything by making it only one object, but there, we come back to proprietary solutions…
The engine code is free open.
In fact, what I do is for computer scientists who code.
A product to relieve stewardship which helps take care of the problem to be dealt with ex for a business then if more than 80% is generated and just functional uses of the style remain

I have to enter the name then display the complete article file
I can put the zones inactive (hidden zone) as long as I have not validated the name of the article and vice versa, this is the kind of action that the coder will have once the source code has been generated. After if it does not activate the indicator to display, it is no longer my responsibility, but the functional and not the engine that runs the generated code.

You can generate the code by hand in the same way as the designer (generator) but it is much longer and requires more debugging I have already done this when I was working, a time saving divided by 10 when you know the pressure we have, because, it’s still for yesterday and generating screens and writing the code of the functional program are well separated, one must come from a tool the other a response to the company.

I tried to be clear hummm hummm, I’m not sure :wink: :wink: ;).

by reading threads I may have found one of the answers…

A solution that answers one of my questions,
having functions used in a routine, well loop, there I can use it and put catch .{} , but it will still throw the error just that in my internal routine I don’t need to handle it.
i think i am right??? .

somethingThatThrowsAnError() catch {};
                             ^^^^^^^^

This will completely ignore any error thrown by my example somethingThatThrowsAnError function.

1 Like

yes, but it’s more complicated than that, there are commands like alloc… buffer which don’t accept cacth{}
the other orders for example write

but I’m glad I spent the day revising and testing catch…
and I found some typos in my code about the practice of “catch”
anyway there are times when unreachale won’t be a problem, for example an allocPrint with parameters to control, I don’t see where it can crash

Can you show us your code?

the first code i would have wanted in my engine, to ignore the error, but not for any other use i need to know there is an error.

pub fn strToUsize(v: []const u8) usize{
  return std.fmt.parseUnsigned(u64, v,10) catch {}; error  compil
}

i =  strToUsize("toto")  ;

other

obviously, I can’t have an error, at worst I don’t get anything back

the order in question
const result = allocator. alloc(u8, n - pos) catch unreachable;
impossible to put catch {}

pub fn subStr( a: []const u8,pos: usize, n:usize) []const u8 {
  if (n == 0 or n > a.len) return "error substr";
  if (pos > a.len) return "error substr";
  const allocator = std.heap.page_allocator;
  const result = allocator.alloc(u8, n - pos) catch unreachable;
  defer allocator.free(result);
  std.mem.copy(u8, result, a[pos..n]);
  return std.fmt.allocPrint(allocator,"{s}",.{result},)  catch unreachable;
}

there are many solutions but it becomes allambic
this is a very dirty piece of code

I found a solution

  pub fn dsperr(errpgm :anyerror  ) void { 

    std.debug.print("error {}",.{errpgm});

  }

pub fn strToUsize(v: []const u8) usize{

      var n :u64 =  0;
      n = std.fmt.parseUnsigned(u64, v,10)  catch |err| { dsperr(err); return 0 ;};
 
  return n ;
}

adequate

I ended up finding a solution:
I still don’t want the program to stop so I’ll do as I showed, but a bit cleaner.
I still wanted to report that there is an error, and I would like on a 5250 a message at the bottom of the screen.

I believe you already answered your own question, but I’m writing a follow-up for other people reading this post.

The issue with this line of code is that it still needed to return something from this function. In the case where you catch the error, it was not returning something else in place of the value. So you could do this for instance (I’m not recommending this, but it will allow the program to compile):

pub fn strToUsize(v: []const u8) usize {
  return std.fmt.parseUnsigned(u64, v,10) catch return 0;
}

@JPL Have you considered using std.log instead of printing to debug? You may already have considered this, I just wanted to point it out.

1 Like

Completely agree with you

Completely agree with you was to get your opinion

const std = @import("std");

  pub fn dsperr(errpgm :anyerror  ) void { 
    const stdin = std.io.getStdIn().reader();
    std.debug.print("\nTo report: {s}\n",.{@errorName(errpgm)});

    var buf : [4]u8 =  [_]u8{0} ** 4;
    _=  stdin.readUntilDelimiterOrEof(buf[0..], '\n') catch unreachable;
  }


// internal normal
fn strToUsize_00( str : []const u8 ) usize{
  return std.fmt.parseUnsigned(u64, str,10) 
        catch |err| { @panic(@errorName(err));};
}


// astuce dangereuse reserve internal function
fn strToUsize_01(str: []const u8) usize{
      return std.fmt.parseUnsigned(u64, str,10)  catch  { return 0;}; 
}


//  mise a dispo utilisateur pub fn

pub const ErrForms = error{
  char_not_digital_invalide
};

pub fn strToUsize_02( str : []const u8) usize{

var digit: [1][] u8 = undefined;
var buffer : [100] u8 =  [_]u8{0} ** 100;

digit [0]  = std.fmt.bufPrint(buffer[0..], "{s}",.{str}) 
                  catch |err| { @panic(@errorName(err));};

      for ( digit, 0..) |d, idx| {

        if ( !std.ascii.isDigit(d[idx])) {
            dsperr(ErrForms.char_not_digital_invalide);
            return 0 ;
        }
      }

  return std.fmt.parseUnsigned(u64, str,10) 
        catch |err| { @panic(@errorName(err));};


}

// data zone internal area preflight max len usize


pub fn main() !void {

var i : usize = 10;
var data : [] const u8 = undefined ;

data = "1234";
i =  strToUsize_00(data)  ;

std.debug.print(" preflight area 00>{d}\n",.{i});

data = "9876";
i =  strToUsize_01(data)  ;

std.debug.print("internal area preflight 01>{d}\n",.{i});


data = "abc01";
i =  strToUsize_02(data)  ;

std.debug.print("free zone upstream 02>{d}\n",.{i});

data = "1234";
i =  strToUsize_02(data)  ;

std.debug.print("free zone upstream 02>{d}\n",.{i});

}
1 Like

Aha, yes - I see. It’s very understandable to dispatch function calls like this. Separating from external and internal. In this case, I can see what you meant by having to carefully choose error handling mechanisms. You’ve got a lot of variety in a small section of code! :slight_smile: