On Sun, Sep 30, 2018 at 2:27 PM Steven D'Aprano email@example.com wrote:
On Sun, Sep 30, 2018 at 12:17:25PM +1000, Chris Angelico wrote:
On Sun, Sep 30, 2018 at 11:54 AM Steven D'Aprano firstname.lastname@example.org wrote:
This discussion is for those of us who would like to include DbC in our projects but don't like existing solutions. C++ being designed with a shovel is not relevant.
(Except in the sense that we should always be careful about piling on feature upon feature into Python.)
And as such, I do not want to see dedicated syntax for no purpose other than contracts.
That's a reasonable objection, except that contracts are a BIG purpose.
I think that's a matter of debate, but sure, let's accept that contracts are big.
Would you object to dedicated syntax for object oriented programming? (Classes and methods.) I hope not. Imagine if OOP in Python was limited to an API like this:
TBH I don't think contracts are nearly as big a tool as classes are.
MyClass = type(name="MyClass", parent=object) MyClass.add_method(__init__=lambda self, arg: setattr(self, "arg", arg)) MyClass.add_method(__str__=lambda self: "MyClass(%r)" % (self.arg,)) MyClass.add_method(spam=lambda self, x, y: (self.arg + x)/y) MyClass.add_method(eggs=lambda self, x, y: self.arg*x - y) MyClass.add_member(cheese='Cheddar') MyClass.add_member(aardvark=None)
I know that adding syntax is a big step, and should be considered a last resort for when a library or even a built-in won't work. But adding contracts isn't a small benefit. Its not a magic bullet, nobody says that, but I would say that contracts as a feature is *much* bigger and more important than (say) docstrings, and we have dedicated syntax for docstrings.
Hmm, what we really have is just string literals, plus a bit of magic that says "if the first thing in a function/class is a string literal, we save it". But sure.
What I'm interested in is (a) whether something can and should be added to the stdlib, and (b) whether some specific (and probably small) aspect of it could benefit from language support. As a parallel example, consider type hints. The language has ZERO support for special syntax for a language of types.
That's a weird use of the word "ZERO" :-)
def spam(x: int) -> float: y: int
I count three special syntax forms for a language of types:
- parameter type hints;
- return type hints;
- variable type hints.
(Yes, I'm aware that *technically* we can put anything we like in the hints, they don't have to be used as type hints, but Guido has made it clear that such uses are definitely of second-rate importance and only grudgingly supported.)
That's exactly my point though. The syntax is for "attach this thing to that function". There is no syntax for a language of types, a type algebra syntax. We don't have a syntax that says "tuple containing int, two strings, and float". The syntactic support is the very smallest it can be - and, as you say, it's not actually restricted to type hints at all. If I'm reading the dates correctly, annotations were completely non-specific from 2006 (PEP 3107) until 2014 (PEP 484).
So what is the smallest piece of syntactic support that would enable decent DbC? And "none at all" is a valid response, although I think in this case it's incorrect.
What you have is simple, straight-forward names like "List", and the normal behaviours that we already can do such as subscripting. There is language support, however, for attaching expressions to functions and their arguments.
Your point is taken that there is no separate syntax for referring to the types *themselves*, but then there's no need for such. int is int, whether you refer to the class "int" or the static type "int".
The static type "int" is one of the simplest possible type declarations. There are much more complicated options.
At the moment, I'm seeing decorator-based contracts as a clunky version of unit tests.
Contracts are not unit tests.
Contracts and unit tests are complementary, and overlap somewhat, but they are the same. Unit tests only test the canned values you write in you tests. Contracts test real data you pass to your application.
And yet all the examples I've seen have just been poor substitutes for unit tests. Can we get some examples that actually do a better job of selling contracts?