[Python-ideas] Why is design-by-contracts not widely adopted?

Terry Reedy tjreedy at udel.edu
Mon Oct 8 15:34:51 EDT 2018


On 10/8/2018 10:26 AM, Steven D'Aprano wrote:
> On Sun, Oct 07, 2018 at 04:24:58PM -0400, Terry Reedy wrote:

>> https://www.win.tue.nl/~wstomv/edu/2ip30/references/design-by-contract/index.html
>>
>> defines contracts as "precise (legally unambiguous) specifications" (5.2
>> Business Contracting/Sub-contracting Metaphor)
> 
> You are quoting that out of context. The full context says (emphasis
> added):
> 
>      IN THE BUSINESS WORLD, contracts are precise (legally unambiguous)
>      specifications that define the obligations and benefits of the
>      (usually two) parties involved.

This is silly.  Every quote that is not complete is literally 'out of 
context'.  However, 'quoting out of context', in the colloquial sense, 
means selectively quoting so as to distort the original meaning, whereas 
I attempted to focus on the core meaning I was about to discuss.

Marko asked an honest question about why things obvious to him are not 
obvious to others.  I attempted to give an honest answer.  If my answer 
suggested that I have not undertstood Marko properly, as is likely, he 
can use it as a hint as to how communicate his position better.

 >> I said above that functions may be specified by
 >> process rather than result.
 >
 > Fine. What of it? Can you describe what the function does?
 >
 > "It sorts the list in place."
 >
 > "It deletes the given record from the database."

 > These are all post-conditions.

No they are not.  They are descriptions of the process.  Additional 
mental work is required to turn them into formal descriptions of the 
result that can be coded.  Marko appears to claim that such coded formal 
descriptions are easier to read and understand than the short English 
description.  I disagree.  It is therefore not obvious to me that the 
extra work is worthwhile.

>> def append_first(seq):
>>      "Append seq[0] to seq."
> [...]

The snipped body (revised to omit irrelevant 'return')
     seq.append(seq[0])

>> But with duck-typing, no post condition is possible.
> 
> That's incorrect.
> 
> def append_first(seq):
>      require:
>          len(seq) > 0

seq does not neccessarily have a __len__ method

>          hasattr(seq, "append")

The actual precondition is that seq[0] be in the domain of seq.append. 
The only absolutely sure way to test this is to run the code body.  Or 
one could run seq[0] and check it against the preconditions, if formally 
specified, of seq.append.

>      ensure:
>          len(seq) == len(OLD.seq) + 1
>          seq[0] == seq[-1]

Not even all sequences implement negative indexing.

This is true for lists, as I said, but not for every object the meets 
the preconditions.  As others have said, duck typing means that we don't 
know what unexpected things methods of user-defined classes might do.

class Unexpected():
     def __init__(self, first):
         self.first = first
     def __getitem__(self, key):
         if key == 0:
             return self.first
         else:
             raise ValueError(f'key {key} does not equal 0')
     def append(self, item):
         if isinstance(item, int):
             self.last = item
         else:
             raise TypeError(f'item {item} is not an int')

def append_first(seq):
     seq.append(seq[0])

x = Unexpected(42)
append_first(x)
print(x.first, x.last)
# 42 42

A more realistic example:

def add(a, b): return a + b

The simplified precondition is that a.__add__ exists and applies to b or 
that b.__radd__ exists and applies to a.  I see no point in formally 
specifying this as part of 'def add' as it is part of the language 
definition.  It is not just laziness that makes me averse to such 
redundancy.

Even ignoring user classes, a *useful* post-condition that applies to 
both numbers and sequences is hard to write.  I believe + is 
distributive for both, so that a + (b + b) = (a + b) + b, but

-- 
Terry Jan Reedy



More information about the Python-ideas mailing list