On Sun, Dec 21, 2014 at 9:35 PM, Nick Coghlan
[...] I realised in trying to write this email that I don't currently understand the consequences of not having annotation data available for a module in terms of the ripple effects that may have on what scanners can check - from the end user perspective, I believe that's something I'd need to know, even though I wouldn't necessarily need to know *why* those were the default assumptions for unannotated operations. (I'm curious about the latter from a language *design* perspective, but I think I'd be able to use the feature effectively just by knowing the practical consequences without necessarily understanding the theory)
That's what "Any" is for. If an object's type is "Any", then every operation on it (including getattr) also return "Any", from the checker's POV. You stop the ripples with an explicitly declared type -- e.g. if you pass it to a function as an arg with a type annotation, inside that function the argument is assumed to have the declared type (but the "Any" prevents diagnostics about the call site). In mypy there is also an explicit cast operator (http://mypy.readthedocs.org/en/latest/casts.html), we may add this to the PEP (it's one of the many details left out from the Quip doc that need to be filled in for the actual PEP). The importance of "Any" cannot be overstated -- without it, you either have to declare types everywhere, or you end up with a "everything inherits from everything" situation. The invention of "Any" prevents this (and this is why is-consistent-with cannot be transitive). Read Jeremy Siek's Gradual Typing blog post for more. Also, consider the important difference between Any and object. They are both at the top of the class tree -- but object has *no* operations (well, almost none -- it has repr() and a few others), while Any supports *all* operations (in the sense of "is allowed by the type system/checker"). This places Any also at the *bottom* of the class tree, if you can call it that. (And hence it is more a graph than a tree -- but if you remove Any, what's left is a tree again.) Any's "contagiousness" is somewhat similar to NaN for floats, but it has its limits -- e.g. repr() of Any is still a string, and Any==Any is still a bool. Another similar concept exists IIUC in Objective-C, which has a kind of null object that can be called (sent a message), always returning another null result. (This has occasionally been proposed for Python's None, but I don't think it is appropriate.) But of course, Any only exists in the mind of the static checker -- at runtime, the object has a concrete type. "Any" is just the type checker's way to say "I don't know the type, and I don't care". I bet compilers with good error recovery have some similar concept internally, used to avoid a cascade of errors due to a single typo. -- --Guido van Rossum (python.org/~guido)