red / REP

Red Enhancement Process
BSD 3-Clause "New" or "Revised" License
11 stars 4 forks source link

Designs to limit error propagation in our code #132

Open hiiamboris opened 2 years ago

hiiamboris commented 2 years ago

Early error detection often saves us debugging time and provides cleaner error messages. It's always important to know what's the first thing that has gone wrong.

Code we write has a certain structure. Single function body is usually written by a single person in a short period of time and is tested as a whole, but even then it's useful to put assertions into the body when one is not 100% certain in it's logic validity. Aggregates of functions - objects, DSLs, source files, modules, programs, remote endpoints - are more complex and it's much harder to oversee data exchange between them. This is where most bugs creep in, and why all Red functions have type signatures, unlike some quick'n'dirty scripts.

Common junctions on the path of data exchange are:

  1. function arguments as data enters it
  2. function return value as data leaves it: e.g. one may put exit somewhere, forgetting to return a value
  3. exposed (unprotected) object fields (facets): what one can assign without breaking the object
  4. dialectic data as being parsed: it's easy to parse a dialect today, but quite annoying to provide error detection and reporting
  5. remote and inter-process exchange (API endpoints), which may be organized as any of the above, but it's common to use functions interface

If I was to rate their validity checking importance from 0 to 10, I would give [10 4 6 7 10] respectively. Return values are often tested by test suites, and object fields aren't always for tinkering, and dialects are not as widespread to compete in importance.

Checks can be divided by category:

We only have 1A implemented, and even 2A serves only for documentation purposes.

What happens if e.g. I assign to a face's facet something it doesn't expect? It either:

This is ultimately a doomed approach to design (as if we were carving a window after building a solid concrete wall), and it only allows us to save time now and better undestand the weak points in our design so we can build better later.

I propose using this REP as a place for thoughts on how to improve language's DbC support in the area of data validity checking.

To be competitive eventually we'll need language or library support for all of this.


What I currently have:

Junction \ Category A. Type B. Value (range, class) C. Relations D. Syntax
0. General-purpose #assert #assert #assert N/A
1. Function arguments included in Red advanced-function (#assert)* N/A
2. Function return (#assert)** (#assert)** N/A N/A
3. Object fields classy-object, typed-object classy-object, typed-object N/A
4. Dialect syntax N/A N/A N/A #expect
5. Remote calls

* One not covered here case is conflicting refinements test, which is currently adhoc too. On compatibility matrices. Implementation must be useful on both R/S and Red levels, so fast.

** #assert covers unit (module) testing, but not runtime return value checks, which are also useful, since tests can only cover a very limited domain.

For class validity (B) checks we'll need REP #102, then object is like any other argument.

Links and my confidence in the design (feedback is welcome):

Design Confidence Comment
#assert 9/10 Copy or not is the only question still nagging at me
advanced-function 5/10 Fresh yet
classy-object 5/10 Fresh yet
typed-object 3/10 Just an early experiment, likely to be unified with classy-object
#expect 2/10 Useful, but need a better idea

Spaces serve as guinea pig for classy-object and advanced-function, but while they are a great fit for the first (being objects open to user's modification), advanced-function is only relevant on occasion, maybe in a few dozen functions, and is less used there than assertions. I expect some more function-oriented code, like geometry, would be a much better test.