On one hand, I do agree that invariants are easier to read when they’re stated positively. In my code, I follow every invariant-checking if statement with a comment that says something along the lines of // Invariant: the array is large enough for this algorithm to make sense.
On the other hand, my code usually checks that the invariant is not held, and exits early. So my comments state the invariant positively, but the code states it negatively.
I think I could get used to negating a positively stated invariant:
if (!(array.size() > MIN_DATAPOINTS_COUNT)) {
return;
}
But that looks quite ugly to me and I immediately itch to rewrite it to <=.
Yup! This reasoning sold me on Swit’s guard statement, its awesome to be able to write something along the lines of
assert invariant orelse return
That’s why the advice here is to try to re-arrange the code such that the positive invariant is natural, rather than to just mechanically postivise it — you need guard / unless for the mechanical transformation to always give good results.
Usually if I find a conditional hard to read I’ll pull it out into an intermediate variable. I never really do it with single clause conditions, but for illustrative purposes your example would become something like:
hasEnoughData = array.size() > MIN_DATA_POINTS_COUNT
if !hasEnoughData {
return
}
It allows you to state the property positively, but show that the condition is about the negative case.
We check if the array hasEnoughData. The property is easy to verify.
The conditional is about the negative case. The intent is clear.
We know exactly what the property is checking because the variable name acts as a micro comment that is less likely to become out of date.
The benefits of it are much better with more complex conditions.
Another reason to state things positively, is that the results make more sense when NaN is involved.
For example:
is wrong if
x
is NaN. NaN is outside the number line, so it does not fall within, above, or below this interval.Rather than add separate NaN checks everywhere, phrase it positively:
The second rule has me conflicted.
On one hand, I do agree that invariants are easier to read when they’re stated positively. In my code, I follow every invariant-checking
if
statement with a comment that says something along the lines of// Invariant: the array is large enough for this algorithm to make sense
.On the other hand, my code usually checks that the invariant is not held, and exits early. So my comments state the invariant positively, but the code states it negatively.
I think I could get used to negating a positively stated invariant:
But that looks quite ugly to me and I immediately itch to rewrite it to
<=
.Yup! This reasoning sold me on Swit’s
guard
statement, its awesome to be able to write something along the lines ofThat’s why the advice here is to try to re-arrange the code such that the positive invariant is natural, rather than to just mechanically postivise it — you need
guard
/unless
for the mechanical transformation to always give good results.Usually if I find a conditional hard to read I’ll pull it out into an intermediate variable. I never really do it with single clause conditions, but for illustrative purposes your example would become something like:
It allows you to state the property positively, but show that the condition is about the negative case.
hasEnoughData
. The property is easy to verify.The benefits of it are much better with more complex conditions.
Interesting… I wonder if anyone has written a language with < and <= but not > or >=?
guard-else/assert-orelse/“if not” seems like a really nice language feature - I would definitely use that.