Python Sanity Proposal: Type Hinting Solution

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sat Jan 24 07:21:10 CET 2015


Fetchinson . wrote:

> On 1/23/15, Steven D'Aprano <steve+comp.lang.python at pearwood.info> wrote:
[...]
>> Cobra is especially close to Python-like syntax, and supports unit tests
>> as well:
>>
>>
>>     def sqroot(i as int) as float
>>         require
>>            i > 0
>>         ensure
>>            result > 0
>>         tests
>>            assert sqroot(25) == 5.0
>>         body
>>             ...
>>
>> It would be nice to be able to include at least *some* tests right there
>> in the code rather than in a separate file.
> 
> I completely agree. A cobra-style type hinting implementation would
> satisfy everyone who doesn't want to make function signatures noisy
> (including me) and would also satisfy those who advocate for the
> actual feature (type hinting) without worrying much about the
> implementation detail. These two groups are overlapping by the way :)

I don't understand this. Cobra's type-hints are right there in the function
signature, just like Python annotations.


# Cobra
def sqroot(i as int) as float

# Python
def sqroot(i:int)->float:


Cobra's use of "as" clashes with Python. In Python, "as" is used for
name-binding:

import module as name
with open('file') as f
except Exception as e

but apart from that minor difference, they're virtually identical.


> In any case, I'm pretty sure it was said before, but I can't really
> find it anywhere, can someone tell me what the rationale is for
> *function signature* type hinting?

The basic principle is that things which are related should be found
together. The further away they are, the worse.

Bad:
- the parameter name and the type are in different files

Better:
- the parameter name and the type are only a few lines apart

Best:
- the parameter name and type are right next to each other


The closer they are, the easier it is to keep them in sync, and the easier
it is to see the relevant information at a glance. Putting them together
also means that you don't have to repeat the argument name:

int n
def spam(n): ...

versus

def spam(n:int): ...


Those reasons are why decorators have the syntax which they do:

@decorator
def spam(n): 
    do_this()
    do_that()
    do_something_else()


is better than the old way of using decorators:

def spam(n): 
    do_this()
    do_that()
    do_something_else()

spam = decorator(spam)


The decorator is only a single line away from the signature, and you don't
have to repeat the name.


We can see this at work in Pascal. Pascal functions have type declarations
in the signature, and variable declarations between the signature and the
body:


function sqroot(arg: Integer): Real;
  var
    x: Integer;
    y: Real;
    z: Something_Else;
  begin
    do_this(1, 2);
    do_that(3, 4);
    do_something_else(5, 6);
    x := some expression;  { what's the type of x again? }
  end;


The declarations in the signature work very well and are easy to use, but
the "var" section, not so much. Especially in large functions, the place
where you declare the variable and its type, and the place where you first
use it, can be separated by many lines. This makes maintenance and reading
of the code more difficult.

Newer languages like Java let you declare the variable the first time you
use it:

    int x = some expression; 

and you don't have to search very far to find out what sort of thing x is,
it is right there.



-- 
Steven




More information about the Python-list mailing list