<html>
  <head>
    <meta content="text/html; charset=windows-1252"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    On 18.09.2015 05:00, Steven D'Aprano wrote:<br>
    <blockquote cite="mid:20150918030024.GN31152@ando.pearwood.info"
      type="cite">
      <pre wrap="">I don't think they are all or nothing. I think it is possible to have 
incomplete documentation and partial test coverage -- it isn't like you 
go from "no documentation at all and zero tests" to "fully documented 
and 100% test coverage" in a single step.</pre>
    </blockquote>
    <br>
    This was a misunderstanding. The "all or nothing" wasn't about "test
    everything or don't do it at all". It was about the robustness of
    future benefits you gain from it. Either you have a test or you
    don't.<br>
    <br>
    With type annotations you have 40% or 60% <b>depending</b> on the
    quality of the tool you use. It's fuzzy. I don't like to build stuff
    on jello. Just my personal feeling here.<br>
    <br>
    <blockquote cite="mid:20150918030024.GN31152@ando.pearwood.info"
      type="cite">
      <pre wrap="">That's one use-case for them. Another use-case is as documentation:

def agm(x:float, y:float)->float:
    """Return the arithmetic-geometric mean of x and y."""

versus

def agm(x, y):
    """Return the arithmetic-geometric mean of x and y.

    Args:
        x (float): A number.
        y (float): A number.

    Returns:
        float: The agm of the two numbers.
    """
</pre>
    </blockquote>
    <br>
    The type annotation explains nothing. The short doc-string
    "arithmetic-geometric mean" explains everything (or prepare you to
    google it). So, I would prefer this one:<br>
    <pre wrap="">def agm(x, y):
    """Return the arithmetic-geometric mean of x and y."""
</pre>
    <br>
    <blockquote cite="mid:20150918030024.GN31152@ando.pearwood.info"
      type="cite">
      <blockquote type="cite">
        <pre wrap="">So, I have difficulties to 
infer which parameters actually would benefit from annotating. 
</pre>
      </blockquote>
      <pre wrap="">
The simplest process may be something like this:

- run the type-checker in a mode where it warns about variables 
  with unknown types;
- add just enough annotations so that the warnings go away.

This is, in part, a matter of the quality of your tools. A good type 
checker should be able to tell you where it can, or can't, infer a type.
</pre>
    </blockquote>
    <br>
    You see? Depending on who runs which tools, type annotations need to
    be added which are redundant for one tool and not for another and
    vice versa. (Yes, we allow that because we grant the liberty to our
    devs to use the tools they perform best with.)<br>
    <br>
    Coverage, on the other hand, is strict. Either you traverse that
    line of code or you don't (assuming no bugs in the coverage tools).<br>
    <br>
    <blockquote cite="mid:20150918030024.GN31152@ando.pearwood.info"
      type="cite">
      <blockquote type="cite">
        <pre wrap="">I am 
either doing redundant work (because the typechecker is already very 
well aware of the type) or I actually insert explicit knowledge (which 
might become redundant in case typecheckers actually become better).
</pre>
      </blockquote>
      <pre wrap="">
You make it sound like, alone out of everything else in Python 
programming, once a type annotation is added to a function it is carved 
in stone forever, never to be removed or changed :-)
</pre>
    </blockquote>
    <br>
    Let me reformulate my point: it's not about setting things in stone.
    It's about having more to read/process mentally. You might think,
    'nah, he's exaggerating; it's just one tiny little ": int" more here
    and there', but these things build up slowly over time, due to
    missing clear guidelines (see the fuzziness I described above). Devs
    will simply add them just everywhere just to make sure OR ignore the
    whole concept completely.<br>
    <br>
    It's simply not good enough. :(<br>
    <br>
    <br>
    Nevertheless, I like the protocol idea more as it introduces actual
    names to be exposed by IDEs without any work from the devs. That's
    great!<br>
    <br>
    <br>
    You might further think, 'you're so lazy, Sven. First, you don't
    want to help the type checker but you still want to use it?' Yes, I
    am lazy! And I already benefit from it when using PyCharm. It might
    not be perfect but it still amazes me again and again what it can
    infer without any type annotations present.<br>
    <br>
    <blockquote cite="mid:20150918030024.GN31152@ando.pearwood.info"
      type="cite">
      <pre wrap="">def spam(n=3):
    return "spam"*n

A decent type-checker should be able to infer that n is an int. What if 
you add a type annotation?

def spam(n:int=3):
    return "spam"*n
</pre>
    </blockquote>
    <br>
    It's nothing seriously wrong with it (except what I described
    above). However, these examples (this one in particular) are/should
    not be real-world code. The function name is not helpful, the
    parameter name is not helpful, the functionality is a toy.<br>
    <br>
    My observation so far:<br>
    <br>
    1) Type checking illustrates its point well when using academic
    examples, such as the tuples-of-tuples-of-tuples-of-ints I described
    somewhere else on this thread or unreasonably short toy examples.<br>
    <br>
    (This might be domain specific; I can witness it for business
    applications and web applications none of which actually need to
    solve hard problems admittedly.)<br>
    <br>
    2) Just using constant and sane types like a class, lists of
    single-class instances and dicts of single-class instances for a
    single variable enables you to assign a proper name to it and forces
    you to design a reasonable architecture of your functionality by
    keeping the level of nesting at 0 or 1 and split out pieces into
    separate code blocks.<br>
    <br>
    Best,<br>
    Sven<br>
  </body>
</html>