Hi Chris,

It's easy to say that they're boolean expressions. But that's like
saying that unit tests are just a bunch of boolean expressions too.
Why do we have lots of different forms of test, rather than just a big
fat "assert this and this and this and this and this and this"?
Because the key to unit testing is not "boolean expressions", it's a
language that can usefully describe what it is we're testing.
Contracts aren't just boolean expressions - they're a language (or a
mini-language) that lets you define WHAT the contract entails.

Sorry, I misunderstood you. You are probably referring to knowing the terms like "preconditions, postconditions, invariants, strengthening/weakening", right? In that case, yes, I agree, I presuppose that readers are familiar with the concepts of DbC. Otherwise, of course, it makes no sense to use DbC if you assume nobody could actually figure out what it is :).

But that's not the objection that is made too often -- what I really read often is that DbC is not beneficial not because people are not familiar with or can learn DbC, but really because reading boolean expressions is hard. That is what I was referring to in my original message.

> Thanks for this clarification (and the download-yt example)! I actually only had packages-as-libraries in mind, not the executable scripts; my mistake. So, yes, "any Pypi package" should be reformulated to "any library on Pypi" (meant to be used by a wider audience than the developers themselves).
>

Okay. Even with that qualification, though, I still think that not
every library will benefit from this. For example, matplotlib's
plt.show() method guarantees that... a plot will be shown, and the
user will have dismissed it, before it returns. Unless you're inside
Jupyter/iPython, in which case it's different. Or if you're in certain
other environments, in which case it's different again. How do you
define the contract for something that is fundamentally interactive?

You can define very weak contracts. For instance, input() guarantees
that its return value is a string. Great! DbC doing the job of type
annotations. Can you guarantee anything else about that string? Is
there anything else useful that can be spelled easily?

In this case, no, I would not add any formal contracts to the function. Not all contracts can be formulated, and not all contracts are even meaningful. I suppose interactive functions are indeed a case that it is not possible. If any string can be returned, than the contract is empty.

However, contracts can be useful when testing the GUI -- often it is difficult to script the user behavior. What many people do is record a session and re-play it. If there is a bug, fix it. Then re-record. While writing unit tests for GUI is hard since GUI changes rapidly during development and scripting formally the user behavior is tedious, DbC might be an alternative where you specify as much as you can, and then just re-run through the session. This implies, of course, a human tester.

Let me take a look at matplotlib show:
matplotlib.pyplot.show(*args, **kw)[source]

Display a figure. When running in ipython with its pylab mode, display all figures and return to the ipython prompt.

In non-interactive mode, display all figures and block until the figures have been closed; in interactive mode it has no effect unless figures were created prior to a change from non-interactive to interactive mode (not recommended). In that case it displays the figures but does not block.

A single experimental keyword argument, block, may be set to True or False to override the blocking behavior described above.

Here are the contracts as a toy example; (I'm not familiar at all with the internals of matplotlib. I haven't spent much time really parsing and analyzing the docstring -- as I'll write below, it also confuses me since the docstring is not precise enough.)

* If in ipython with its pylab mode, all figures should be displayed.
* If in non-interactive mode, display all figures and block

I'm actually confused with what they mean with:

In non-interactive mode, display all figures and block until the figures have been closed; in interactive mode it has no effect unless figures were created prior to a change from non-interactive to interactive mode (not recommended). In that case it displays the figures but does not block.

A single experimental keyword argument, block, may be set to True or False to override the blocking behavior described above.

If only they spelled that out as a contract :)

"it has no effect": What does not have effect? The call to the function? Or the setting of some parameter? Or the order of the calls?

"unless ...to interactive mode": this would be actually really more readable as a formal contract. It would imply some refactoring to add a property when a figure was created (before or after the change to interactive mode from non-interactive mode). Right now, it's not very clear how you can test that even yourself as a caller. And what does "(not recommended)" mean? Prohibited?

The blocking behavior and the corresponding argument is hard to parse from the description for me. Writing it as a contract would, IMO, be much more readable.

Here is my try at the contracts. Assuming that there is a list of figures and that they have a property "displayed" and that "State.blocked" global variable refers to whether the interface is blocked or not::
@post(lambda: all(figure.displayed for figure in figures)
@post(lambda: not ipython.in_pylab_mode() or not State.blocked)
@post(lambda: not interactive() or State.blocked)
matplotlib.pyplot.show()
 
The function that actually displays the figure could ensure that the "displayed" property of the figure is set and that the window associated with the figure is visible.

Cheers,
Marko


On Wed, 26 Sep 2018 at 09:19, Chris Angelico <rosuav@gmail.com> wrote:
On Wed, Sep 26, 2018 at 5:10 PM Marko Ristin-Kaufmann
<marko.ristin@gmail.com> wrote:
> The original objection was that DbC in general is not beneficial; not that there are lacking tools for it (this probably got lost in the many messages on this thread). If you assume that there are no suitable tools for DbC, then yes, DbC is certainly not beneficial to any project since using it will be clumsy and difficult. It's a chicken-and-egg problem, so we need to assume that there are good tools for DbC in order for it to be beneficial.
>
>> Disagreed. I would most certainly NOT assume that every reader knows
>> any particular syntax for such contracts. However, this is a weaker
>> point.
>
>
> The contracts are written as boolean expressions. While I agree that many programmers have a hard time with boolean expressions and quantifiers, I don't see this as a blocker to DbC. There is no other special syntax for DbC unless we decide to add it to the core language (which I don't see as probable). What I would like to have is a standard library so that inter-library interactions based on contracts are possible and an ecosystem could emerge around it.
>

It's easy to say that they're boolean expressions. But that's like
saying that unit tests are just a bunch of boolean expressions too.
Why do we have lots of different forms of test, rather than just a big
fat "assert this and this and this and this and this and this"?
Because the key to unit testing is not "boolean expressions", it's a
language that can usefully describe what it is we're testing.
Contracts aren't just boolean expressions - they're a language (or a
mini-language) that lets you define WHAT the contract entails.

>> You might argue that a large proportion of PyPI projects will be
>> "library-style" packages, where the main purpose is to export a bunch
>> of functions. But even then, I'm not certain that they'd all benefit
>> from DbC.
>
>
> Thanks for this clarification (and the download-yt example)! I actually only had packages-as-libraries in mind, not the executable scripts; my mistake. So, yes, "any Pypi package" should be reformulated to "any library on Pypi" (meant to be used by a wider audience than the developers themselves).
>

Okay. Even with that qualification, though, I still think that not
every library will benefit from this. For example, matplotlib's
plt.show() method guarantees that... a plot will be shown, and the
user will have dismissed it, before it returns. Unless you're inside
Jupyter/iPython, in which case it's different. Or if you're in certain
other environments, in which case it's different again. How do you
define the contract for something that is fundamentally interactive?

You can define very weak contracts. For instance, input() guarantees
that its return value is a string. Great! DbC doing the job of type
annotations. Can you guarantee anything else about that string? Is
there anything else useful that can be spelled easily?

> I totally agree. The discussion related to DbC in my mind always revolved around these use cases where type annotations are beneficial as well. Thanks for pointing that out and I'd like to apologize for the confusion! For the future discussion, let's focus on these use cases and please do ignore the rest. I'd still say that there is a plethora of libraries published on Pypi (Is there a way to find out the stats?).
>

Ugh.... I would love to say "yes", but I can't. I guess maybe you
could look at a bunch of requirements.txt files and see which things
get dragged in that way? All you'll really get is heuristics at best,
and even that, I don't know how to provide. Sorry.

ChrisA
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/