[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