Skip to main content
added 36 characters in body
Source Link
J_H
  • 42.1k
  • 3
  • 38
  • 157

Design by Contract is one path toward that. It promotes composability of components.

Design by Contract is one path toward that.

Design by Contract is one path toward that. It promotes composability of components.

added 36 characters in body
Source Link
J_H
  • 42.1k
  • 3
  • 38
  • 157

Assuming we can malloc(), and the power is on, this function pretty much always returns what you expect, when given the proper object type.

Yes. Or no. It's not your responsibility responsibility. A pre-condition wasn't satisfied or you can't make your post-condition true for whatever reason, so you bail out and make the caller aware of it. What the caller does is up to him, it's not your job.

Assuming we can malloc(), and the power is on, this function pretty much always returns what you expect.

Yes. Or no. It's not your responsibility. A pre-condition wasn't satisfied or you can't make your post-condition true for whatever reason, so you bail out and make the caller aware of it. What the caller does is up to him, it's not your job.

Assuming we can malloc() and the power is on, this function pretty much always returns what you expect, when given the proper object type.

Yes. Or no. It's not your responsibility. A pre-condition wasn't satisfied or you can't make your post-condition true for whatever reason, so you bail out and make the caller aware of it. What the caller does is up to him, it's not your job.

Source Link
J_H
  • 42.1k
  • 3
  • 38
  • 157

manifesto

Code should be bug-free.

Design by Contract is one path toward that.

What should I do with the fatal errors? Make the program crash?

Yes.

Or make it do the Right Thing, if you prefer. (The current implementation just ignores the error and stumbles forward.)

total function

A function is specified to accept inputs and produce some result: return value and/or side effects.

The inputs can be explicit in the signature, they can also be global variables, filesystem fullness, wallclock time, PRNG state, and so on.

Some functions are total -- they will always produce a correct result, for any input. Here is one, in python:

def increment(n: int) -> int:
    return n + 1

Assuming we can malloc(), and the power is on, this function pretty much always returns what you expect.

Here is a similar function in C:

uchar increment(uchar n) {
    return n + 1;
}

Calling increment(254) does the Right Thing. But probably the caller is being too adventurous upon supplying an unsigned arg of 255. For modulo operations this is well defined, but if the contract is to "return a bigger number", then this is a partial function. If this were C++ we might choose to throw an exception for that.

The familiar sqrt() function offers a similar example. If we rule out NaN and complex / imaginary numbers, then caller should avoid offering a negative argument. This is called a pre-condition on acceptable inputs. If we do see negative input, it is appropriate for sqrt to immediately raise a fatal error so caller will fix their broken code.

post-condition

The contract for the root finder looks like this: "Give me a non-negative real. I promise to hand back a real which, when squared, equals the original argument." (We are in FP-land, here, so we finesse "equals" to mean "low relative error", typically specified by some epsilon parameter.)

Now, in a case like sqrt(-3), there is simply no way to fulfill that contract. What should the root finder do? Return a sentinel like 99? (Nevermind that it's indistinguishable from sqrt(9801).) Maybe "better" sentinel values happen to be available, like -99? Sure, that's one possibility that folks have tried. It makes the contract more complex, and some callers will forget to check for that condition. Or, we could throw up our hands!

The much better alternative, when faced with an "impossible" contract, is to give up, with raise. Did we return some float value? No. We returned nothing, the computation didn't happen.

Throwing an exception interrupts the flow so caller won't accidentally trust some arbitrary sentinel value as being "correct". It might be a fatal exception, which halts with a stack trace. Or the caller might have anticipated this situation, and have some strategy for coping with it. Caller might try an alternative call (flip the sign), or pause while it asks a human for help, or re-raise so its higher level caller is aware of the situation.

Make the program crash?

Yes. Or no. It's not your responsibility. A pre-condition wasn't satisfied or you can't make your post-condition true for whatever reason, so you bail out and make the caller aware of it. What the caller does is up to him, it's not your job.