From: Dmitry A. Kazakov on
On Thu, 13 May 2010 04:03:38 +0200, Georg Bauhaus wrote:

> All of these features of C have merits besides what else
> they may have. But they sure don't warrant dismissing DbC?

No. The point was about lack of substance in DbC as promoted by Eiffel.

> Ada seems better here. But before the arrival of preconditions
> etc. Ada didn't have that much to offer to programmers wanting
> to state sets of permissible values, for example for subprogram
> parameters.

Ada has strong types. "A set of permissible values" does not look like
that.

> Ada subtypes, e.g. scalars, are poorly equipped for this style of
> value set specification, AFAICS. They do establish two implicit
> preconditions (>= S'First and <= S'Last).

These are not preconditions, but invariants.

> A Qi style type system could do more via types but is not to be in Ada
> any time soon, is it?

I don't know. If Ada designers will keep on driving Ada into a dynamically
typed language, then probably never.

> package P is
>
> type Number is range 1 .. 10;
> function Compute (A, B: Number) return Number
> with Precondition => A >= B;

I don't understand this notation.

> (Such specifications, if they were to be being static, would exclude
> a test of A in relation to B at run time,

You need not to specify anything you cannot check... As somebody said, do
not check for bugs you aren't going to fix. (:-))

> A distinguishing property of DbC visible here is
> that a programmer will *see* the contract (or part thereof).

I do not accept "preconditions" in the operations, which are not statically
true:

function "/" (X, Y : Number) return Number -- Wrong!
pre : Y /= 0.0;

function "/" (X, Y : Number) return Number -- Right!
post : result = X/Y or else Constraint_Error;

>>> Which set of "specifications" has only static things in it?
>>> Why exclude conditionals whose results cannot reasonably
>>> be computed by the compiler but
>>>
>>> (a) can be computed for every single case occuring in the
>>> executing program
>>
>> If they can for *every* case, they are statically checkable, per
>> definition.
>
> "Every case" cannot "reasonably be computed by the compiler".

Either they can or cannot. You said they can, but then added "unreasonably
can", or "reasonably cannot", whatever. Sound like Eiffel's contracts, x
is even, not really even, we wanted it be even... (:-))

>>> (b) can guide the author of a client of a DbC component?
>>
>> I don't know what does it mean technically.
>
> Contractually, it means "do as the preconditions say."

Like "do as comments say"? Note, since your "preconditions" are not static,
how can you know *what* they say, in order to do as they do? Eiffel masters
with easily. Its DbC is "do as you want, we'll see later." But I think that
the honor of inventing this design principle by right belongs to C...

> "Under no circumstances shall the body of a routine ever
> test for the routine's precondition." -- OOCS2, p.343
>
> IOW, no redundant checks.

No, it would be *inconsistent* checks. No program can check itself. Eiffel
pre- and postconditions fall into this category.

Of course, I can imagine a compiler which would use a separate CPU core to
check pre- and ponstconditions at tun-time. but that is beyond the point.

>>> For example, assume that Is_Prime(N) is a precondition of sub P.
>>> Furthermore, TIME(Is_Prime(N))>> PERMISSIBLE_TIME.
>>> Then, still, PRE: Is_Prime (N) expresses an obligation,
>>> part of a contract: Don't call P unless N is prime,
>>> no matter whether or not assertion checking is in effect.
>>
>> char X [80]; // Don't use X[80], X[81], no matter etc.
>
> Yes, and don't use X[-200]. But you can. OTOH:
>
> function Access_Element (S: String_80; X: Index_80);
>
> function Careful (S: String_80; X: Index_80)
> with Precondition => X rem 2 = 1;
>
> How would you write a statically checked version?

Trivially:

function Careful (S: String_80; X: Index_80)
with Precondition True;
with Postcondition <whatever> or Constraint_Error

>>> (A DbC principle is that assertions are *not* a replacement
>>> for checking input (at the client side).)
>>
>> Exactly. Assertion is not a check. You said this.
>
> What check means, exactly, is important.

Error_Check (P) => P is incorrect

other things you mentioned are control flow control statements. It is
really simply: an error/correctness check is a statement about the program
behavior. If-statement/exception etc is the program behavior.

>>>>> What is "breach of contract" in your world?
>>>>
>>>> A case for the court.
>>>
>>> What's the court (speaking of programs)?
>>
>> A code revision.
>
> How do you become aware of a broken contract?
> Statically? Dynamically?

Me? I am not a part of the system. These terms do not apply to me. Before
going any further, you should define the system under consideration. Note
that your "DbC mindset" includes the programmer(s) into the system,
delivered and deployed. Isn't that indicatory? (:-))

>>> I'm a programmer. If I "design" anything, it is a program whose
>>> parts need to interact in a way that meets some almost entirely
>>> non-mathematical specification.
>>
>> Mathematics has nothing to do with this. It is about a clear distinction
>> between a contract and a condition, how undesirable it could be but
>> nevertheless it is a condition, which you, as a programmer are obliged to
>> consider as *possible*. Eiffel gives you an illusion of safety.
>
> No. Eiffel marketing claims that a fully proven DbC program
> is 100% bug free.

I can imagine Microsoft marketing doing same, if they didn't already...

>> Simply consider
>> the following rule: my program shall handle each exception I introduce.
>> Objections?
>
> No objection.
>
>> Then consider exceptions from assertions.
>
> That is what Eiffel's rescue mechanism is about.

And how do you rescue from:

function "-" (X : Positive) return Positive;

Note, if you handle an exception then it is same as if-statement. (I agree
that if-statements could be better arranged, have nicer syntax etc. Is that
the essence of Eiffel's DbC?)

>>>>> How would you specify
>>>>>
>>>>> subtype Even is ...; ?
>>>>
>>>> Ada's syntax is:
>>>>
>>>> subtype<name> is<name> <constraint>;
>>>
>>> What will static<name> and static<constraint> be for subtype Even?
>>
>> No, in Ada<constraint> is not required to be static. Example:
>>
>> procedure F (A : String) is
>> subtype Span is Integer range A'Range;
>
> Is the constraint, not to be static, then part of the contractual
> specification of subtype Even?

The specification of includes all possible values of the constraint. When
you assign -1 to Positive, it is not an error, it is a valid, well-defined
program, which behavior is required to be Constraint_Error propagation.
When you assign -1.0 to Positive, it is an error and the program is
invalid. Where and how you are using these two mechanisms is up to you. A
FORTRAN program can be written in Ada, of course...

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Dmitry A. Kazakov on
On Thu, 13 May 2010 02:37:37 +0200, stefan-lucks(a)see-the.signature wrote:

> On Wed, 12 May 2010, Dmitry A. Kazakov wrote:
>
>> On Wed, 12 May 2010 20:10:26 +0200, stefan-lucks(a)see-the.signature wrote:
>>
>>> In Eiffel, I'll either get an answer, or the program will tell that at
>>> some point of time when computing, say, the factorial of 100, a certain
>>> exception has been raised. (I didn't try out Eiffel, but that is what I
>>> would expect.) But if I get an answer, I can be sure it is the right one.
>>> That is partial correctness.
>>
>> 1. Wrong answer is no more/less incorrect as an exception.
>
> We clearly disagree on this point.
>
> Consider being new in a town, and asking how to go from, say, the railway
> station to the market place. If we ask a guy who appears to be a local
> person in our eyes, but is actually as foreing as we are, most people
> would prefer an exception "sorry, I don't know the way" over the answer of
> being directed into the wrong direction. If you think, the "sorry" is as
> bad as the wrong direction, that is your problem, which I am not
> interested in discussing any further.

Consider another example. Let you have a word processor, which also
calculates frequencies of some words. After typing dozen pages of the
document, you remove a word (the last instance, occasionally), and the
processor crashes on zero divide trying to calculate its new frequency. I
bet you would prefer a rubbish frequency displayed.

The bottom line, the effect of an error is undefined. You cannot define any
reasonable action upon, UNLESS further information were available. Which is
equivalent to *specification* of a behavior. Once the behavior is defined,
it is no more an error, e.g. the frequency of an unused word is displayed
as an empty string. It is a mere requirement. If you don't know the answer,
say "I don't know", do not shoot.

>> 2. It is unrelated to error checks. The programmer did not use any. That
>> Eiffel possibly checks for integer overflow and C does not is irrelevant to
>> the issue.
>
> The exception may just as well be raised when checking the postcondition.

or from an if-statement placed by the programmer.

>> {Any Precondition}
>> raise Program_Error;
>> {Postcondition}
>
> Yes, that program is partially correct.

I wouldn't consider it as such.

> Well, Hoare did originally not bother with exceptions,

BTW Hoare, wasn't it actually Edsger Dijkstra?

>> The problem is that Program_Error does not satisfy postcondition.
>
>> His approach, which I greatly appreciate, is perfectly compatible with
>> exceptions. Exception propagation is a part of the program behavior to be
>> checked as anything else. E.g.
>>
>> pre : x = 1
>> if x = 1 then
>> raise Foo;
>> end if;
>> post : Foo propagating
>>
>> This program were be incorrect if it would not raise Foo.
>
> Now I see what you mean. You are asking for contracts which include
> exception propagation. Yes, that would be nice to have.

How could it be otherwise?

> Then you should like Eiffel, because this is what Eiffel actually does. In
> Eiffel, the formal postcondition
>
> require P;
>
> is a shortcut notation for the logical
>
> Postcondition: P or else (Postcondition_Violation propagating);
>
> (whatever the name of the exception is), which is the real postcondition
> of an Eiffel program. If Eiffel checks P at the end of the program, and
> raises that exception when the check fails, then
>
> Eiffel-programs are always correct!

Yes. Therefore a lazy programmer could write

raise Postcondition_Violation;

and claim his salary.

> You don't even need to perform any static analysis to ensure their
> correctness. (But you must not turn of the checks!)

Nice language, the program semantics depends on the compiler options. (We
have this in some places in Ada too, unfortunately)

> Of course the catch is that you would rather want to specify
> postconditions which exclude exception propagation.

Yep.

> -> Eiffel is a very expressive programming language,
> while SPARK is less expressive.
>
> -> The language for contracts in SPARK is more expressive
> than the language for contracts in Eiffel.

OK

> That doesn't mean that the contracts you can express in Eiffel are
> invalid, however.

With the addition you made they are not. Once you remove
Postcondition_Violation (which can be done by a compiler switch), they
become invalid.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Georg Bauhaus on
On 5/13/10 10:39 AM, Dmitry A. Kazakov wrote:

>> Ada seems better here. But before the arrival of preconditions
>> etc. Ada didn't have that much to offer to programmers wanting
>> to state sets of permissible values, for example for subprogram
>> parameters.
>
> Ada has strong types. "A set of permissible values" does not look like
> that.

Ada's strong types are not really strong enough to express
a supplier library's ADT expectations in the sense of DbC.
Subtypes (or the type system) would need to by more specific for that.

One OOSC2 example is easily handled by Ada's subtypes,
though.

It also shows that DbC does *not* assume contracts should
necessarily become a meaningful part of your executing program,
i.e. have the program do assertion monitoring. On the contrary.

function sqrt(x: REAL) return REAL;
pragma Precondition (X >= 0.0);
-- and something like pragma
--- Postcondition (sqrt'Result * sqrt'Result = x);

function Sqrt(x: REAL) return REAL is
begin
...
end Sqrt;

(The requirement that X >= 0.0 on entry will actually be part of
a translated *Ada* program if REAL above is defined as a
subtype of some type that includes values below 0.0.
The check is not necessarily part of a corresponding Eiffel
program that has require X >= 0.0.)
Anyway, wherever the Ada compiler sees that a value passed to Sqrt
is < 0.0, it can warn at least.

The following would *not* be a DbC style Sqrt:

function Sqrt_Not_DbC (X: REAL) return REAL is
begin
if X < 0.0 then
-- "Handle the error, somehow"
else
-- "Proceed with normal square root computation"
end if;
end Sqrt_Not_DbC; -- cf. OOSC2, p.343

Instead, supplier is supposed to write the program Sqrt as if
X >= 0.0 on entry. Only if X >= 0.0 can client legitimately expect
supplier's Sqrt to terminate in a state that satisfies the postcondition
and INV. (INV is probably just True here.)


>> Ada subtypes, e.g. scalars, are poorly equipped for this style of
>> value set specification, AFAICS. They do establish two implicit
>> preconditions (>= S'First and<= S'Last).
>
> These are not preconditions, but invariants.

'First and 'Last are invariants of the type and they express
preconditions when reinterpreted in the normal DbC way.
Again, can you produce a statically checked invariant for
subtype Even?


>> package P is
>>
>> type Number is range 1 .. 10;
>> function Compute (A, B: Number) return Number
>> with Precondition => A>= B;
>
> I don't understand this notation.

It says that the client of Compute would have to provide
A, B such that A >= B if client expects Compute to produce
its postcondition; otherwise, the expectation is unjustified
and Compute is not bound by the contract, i.e. may fail and
produce whatever, including an exception.

The contract here is this:
I, client, give you, Compute, numbers A and B that are as
you told me.
I, Compute, will produce my Postcondition if you give me
numbers A and B as I told you.



> I do not accept "preconditions" in the operations, which are not statically
> true:

That's your choice then, but not DbC's, which is more
inclusive (again, statically checked subtype Even vs. X rem 2 = 0).


> function "/" (X, Y : Number) return Number -- Wrong!
> pre : Y /= 0.0;
>
> function "/" (X, Y : Number) return Number -- Right!
> post : result = X/Y or else Constraint_Error;

The second "/" is a DbC one. There is a big warning sign in OOSC2 that
says,

/ \
NO PRECONDITION
TOO BIG
OR TOO SMALL
\ /

While a matter of choice, there is one strict rule: "It is _never_
acceptable to deal with a correctness condition on both the client
and supplier sides."



>>>> Which set of "specifications" has only static things in it?
>>>> Why exclude conditionals whose results cannot reasonably
>>>> be computed by the compiler but
>>>>
>>>> (a) can be computed for every single case occuring in the
>>>> executing program
>>>
>>> If they can for *every* case, they are statically checkable, per
>>> definition.
>>
>> "Every case" cannot "reasonably be computed by the compiler".
>
> Either they can or cannot. You said they can, but then added "unreasonably
> can", or "reasonably cannot", whatever. Sound like Eiffel's contracts, x
> is even, not really even, we wanted it be even... (:-))

When I mentioned "not reasonably be computed by the compiler" early on,
I meant that DbC should be a possible system that helps programmers.
I did not want to theorize program analysis systems that are impossible
to produce.

I can think of a DbC program with assertion monitoring turned on
and see it as a stepwise attempt at working out the correctness
properties of the program. For example, I can see the program
crash when a new change set makes some variable have a different
value. This value no longer meets the stated requirements of
some subprogram. That is shown then by the run-time and I can
think again. Preferrably think about possible mistakes in my
attempts at corectness lemmata, since otherwise this procedure
will degrade into debugging things into some state.

But DbC here yields more than dealing with exceptions and erroneous
execution in some random fashion can possibly achieve!


>>>> (b) can guide the author of a client of a DbC component?
>>>
>>> I don't know what does it mean technically.
>>
>> Contractually, it means "do as the preconditions say."
>
> Like "do as comments say"?

Yes. Only, a DbC system can be quite helpful in checking
my understanding of more formal "comments".


> Note, since your "preconditions" are not static,
> how can you know *what* they say, in order to do as they do?

I can write my client code such that the preconditions are
true. I know what a procondition says if I do not need to
solve hard problems to know the meaning of a predicate.
Note that DbC includes requirements that are stated in
natural language. Of course, not even trying to write
predicates defeats the purpose of contracting between
client and supplier, assisted by the DbC system.

E.g., I read a number from input, and another number
from input. I know that subprogram P wants one number to be odd
and the other number to be even. (Otherwise, it does not guarantee
that it produces its postcondition.) These are easily
tested conditions. I know what I have to test and I know
how to test what.


>> IOW, no redundant checks.
>
> No, it would be *inconsistent* checks. No program can check itself.

DbC checks are not part of the (intended) program. Monitoring can be
turned off and this should have no effect on program correctness.
The proof obligation rests on us, the programmers.


>>>> For example, assume that Is_Prime(N) is a precondition of sub P.
>>>> Furthermore, TIME(Is_Prime(N))>> PERMISSIBLE_TIME.
>>>> Then, still, PRE: Is_Prime (N) expresses an obligation,
>>>> part of a contract: Don't call P unless N is prime,
>>>> no matter whether or not assertion checking is in effect.
>>>
>>> char X [80]; // Don't use X[80], X[81], no matter etc.
>>
>> Yes, and don't use X[-200]. But you can. OTOH:
>>
>> function Access_Element (S: String_80; X: Index_80);
>>
>> function Careful (S: String_80; X: Index_80)
>> with Precondition => X rem 2 = 1;
>>
>> How would you write a statically checked version?
>
> Trivially:
>
> function Careful (S: String_80; X: Index_80)
> with Precondition True;
> with Postcondition<whatever> or Constraint_Error

This is legitimate I'd say, but is also is a destructive omission of
what the precondition above does specify. Yours (True) does not tell
the client programmer the conditions that by DbC will guarantee
Postcondition. Without hardware failure or other occurrences
that cannot be expected to be handled by the Careful algorithm,
Careful must produce Postcondition provided odd Index_80 numbers
are passed for X. But in your version,

(a) there is nothing the programmer knows about valid Index_80
values (viz. the odd ones)

(b) there is no debugging hook that can be turned on
or off (monitoring the precondition X rem 2 = 1).


> other things you mentioned are control flow control statements. It is
> really simply: an error/correctness check is a statement about the program
> behavior. If-statement/exception etc is the program behavior.

Normal If-statements and exceptions cannot be turned off.


> Note
> that your "DbC mindset" includes the programmer(s) into the system,
> delivered and deployed. Isn't that indicatory? (:-))

The programmer is the one ingredient that matters most in DbC,
since programming is a human activity, and contracts are between
clients and suppliers of software components (classes).


> And how do you rescue from:
>
> function "-" (X : Positive) return Positive;
>
> Note, if you handle an exception then it is same as if-statement. (I agree
> that if-statements could be better arranged, have nicer syntax etc. Is that
> the essence of Eiffel's DbC?)

I'm assuming this is not an input-sanitizing function.

You don't handle the exception as part of the algorithm:

minus: INTEGER
require Current > 1

do
Current := Current - 1
end

ensure Current + 1 = old Current

Where Ada requires "ifs" for bounds checking and raising an
exception accordingly, DbC/Eiffel requires that you express the
correctness properties of subprograms *and* that you choose
whether or not you want the "if", and which ones.


>>>>>> How would you specify
>>>>>>
>>>>>> subtype Even is ...; ?
>>>>>
>>>>> Ada's syntax is:
>>>>>
>>>>> subtype<name> is<name> <constraint>;
>>>>
>>>> What will static<name> and static<constraint> be for subtype Even?
>>>
>>> No, in Ada<constraint> is not required to be static. Example:
>>>
>>> procedure F (A : String) is
>>> subtype Span is Integer range A'Range;
>>
>> Is the constraint, not to be static, then part of the contractual
>> specification of subtype Even?
>
> The specification of includes all possible values of the constraint. When
> you assign -1 to Positive, it is not an error, it is a valid, well-defined
> program, which behavior is required to be Constraint_Error propagation.
> When you assign -1.0 to Positive, it is an error and the program is
> invalid. Where and how you are using these two mechanisms is up to you. A
> FORTRAN program can be written in Ada, of course...
>

This specifies that assigning -1 to a positive will raise an exception.
It does not specify the possible values for Even (which would be
constrained to include only even numbers).

From: Robert A Duff on
Georg Bauhaus <rm.dash-bauhaus(a)futureapps.de> writes:

> How would you specify
>
> subtype Even is ...; ?

In Ada 2012, if AI-153 gets approved, you will be able to say:

subtype Even is Integer
with Predicate => (Even mod 2) = 0;
^^^^ refers to the "current instance"
of the subtype

That particular example isn't very useful, but this kind of
thing is:

type Color is (Red, Orange, Yellow, Green, Blue);
subtype Primary_Color is Color
with Predicate => Primary_Color in (Red, Green, Blue);

By the way, I don't think "static" is a property of assertions.
It's a property of how they're checked. If we have
a precondition that "X > Y" (where X and Y are formal parameters),
that's neither "static" nor "dynamic" in and of itself.
One tool (e.g. an Ada compiler) might check it dynamically.
A smarter tool (a proof checker, or a smarter compiler,
or even a human being) might check it statically.
It might even be checked statically at some call sites,
but not others.

- Bob
From: Dmitry A. Kazakov on
On Sat, 15 May 2010 01:45:50 +0200, Georg Bauhaus wrote:

> On 5/13/10 10:39 AM, Dmitry A. Kazakov wrote:
>
>>> Ada seems better here. But before the arrival of preconditions
>>> etc. Ada didn't have that much to offer to programmers wanting
>>> to state sets of permissible values, for example for subprogram
>>> parameters.
>>
>> Ada has strong types. "A set of permissible values" does not look like
>> that.
>
> Ada's strong types are not really strong enough to express
> a supplier library's ADT expectations in the sense of DbC.

No type system can express the program semantics. Otherwise you would need
not program any bodies, only declarations.

> Again, can you produce a statically checked invariant for
> subtype Even?

Yes, I can:

type Even is private; -- No literals visible
function To_Integer (X : Even) return Integer;
function To_Even (X : Integer) return Even;
private
type Even is new Integer; -- Use literals as follows: 1 means 2

function To_Integer (X : Even) return Integer is
begin
return Integer (X * 2);
end To_Integer;

function To_Even (X : Integer) return Even is
begin
if X mod 2 = 0 then
return Even (X / 2);
else
raise Constraint_Error;
end if;
end To_Even;

>>> package P is
>>>
>>> type Number is range 1 .. 10;
>>> function Compute (A, B: Number) return Number
>>> with Precondition => A>= B;
>>
>> I don't understand this notation.
>
> It says that the client of Compute would have to provide
> A, B such that A >= B if client expects Compute to produce
> its postcondition; otherwise,

Compute should have the contract: if the sequence A,B is sorted then ...
else Constraint_Error propagated. And of course, A, B is just an interval
[B, A], and should be declared as such:

function Compute (X : Interval) return Number
with Precondition => True;

> Yes. Only, a DbC system can be quite helpful in checking
> my understanding of more formal "comments".

You say that Eiffel DbC is about writing structured comments? I do not
object.

>> Note, since your "preconditions" are not static,
>> how can you know *what* they say, in order to do as they do?
>
> I can write my client code such that the preconditions are
> true.

Really? What about:

pre : not HALT(p)

There is a reason why they aren't static.

>>> IOW, no redundant checks.
>>
>> No, it would be *inconsistent* checks. No program can check itself.
>
> DbC checks are not part of the (intended) program. Monitoring can be
> turned off and this should have no effect on program correctness.

and thus on program execution. q.e.d.

>> Trivially:
>>
>> function Careful (S: String_80; X: Index_80)
>> with Precondition True;
>> with Postcondition<whatever> or Constraint_Error
>
> This is legitimate I'd say, but is also is a destructive omission of
> what the precondition above does specify. Yours (True) does not tell
> the client programmer the conditions that by DbC will guarantee
> Postcondition. Without hardware failure or other occurrences
> that cannot be expected to be handled by the Careful algorithm,
> Careful must produce Postcondition provided odd Index_80 numbers
> are passed for X. But in your version,
>
> (a) there is nothing the programmer knows about valid Index_80
> values (viz. the odd ones)

They are *all* valid, that is the contract of Careful.

> (b) there is no debugging hook that can be turned on
> or off (monitoring the precondition X rem 2 = 1).

Debugging hooks do not belong to code.

>> other things you mentioned are control flow control statements. It is
>> really simply: an error/correctness check is a statement about the program
>> behavior. If-statement/exception etc is the program behavior.
>
> Normal If-statements and exceptions cannot be turned off.

Come on. C has preprocessor!

>> Note
>> that your "DbC mindset" includes the programmer(s) into the system,
>> delivered and deployed. Isn't that indicatory? (:-))
>
> The programmer is the one ingredient that matters most in DbC,
> since programming is a human activity, and contracts are between
> clients and suppliers of software components (classes).

That is what your car manufacturer will tell you next time, when the brake
system will accelerate instead of braking...

>> And how do you rescue from:
>>
>> function "-" (X : Positive) return Positive;
>>
>> Note, if you handle an exception then it is same as if-statement. (I agree
>> that if-statements could be better arranged, have nicer syntax etc. Is that
>> the essence of Eiffel's DbC?)
>
> I'm assuming this is not an input-sanitizing function.

It was unary minus.

> Where Ada requires "ifs" for bounds checking and raising an
> exception accordingly, DbC/Eiffel requires that you express the
> correctness properties of subprograms *and* that you choose
> whether or not you want the "if", and which ones.

Bounds are one kind of constraints. Eiffel has more of those. Ada goes in
the same direction as Robert's response shows. I don't like it for two
reasons:

1. Dynamic checks. This was discussed. Correctness checks shall be static,
else it is a part of the behavior to be contracted as any other.

2. It is weakly/un-typed. The constraints you impose on input and output
are specifications of a [sub]type.

2.a. This type is anonymous
2.b. This type mutates (precondition /= postcondition)
2.c. This type is inferred and structurally equivalent

To me this is a deep breach with the core of Ada type system.

> This specifies that assigning -1 to a positive will raise an exception.
> It does not specify the possible values for Even (which would be
> constrained to include only even numbers).

You have to define "possible value":

1. Member of the type domain set. (3 is a member of the Even as a subtype
of Integer)
2. Value of an initialized instance (3 cannot be a value of any Even
object)

(Considering Even an Ada subtype or a type inheriting to integer.)

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de