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
Improves clarity and consistency in 'chapter-2-contracts.md' by revising explanations, updating code examples, and correcting terminology. Adds custom dictionary words to VSCode settings and enables the Katex preprocessor in book.toml for better math rendering.
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
@@ -715,7 +717,7 @@ Now, not every contract is as simple as the ones we've shown so far,
715
717
but simplicity is a goal. In fact, if you can't write a terse,
716
718
simple, but _complete_ contract for a component, there's a good chance
717
719
it's badly designed. A classic example is the C library `realloc`
718
-
function, which does at least three different things—allocate, deallocate, and resize
720
+
function, which does at least three different things—allocate, deallocate, and resize
719
721
dynamic memory—all of which
720
722
need to be described. A better design would have separated these
721
723
functions. So simple contracts are both easy to digest and easy to
@@ -804,10 +806,10 @@ What are the preconditions for removing an element? Obviously, there
804
806
needs to be an element to remove.
805
807
806
808
```swift
807
-
/// Removes and returns the last element.
808
-
///
809
-
/// - Precondition: `self` is non-empty.
810
-
publicmutatingfuncpopLast() -> T { ... }
809
+
/// Removes and returns the last element.
810
+
///
811
+
/// - Precondition: `self` is non-empty.
812
+
publicmutatingfuncpopLast() -> T { ... }
811
813
```
812
814
813
815
A client of this method is considered to have a bug unless
@@ -820,25 +822,25 @@ bug. The bug could be in the documentation of course, *which is a
820
822
part of the method*.
821
823
822
824
```swift
823
-
/// Removes and returns the last element.
824
-
///
825
-
/// - Precondition: `self` is non-empty.
826
-
/// - Postcondition: The length is one less than before
827
-
/// the call. Returns the original last element.
828
-
publicmutatingfuncpopLast() -> T { ... }
825
+
/// Removes and returns the last element.
826
+
///
827
+
/// - Precondition: `self` is non-empty.
828
+
/// - Postcondition: The length is one less than before
829
+
/// the call. Returns the original last element.
830
+
publicmutatingfuncpopLast() -> T { ... }
829
831
```
830
832
831
833
The invariant of this function is the rest of the elements, which are
832
834
unchanged:
833
835
834
836
```swift
835
-
/// Removes and returns the last element.
836
-
///
837
-
/// - Precondition: `self` is non-empty.
838
-
/// - Postcondition: The length is one less than before
839
-
/// the call. Returns the original last element.
840
-
/// - Invariant: the values of the remaining elements.
841
-
publicmutatingfuncpopLast() -> T { ... }
837
+
/// Removes and returns the last element.
838
+
///
839
+
/// - Precondition: `self` is non-empty.
840
+
/// - Postcondition: The length is one less than before
841
+
/// the call. Returns the original last element.
842
+
/// - Invariant: the values of the remaining elements.
843
+
publicmutatingfuncpopLast() -> T { ... }
842
844
```
843
845
844
846
Now, if the postcondition seems a bit glaringly redundant with the
@@ -865,10 +867,10 @@ invariant is also trivially implied. And that is also very commonly
865
867
omitted.
866
868
867
869
```swift
868
-
/// Removes and returns the last element.
869
-
///
870
-
/// - Precondition: `self` is non-empty.
871
-
publicmutatingfuncpopLast() -> T { ... }
870
+
/// Removes and returns the last element.
871
+
///
872
+
/// - Precondition: `self` is non-empty.
873
+
publicmutatingfuncpopLast() -> T { ... }
872
874
```
873
875
874
876
In fact, the precondition is implied by the summary too. You
@@ -886,8 +888,8 @@ you are going to remove the last element, so the original declaration
886
888
should be sufficient:
887
889
888
890
```swift
889
-
/// Removes and returns the last element.
890
-
publicmutatingfuncpopLast() -> T { ... }
891
+
/// Removes and returns the last element.
892
+
publicmutatingfuncpopLast() -> T { ... }
891
893
```
892
894
893
895
In practice, once you are comfortable with this discipline, the
@@ -929,12 +931,14 @@ the elements arranged from least to greatest. The contract gives an
929
931
explicit precondition that isn't implied by the summary: it requires
930
932
that the predicate be a strict weak ordering.
931
933
934
+
<!-- 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. -->
935
+
932
936
_Some_ precondition on the predicate is needed just to make the result
933
937
a meaningful sort with respect to the predicate. For example, a
934
938
totally unconstrained predicate could return random boolean values,
935
939
and there's no reasonable sense in which the function could be said to
936
940
leave the elements sorted with respect to that. Therefore the
937
-
predicate at least has to be stable. To leave elements meaningfully
941
+
predicate at least has to be deterministic. To leave elements meaningfully
938
942
sorted, the predicate has to be *transitive*: if it is `true` for
939
943
elements (*i*, *j*), it must also be true for elements (*i*, *j*+1).
940
944
A strict weak ordering has both of these properties, among others.
@@ -981,19 +985,19 @@ essential information—but because the statement of effects is tricky,
981
985
this is a case where an example might really help.
982
986
983
987
```swift
984
-
/// Sorts the elements so that `areInIncreasingOrder(self[i+1],
985
-
/// self[i])` is false for each `i` in `0 ..< length - 2`.
986
-
///
987
-
/// var a = [7, 9, 2, 7]
988
-
/// a.sort(areInIncreasingOrder: <)
989
-
/// print(a) // prints [2, 7, 7, 9]
990
-
///
991
-
/// - Precondition: `areInIncreasingOrder` is [a strict weak
0 commit comments