On Sun, Sep 23, 2018 at 07:09:37AM +0200, Marko Ristin-Kaufmann wrote: > After the discussion we had on the list and after browsing the internet a > bit, I'm still puzzled why design-by-contract was not more widely adopted > and why so few languages support it. [...]
> *. *After properly reading about design-by-contract and getting deeper into > the topic, there is no rational argument against it and the benefits are > obvious. And still, people just wave their hand and continue without > formalizing the contracts in the code and keep on writing them in the > descriptions. > > * Why is that so? [...] You are asking a question about human psychology but expecting logical, technical answers. I think that's doomed to failure. There is no nice way to say this, because it isn't nice. Programmers and language designers don't use DbC because it is new and different and therefore scary and wrong. Programmers, as a class, are lazy (they even call laziness a virtue), deeply conservative, superstitious, and don't like change. They do what they do because they're used to it, not because it is the technically correct thing to do, unless it is by accident. (Before people's hackles raise too much, I'm speaking in generalities, not about you personally. Of course you are one of the 1% awesomely logical, technically correct programmers who know what you are doing and do it for the right reasons after careful analysis. I'm talking about the other 99%, you know the ones. You probably work with them. You've certainly read their answers on Stackoverflow or The Daily WTF and a million blogs.) They won't use it until there is a critical mass of people using it, and then it will suddenly flip from "that weird shit Eiffel does" to "oh yeah, this is a standard technique that everyone uses, although we don't make a big deal about it". Every innovation in programming goes through this. Whether the innovation goes mainstream or not depends on getting a critical mass, and that is all about psychology and network effects and nothing to do with the merit of the idea. Remember the wars over structured programming? Probably not. In 2018, the idea that people would *seriously argue against writing subroutines* seems insane, but they did. As late as 1999, a former acquaintance of mine was working on a BASIC project for a manager who insisted they use GOTO and GOSUB in preference to subroutines. Testing: the idea that we should have *repeatable automated tests* took a long time to be accepted, and is still resisted by both developers and their managers. What's wrong with just sitting a person down in front of the program and checking for bugs by hand? We still sometimes have to fight for an automated test suite, never mind something like test driven development. ML-based languages have had type inference for decades, and yet people still think of type checking in terms of C and Java declarations. Or they still think in terms of static VERSUS dynamic typing, instead of static PLUS dynamic typing. I could go on, but I think I've made my point. I can give you some technical reasons why I don't use contracts in my own Python code, even though I want to: (1) Many of my programs are too small to bother. If I'm writing a quick script, I don't even write tests. Sometimes "be lazy" is the right answer, when the cost of bugs is small enough and the effort to avoid them is greater. And that's fine. Nobody says that contracts must be mandatory. (2) Python doesn't make it easy to write contracts. None of the solutions I've seen are nice. Ironically, the least worst I've seen is a quick and dirty metaclass solution given by Guido in an essay way back in Python 1.5 days: https://www.python.org/doc/essays/metaclasses/ His solution relies only on a naming convention, no decorators, no lambdas: class C(Eiffel): def method(self, arg): return whatever def method_pre(self, arg): assert arg > 0 def method_post(self, Result, arg): assert Result > arg Still not pretty, but at least we get some block structure instead of a wall of decorators. Syntax matters. Without good syntax that makes it easy to write contracts, it will never be anything but niche. (3) In a sense, *of course I write contracts*. Or at least precondition checks. I just don't call them that, and I embed them in the implementation of the method, and have no way to turn them off. Nearly all of us have done the same, we start with a method like this: class C: def method(self, alist, astring, afloat): # do some work... using nothing but naming conventions and an informal (and often vague) specification in the docstring, and while that is sometimes enough in small projects, the third time we get bitten we start adding defensive checks so we get sensible exceptions: def method(self, alist, astring, afloat): if not isinstance(alist, list): raise TypeError('expected a list') if alist == []: raise ValueError('list must not be empty') # and so on... These are pre-conditions! We just don't call them that. And we can't disable them. They're not easy to extract from the source code and turn into specifications. And so much boilerplate! Let's invent syntax to make it more obvious what is going on: def method(self, alist, astring, afloat): requires: isinstance(alist, list) alist != [] isinstance(astring, str) number_of_vowels(astring) > 0 isinstance(afloat, float) not math.isnan(afloat) implementation: # code goes here Its easy to distinguish the precondition checks from the implementation, easy to ignore the checks if you don't care about them, and easy for an external tool to analyse. What's not to like about contracts? You're already using them, just in an ad hoc, ugly, informal way. And I think that is probably the crux of the matter. Most people are lazy, and don't like having to do things in a systematic manner. For years, programmers resisted writing functions, because unstructured code was easier. We still resist writing documentation, because "its obvious from the source code" is easier. We resist writing even loose specifications, because NOT writing them is easier. We resist writing tests unless the project demands it. We resist running a linter or static checker over our code ("it runs, that means there are no errors"). Until peer pressure and pain makes us do so, then we love them and could not imagine going back to the bad old days before static analysis and linters and unit tests. -- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/