You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: better-code/src/chapter-2-contracts.md
+45-43Lines changed: 45 additions & 43 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -142,6 +142,8 @@ Hoare used this notation, called a “Hoare triple,”
142
142
which is an assertion that if **precondition***P* is met, operation
143
143
*S* establishes **postcondition***Q*.
144
144
145
+
<!-- This had been using math for pre and post conditions, but I find mixing math and code makes it look like we are talking about different `x` and $x$ variables and equality vs. assignment gets confusing. I think if the operation is expressed in code, the conditions should be expressed in code. -->
146
+
145
147
For example:
146
148
147
149
- if we start with `x == 2` (precondition), after `x += 1`, `x == 3` (postcondition):
@@ -156,7 +158,7 @@ For example:
156
158
What makes preconditions and postconditions useful for formal proofs
157
159
is this *sequencing rule*:
158
160
159
-
> {P}S{Q} ∧ {Q}T{R} ⇒ {P}S;T{R}
161
+
> {P}S{Q} ^ {Q}T{R} => {P}S;T{R}
160
162
161
163
Given two valid Hoare triples, if the postconditions of the first are
162
164
the preconditions of the second, we can form a new valid triple describing
@@ -327,7 +329,7 @@ When we talk about an instance being “in a good state,” we
327
329
mean that its type's invariants are satisfied.
328
330
329
331
For example, this type's public interface is like an
330
-
array of pairs, but it stores elements of those pairs separate
332
+
array of pairs, but it stores elements of those pairs in separate
331
333
arrays.[^array-pairs]
332
334
333
335
[^array-pairs]: You might want to use a type like this one to store
@@ -716,7 +718,7 @@ Now, not every contract is as simple as the ones we've shown so far,
716
718
but simplicity is a goal. In fact, if you can't write a terse,
717
719
simple, but _complete_ contract for a component, there's a good chance
718
720
it's badly designed. A classic example is the C library `realloc`
719
-
function, which does at least three different things—allocate, deallocate, and resize
721
+
function, which does at least three different things—allocate, deallocate, and resize
720
722
dynamic memory—all of which
721
723
need to be described. A better design would have separated these
722
724
functions. So simple contracts are both easy to digest and easy to
@@ -805,10 +807,10 @@ What are the preconditions for removing an element? Obviously, there
805
807
needs to be an element to remove.
806
808
807
809
```swift
808
-
/// Removes and returns the last element.
809
-
///
810
-
/// - Precondition: `self` is non-empty.
811
-
publicmutatingfuncpopLast() -> T { ... }
810
+
/// Removes and returns the last element.
811
+
///
812
+
/// - Precondition: `self` is non-empty.
813
+
publicmutatingfuncpopLast() -> T { ... }
812
814
```
813
815
814
816
A client of this method is considered to have a bug unless
@@ -821,25 +823,25 @@ has a bug. The bug could be in the documentation of course, *which is
821
823
a part of the method*.
822
824
823
825
```swift
824
-
/// Removes and returns the last element.
825
-
///
826
-
/// - Precondition: `self` is non-empty.
827
-
/// - Postcondition: The length is one less than before
828
-
/// the call. Returns the original last element.
829
-
publicmutatingfuncpopLast() -> T { ... }
826
+
/// Removes and returns the last element.
827
+
///
828
+
/// - Precondition: `self` is non-empty.
829
+
/// - Postcondition: The length is one less than before
830
+
/// the call. Returns the original last element.
831
+
publicmutatingfuncpopLast() -> T { ... }
830
832
```
831
833
832
834
The invariant of this function is the rest of the elements, which are
833
835
unchanged:
834
836
835
837
```swift
836
-
/// Removes and returns the last element.
837
-
///
838
-
/// - Precondition: `self` is non-empty.
839
-
/// - Postcondition: The length is one less than before
840
-
/// the call. Returns the original last element.
841
-
/// - Invariant: the values of the remaining elements.
842
-
publicmutatingfuncpopLast() -> T { ... }
838
+
/// Removes and returns the last element.
839
+
///
840
+
/// - Precondition: `self` is non-empty.
841
+
/// - Postcondition: The length is one less than before
842
+
/// the call. Returns the original last element.
843
+
/// - Invariant: the values of the remaining elements.
844
+
publicmutatingfuncpopLast() -> T { ... }
843
845
```
844
846
845
847
Now, if the postcondition seems a bit glaringly redundant with the
@@ -866,10 +868,10 @@ invariant is also trivially implied. And that is also very commonly
866
868
omitted.
867
869
868
870
```swift
869
-
/// Removes and returns the last element.
870
-
///
871
-
/// - Precondition: `self` is non-empty.
872
-
publicmutatingfuncpopLast() -> T { ... }
871
+
/// Removes and returns the last element.
872
+
///
873
+
/// - Precondition: `self` is non-empty.
874
+
publicmutatingfuncpopLast() -> T { ... }
873
875
```
874
876
875
877
In fact, the precondition is implied by the summary too. You
@@ -887,8 +889,8 @@ you are going to remove the last element, so the original declaration
887
889
should be sufficient:
888
890
889
891
```swift
890
-
/// Removes and returns the last element.
891
-
publicmutatingfuncpopLast() -> T { ... }
892
+
/// Removes and returns the last element.
893
+
publicmutatingfuncpopLast() -> T { ... }
892
894
```
893
895
894
896
In practice, once you are comfortable with this discipline, the
@@ -931,12 +933,14 @@ the elements arranged from least to greatest. The contract gives an
931
933
explicit precondition that isn't implied by the summary: it requires
932
934
that the predicate be a strict weak ordering.
933
935
936
+
<!-- SRP: this section bothers me. "among others" instead of fully spelling out the requirements, using (i, j + 1) which may not exist, and the n^2 comparisons without calling out the O(n^3) complexity or which properties could be practically checked. Also is "stable" the term we want to use? Regular and deterministic are also candidates. I've tried to rewrite this a couple of times, but it just gets too complex and the main point is lost. -->
937
+
934
938
_Some_ precondition on the predicate is needed just to make the result
935
939
a meaningful sort with respect to the predicate. For example, a
936
940
totally unconstrained predicate could return random boolean values,
937
941
and there's no reasonable sense in which the function could be said to
938
942
leave the elements sorted with respect to that. Therefore the
939
-
predicate at least has to be stable. To leave elements meaningfully
943
+
predicate at least has to be deterministic. To leave elements meaningfully
940
944
sorted, the predicate has to be *transitive*: if it is `true` for
941
945
elements (*i*, *j*), it must also be true for elements (*i*, *j*+1).
942
946
A strict weak ordering has both of these properties, among others.
@@ -983,19 +987,19 @@ essential information—but because the statement of effects is tricky,
983
987
this is a case where an example might really help.
984
988
985
989
```swift
986
-
/// Sorts the elements so that `areInIncreasingOrder(self[i+1],
987
-
/// self[i])` is false for each `i` in `0 ..< length - 2`.
988
-
///
989
-
/// var a = [7, 9, 2, 7]
990
-
/// a.sort(areInIncreasingOrder: <)
991
-
/// print(a) // prints [2, 7, 7, 9]
992
-
///
993
-
/// - Precondition: `areInIncreasingOrder` is [a strict weak
0 commit comments