Guido van Rossum: The Theory of Type Hinting for Python 3.5
quip.com
quip.com
a. Better quality and maybe programmer productivity through static checking?
b. Better performance thanks to enforced type letting the compiler do a better job?
c. Something else?
Thanks!
Further down these are cast into more native types, and then walk out into public API's.
And there once more you need to do somewhat strict data-validation.
For these cases, typing rules and annotations will be a boon.
And do note, that there is more common a case of human error or refactoring error that causes problem. Someone sending in a default [] array in an object, and somewhere else it's an expected Boolean.
That matches well what I had in mind with my "a." suggestion.
Now, why cannot we expect "b. Better performance thanks to enforced type letting the compiler do a better job"? Are the semantics of what's being discuss insufficient to provide such benefits? I'm asking that question because I read often that dynamic typing is the #1 performance hit of Python (e.g. [1]). Now that this PEP lets developers provide and enforce type, what's still in the way of reaching the level of performance of statically-typed languages?
[1] Alex Gaynor - Fast Python, Slow Python: https://www.youtube.com/watch?v=7eeEf_rAJds
You can use PEP3107 annotations today and automatically validate them using a decorator to eliminate boilerplate code like that. They don't have to be something to look forward to tomorrow.
A (relatively) high performance runtime annotation type checker.
from ensure import ensure_annotations
@ensure_annotations
def f(x: int, y: float) -> float:
return x+y[1] http://www.artima.com/weblogs/viewpost.jsp?thread=85551 [2] https://www.python.org/dev/peps/pep-3107/ [3] https://mail.python.org/pipermail/python-ideas/2014-August/0... [4] https://mail.python.org/pipermail/python-ideas/2014-August/0... [5] http://www.mypy-lang.org/
That seems odd and I'm not sure I understand the benefits of it. I get that it only applies to things typed as Any (and presumably like with Object, typing as Any is quite rare to want to do - especially with support for union types) but is there an example where you'd want this and the c#/java subclassing would be limiting?
The key is that we're talking about _Gradual_ Typing: it's not that "you never want to type things as Any" (if you always used a dynamically typed language like Python you probably never cared too much), or "subclassing is too limiting" (if anything, imho is not limiting enough).
The point is offering a tool to people who might benefit from some static checks, to avoid stumbling into problems at runtime. Those people have a HUGE codebase with dynamic types (basically, everything is already Any) and you need to _Gradual_ly specify and move over parts of the code to be correctly typed.
as keosak writes, if you want a proper explanation of theory behind it, just read Jsiek's blog posts
http://wphomes.soic.indiana.edu/jsiek/what-is-gradual-typing...
http://wphomes.soic.indiana.edu/jsiek/files/2014/03/retic-py...
http://ecee.colorado.edu/~siek/pubs/pubs/2006/siek06:_gradua...
(Of course, the "functional languages" bit isn't critical here --- the paper defines gradual typing for a lambda calculus, which is the usual vehicle for explaining type systems.)
Basically, you want to partition the program into parts with and without known types. If you know the types, they must be equal. If a type is unknown, it is "equal" (consistent) with anything. The conclusion of the paper is that you need a different, symmetric notion of consistency, that is different from subtyping.
Making Any the root of the type hierarchy doesn't work. You explicitly want to allow things like implicitly passing an Any as an argument where, say, an Int is expected, which would be a down-cast. You don't want to allow implicit down-casts all over the place, because that causes the type hierarchy to collapse --- if you allow implicit down casts, you can always up cast to Any, and then immediately down cast to any other type.
The three rules to apply to quite directly to standard OO C:
1. If t1 is a subclass of t2, t1 is also consistent with t2. (But not the other way around.)
Using the standard OO C method of subclassing structs, ie.
struct t2 {...}; struct t1 {struct t2 t2; ...};
This is obviously true and the normal subclass relationship, though the C syntax to use this is a bit awkward:
t2_method(&t1->t2, arg1, arg2);
2. Any is consistent with every type. (But Any is not a subclass of every type.)
In C the Any type is void. Using the class definitions above this is entirely valid and produces no warnings:
void v;
struct t1 = v;
So if you have an object of void type you can use it wherever you might require a stricter type. If you pass in something which is not consistent you'll get a runtime error (usually a segfault in the case of C).
3. Every type is a subclass of Any. (Which also makes every type consistent with Any, via rule 1.)
This just says that you can do the following without getting any warnings: struct t1 t1; void v = t1;
And this works quite well in C.
The extension to the C type system, beyond the type inference, is to make these rules recursive, especially in function types. For example, this produces a warning under GCC, though it will compile and run fine:
void f(int* func(int b, int c)) {}
void* g(void b, void c) {return b;}
f(g);
That might just be a limitation of GCC's type checking though.
I think this is quite a good direction to take. C's type system has proven sufficiently powerful over the decades to build large systems and at the same time is trivially bypassed when you paint yourself into a typed corner or you want flexibility strict static type checking finds cumbersome to provide.
All function pointer types are convertible to any other function pointer type, and since 'func' is never called as the wrong type there is nothing wrong with your example. It's not a limitation of GCC, it's the intended behavior according to the standard.
If I pass "void g(int a)" to "f(int func(struct foo f))" the standard says everything is fine, but it's almost certainly going to blow up. GCC helpfully notifies me of this.
The only case where this warning isn't what I want is where some of the pointer types have been replaced with void. That is, passing "void g(void a)" to "f(int func(struct foo f))" should work just like "struct foo f = (void *a);" and output no warning.
The proposal from the article supports this case and other similar recursive cases.
I assume you mean a pointer to void here. You explicitly cannot have a void value, as void is an incomplete type (it has no size).
$ gcc -x c - <<<"int main(void) {void a; return 0;}"
<stdin>: In function ‘main’:
<stdin>:1:22: error: variable or field ‘a’ declared void
You can have a pointer to void, which works as you describe, though you must also use a pointer to the struct. void *v;
struct t1 *b = v;Two things in the pragmatic side seem hairy though - type declarations in types and `Undefined`.
However, it does, somehow, with type alias syntax as proposed in this article, make python3 looks even similar to Go.
def comma_sep(items):
return ','.join(map(str, items))
requires two things:- items be iterable
- each item have a __str__ method
This is a TEXT DOCUMENT, right?
(I don't want a downloadable PDF also.)
Also, type inference doesn't mix very well with dynamic types ("dynamic is viral", or similar things, I've briefly skimmed this post and I think it makes the point: http://ericlippert.com/2012/11/09/dynamic-contagion-part-two... )
Type inference infers a static type for every expression in a program. Hindley-Milner is such a type inference algorithm.
Gradual typing allows you to partition your program into typed and untyped parts. The untyped parts need not even be typable under your type system.
Type inference and gradual typing can be combined. See: Siek and Vachharajani: Gradual typing with unification-based inference http://dl.acm.org/citation.cfm?id=1408688
The idea of that paper is that you want to do type inference for the statically typed part of your program in a gradual type system.