> Functions that only mutate instances of well-encapsulated types through their public APIs. That includes nearly all functions that are not methods.
> Methods of well-encapsulated types that only mutate non-member instances of other well-encapsulated types through their public APIs.
This is incorrect! Consider the following function (in psuedocode):
func insert(id: Int, object: ThingInSpace) {
ids.update(id);
var location = get_location(object);
locations[id] = location;
var metadata = compute_metadata(object);
metadatas[id] = metadata;
}
If compute_metadata or get_location throw, then even though we're only modifying dictionaries or sets through their public APIs, we can still invalidate an invariant that affects our program correctness. This function is safe if and only if get_location and compute_metadata cannot throw. If they can throw, this should be rewritten as func insert(id: Int, object: ThingInSpace) {
var location = get_location(object);
var metadata = compute_metadata(object);
ids.update(id);
locations[id] = location;
metadatas[id] = metadata;
}
Adding "try" to the original function makes it clear that this function can destroy the invariant of ids, locations, and metadatas, and it needs to be rearranged to be the second function in order for the code to be correct.