Hacker News new | past | comments | ask | show | jobs | submit login

That's true of any type system.



No it isn't. In a static type system, you're not relying on annotations because the types are all known statically. But in a dynamic type system, you can have regions of untyped code where the best your third party static analyzer can do is slap "Any" on them and you watch as the precision of your type checking erodes to nothing.


I don't know if mypy allows you to mock the API but at least flow does for javascript. If a library is untyped you get a file with all the exported functions and you can quickly set the types for the functions that you interface with. We have been using this for a couple of years and it's a godsend. There's a community project to share these external library definitions https://github.com/flow-typed/flow-typed


That's a neat feature!


You can do the same in mypy by generating a stub file: https://mypy.readthedocs.io/en/stable/stubs.html


!

I had no idea. Super cool — thanks for letting me know!


What is

`public static void main(string args)` but an "annotation"?

You can stick "untyped" code in any language. You just have to annotate things with `Object` and cast them.


An annotation is when you write some information and it doesn't directly impact the evaluation of that which it annotates. It's a "note", essentially. Type annotations in Python do not actually affect any aspect of runtime and are ignored during evaluation. This is why you need a third-party tool to look at them. You can actually stick any arbitrary code you like after that colon — it's all ignored just the same.

What you're referring to are called type ascriptions, and they are often seen in what are sometimes called manifest type systems, where you must explicitly write out your types within a static type system. C and Java are common examples of this. These are not annotations because they have real meaning (either used for declaration or for type conversion).

Additionally, there are plenty of statically typed languages which do not require ascription. These languages feature type inference, which is a process that allows the programmer to leave the deduction of the actual types to the compiler. Haskell, Swift, OCaml, Scala, and I think now C++ to some degree (with `auto`) allow for this (to name but a few).


> Type annotations in Python do not actually affect any aspect of runtime and are ignored during evaluation.

This is equally true in Java. Types are erased at runtime. They don't exist in the bytecode.

Just because the type checking and evaluation are done using one tool, "javac" doesn't really mean anything.

Again, what's the difference between a bash script that runs mypy prior to invoking python and a bash script that compiles and runs a java program, from the perspective of type safety, assuming you know nothing about the two programs?

There is no difference. Neither provides any more guarantees than the other. Put another way, by enforcing use of mypy prior to invocation, you convert the annotation to an ascription.

A deeper analysis would suggest that "ascriptions" are actually the things you use to disambiguate when type inference is not powerful enough (https://docs.scala-lang.org/style/types.html).

Scala, for example, calls its type declarations "annotations", except when they are needed to disambiguate, when they are "ascriptions". So under the scala definition, python's annotations are exactly the same as scalas as long as they are used for type checking, which, if you use mypy, they are.


> This is equally true in Java. Types are erased at runtime. They don't exist in the bytecode.

For the record this isn't true, as anyone who's seen a ClassCastException knows. A subset of type information is erased under limited circumstances, but a lot of type information is still present in the bytecode.

> Just because the type checking and evaluation are done using one tool, "javac" doesn't really mean anything.

What means something is that the types are part of the language specification and, even more importantly, part of the consensus of the language community.

In particular, if you depend on a Java library, you can be extremely confident that the type information of that Java library is actually correct, because you can be extremely confident that the library was built in a way that involved checking the types. This is simply not the case for Python; it's very normal to build Python without running mypy. It's like arguing that C is a safe language because you can run Valgrind on your C code: yes, that external tool exists, but it's not part of the language by default, and so you can't rely on the rest of the ecosystem to have used it.

> A deeper analysis would suggest that "ascriptions" are actually the things you use to disambiguate when type inference is not powerful enough (https://docs.scala-lang.org/style/types.html).

> Scala, for example, calls its type declarations "annotations", except when they are needed to disambiguate, when they are "ascriptions". So under the scala definition, python's annotations are exactly the same as scalas as long as they are used for type checking, which, if you use mypy, they are.

The distinction is a lot more general: type ascriptions change the behaviour of the code while type annotations do not. So a system that only has annotations and not ascriptions is significantly more limited than a full type system that has ascriptions as well, at least in practice - in theory we can do perfect type inference (e.g. H-M), but that ignores some edge cases that are important for real-world programming e.g. handling literals in the source code.


> part of the consensus of the language community.

Indeed, so the only difference is that you trust one languages annotations more than the others. Which is what I said.

> The distinction is a lot more general: type ascriptions change the behaviour of the code while type annotations do not.

Well no, ascriptions don't change behavior, they remove ambiguity when systems aren't able to infer things on their own. Literals aren't the issue for H-M. They're relatively easy. H-M has trouble with inheritance and heavily polymorphic code.


> Indeed, so the only difference is that you trust one languages annotations more than the others. Which is what I said.

Eh, that's like saying there's no difference between breaking the law and not breaking the law, someone might kidnap you and lock you up either way. It's true in a sense, but it's more misleading than helpful.

> Well no, ascriptions don't change behavior, they remove ambiguity when systems aren't able to infer things on their own.

You're trying to gloss over everything as "removing ambiguity", but by that logic any change to a program is just "removing ambiguity". In general there are other possible ascriptions for a given term that would form valid programs, and in general those programs could have arbitrarily different behaviour from the program in question.


> You're trying to gloss over everything as "removing ambiguity", but by that logic any change to a program is just "removing ambiguity". In general there are other possible ascriptions for a given term that would form valid programs, and in general those programs could have arbitrarily different behaviour from the program in question.

I'm not, for what it's worth. There are cases where you could argue that type annotations are just removing "ambiguity" (the type system is successfully inferring, you place an annotation that agrees with the inference, or a stricter annotation that still checks successfully). I can see how this is an ambiguity, but it isn't what I meant.

But scala's definition of ascriptions are for places where the type system can't figure something out, and needs extra information to successfully check. More powerful type inference might address these issues (most common languages don't use H-M). Ambiguity was perhaps the wrong word to use. "aren't able to infer things on their own" is maybe the more important bit.

Type ascriptions don't "change" behavior, because there wasn't valid behavior without them. The type system failed to check (but again, different inference algorithms might be able to get around this)


> Type ascriptions don't "change" behavior, because there wasn't valid behavior without them. The type system failed to check (but again, different inference algorithms might be able to get around this)

No, they exist to change behaviour, that's the whole point of that terminology. The example they give is passing a sequence to a vararg method: if you have a generic method that takes a vararg (generic) parameter, then calling that method with multiple parameters from a sequence, or a single parameter that is a sequence, are both valid but have different behaviours.


I totally agree with everything you've said in this thread. The annotation/ascription distinction comes down to whether the use of the thing changes anything in the result. (Just adding a voice of support in this discussion, for what little it's worth.)


> This is equally true in Java. Types are erased at runtime. They don't exist in the bytecode.

I work with bytecode on a daily basis (I develop a commercial Java obfuscator) and this is untrue. You may be thinking of generic type parameter erasure, but even then special interface methods are generated by the compiler so that no lowering to java/lang/Object happens too soon.

In non-generic code, the types remain and are strongly enforced. Methods have descriptors that restrict parameters to certain types (you can get a VerifyError upon loading the class if you've generated code with a type confusion), there is a 'checkcast' instruction to ensure that a stack values is a certain type, and a ClassCastException is thrown if a cast is impossible.


Alright, we've got two different discussions going on here. I'll address them separately.

---

As far as "annotation" vs "ascription", you've actually just reinforced what I said.

> This is equally true in Java. Types are erased at runtime. They don't exist in the bytecode.

Types are erased at compile time, which means that the type-checking analysis has completed. Many compilers erase types after doing type checking to improve runtime performance. That doesn't mean the types don't do anything. They're used for guiding the type-checking by the compiler.

This is in contrast to Python, which does literally nothing with the stuff you throw in the annotations. MyPy doesn't play a role in this specific aspect of the discussion because we're talking about language implementations, and in Python the type annotations are just artifacts in the code that get ignored during compilation and evaluation.

This is the distinction between ascriptions and annotations, which you actually corroborated here:

> Scala, for example, calls its type declarations "annotations", except when they are needed to disambiguate, when they are "ascriptions".

All of the type stuff in Python is handled via annotations because the language proper does not make use of them whatsoever. They're just decoration, really.

In contrast, Java uses all the types to perform type-checking, making them ascriptions.

In languages with lots of type inference, like Scala, you can supply type information. Sometimes these are ascriptions (which either help the inference algorithm or are used for casting), and sometimes they are just artifacts to help the programmer (in which case they can be regarded as annotations, which I didn't address previously).

But the key component in all of this is that we are only looking at the languages proper, not the suite of third-party tools available to those languages, because this is what dictates the correct terminology. In Python the language, types are just annotations which are completely ignored, and this is not the case in statically typed languages.

---

Which brings us to the second point: your original argument.

This thread started with somebody asking whether static typing will ever be implemented in Python proper, to which somebody said there's no reason for that when we can use MyPy and disable support for `Any` types. The response to this was that it would require all third-party libraries to be fully and correctly annotated, where you came in and claimed "that's true in any type system."

The reason I disagreed with this is because in statically typed languages you're not relying on annotations for anything. Keep in mind that we've established that annotations are specifically artifacts in the code that the language itself doesn't care about, because when the language does care they are called ascriptions. Annotations are supplied by the programmer by desire, not by necessity of the language proper.

You can release a fully-functioning Python library with absolutely zero type annotations, and it will run exactly the same as if you annotated everything. This is because type annotations don't do anything in Python; to gain any use from them requires using third-party tools, like MyPy.

But you cannot release a Java library that doesn't have all its types specified in some manner. Because the Java language requires the types to be present, meaning they aren't annotations like they are in Python.

I guess really what I'm trying to get at is that your response where you said "That's true of any type system" was completely missing the point. The point being made by the person you responded to was about how in a Python ecosystem, simply using MyPy and disabling the `Any` type requires all third-party libraries to be fully and correctly annotated — something which is not usually required in Python, so it's an extra burden on the libraries' developers. This is entirely distinct behavior from any statically typed language because in those languages, you can't just leave out the types and call it a day.


> In contrast, Java uses all the types to perform type-checking, making them ascriptions.

Javac uses them to perform type checking, prior to runtime. Exactly the same way that mypy uses them to perform type checking prior to runtime.

> The reason I disagreed with this is because in statically typed languages you're not relying on annotations for anything. Keep in mind that we've established that annotations are specifically artifacts in the code that the language itself doesn't care about, because when the language does care they are called ascriptions. Annotations are supplied by the programmer by desire, not by necessity of the language proper.

But Scala's annotations are used for type checking. Much as python's are if you enable mypy.

> But you cannot release a Java library that doesn't have all its types specified in some manner. Because the Java language requires the types to be present, meaning they aren't annotations like they are in Python.

Of course you can. You can write a functional java library that specifies that all functions take `Object`. Your argument comes down to that syntactically, java requires you stick something in the place-where-type-declarations-go, while python does not.

This ultimately doesn't have any impact on the strictness of the type checking done, because you can stick useless annotations (Any, Object) in the syntactic spot in either case. The semantics are the same.

Ultimately, your argument comes down to "I trust that more third party libraries will have useful types in Java than in python", but that isn't implicit to the type system, it's because the type ecosystem has been around longer.

Which, like, sure. But to say that python doesn't have static typing is silly. Nearly all the python I write is statically typed.

> In Python the language, types are just annotations which are completely ignored, and this is not the case in statically typed languages.

This is a uselessly semantic argument. Much as if you choose to not typecheck your python, you can choose to write essentially untyped java using Object and casts. Are you really certain that no library you depend on does that, or uses reflection to access dynamic and non-statically-verifiable features?

The safety you get from any static analysis tool, whether a type system or a linter or whatever, relies on your dependencies not trying to subvert the tool (or doing so correctly).

This is essentially the argument about rust's Unsafe. You, or any of your dependencies, is free to subvert the borrow checker and do "unsafe" things.

You either have to trust that they are doing so responsibly, or manually verify the correctness of their unsafe code. Having to opt-in to unsafe access is, indeed, an advantage since it makes static analysis of how much "unsafety" there is easy (much as it's easier to find non-static things in vanilla Java than in python), but you can (and some do!) use static analysis to enforce type annotations in python throughout the entire code base.

> so it's an extra burden on the libraries' developers.

It's exactly the same burden as they would have in Java. So I'm not clear on what the point is. Worth noting though, that mypy supports stub files, so you can annotate a third party api yourself (https://github.com/python/typeshed, https://github.com/google/pytype/tree/master/pytype/pytd/std...), so even this claim is ultimately untrue.


> This is a uselessly semantic argument.

I'm sorry, but it is your argument that is uselessly semantic.

Yes, people could write Java code where all the type ascriptions are for Object. It is technically possible. But nobody does this. It has nothing to do with "the type ecosystem has been around longer" — it has to do with the fact that the types do something in Java, and always have and always will. You cannot compile a Java program and just not check the types.

But people do write Python libraries without type annotations, and that's fine. There's plenty of not-explicitly-typed Python in the world because that's all there was for a long time before the introduction of optional type annotations. And a lot of people prefer to write Python without type annotations at all because that's one of the benefits of a dynamically typed language — explicit types are unnecessary.

I think your argument is useless because you're intentionally conflating these things and arguing that people could write type-less Java, but since this is not actually done in practice it has no bearing in this conversation.

The point originally being addressed was that in Python you either need fully annotated libraries or else you cannot simply "use MyPy with Any turned off" as the far-parent comment suggested. And my point has been that this is a genuine concern in Python because there's lots of Python code without annotations because Python the language doesn't require or even use the annotations, so they are (from the perspective of the language) purely artifacts of no consequence. This is not the case in real, actually used Java code.

Please stop making arguments in bad faith to "prove a point" or whatever. The point the entire time has been about the use of type annotations in practice, and you appear to be intentionally disregarding this in an attempt to... I don't even know. What are you after here? Do you just not like that somebody disagreed with you? Like, I'm genuinely unsure what your purpose is other than to be contrarian. The concern about "are all the third-party libraries I'm using fully annotated?" is a genuine one in Python that is never even thought about when writing Java (have you ever paused to wonder this when writing Java?), so your argument amounts to a semantic debate that has no basis in the real world.


> This is not the case in real, actually used Java code.

But is the case in its type system, which is what I said. If you want to argue that the python developers are less prone to typing things than the java devs, then say that. Don't claim wrong things about the type systems. They're functionally equivalent.

The type checkers have all the same information, however much the users choose to give them. There are actually a few ways in which python's type system is fundamentally more capable than java's (literal types, protocols).

Literally all a static type system is is an assertion (and tooling to assert) that types must validate before you can run the code. That's it. If you provide a presubmit check that types must validate before you run the code, congratulations, your code is statically typed.

Put simply: enabling mypy + no-any gives you all the same static guarantees (and arguably more) that java does, so in what way isn't it statically typed?


> Put simply: enabling mypy + no-any gives you all the same static guarantees (and arguably more) that java does, so in what way isn't it statically typed?

The original question was about Python the language, not Python the ecosystem. At no point is it ever likely to be correct to say "Python is statically typed"; it will only be possible to say "this specific Python code can be statically typed, if you run the static type checker". This is a fundamental difference between optional static typing in Python versus a natively statically typed language. Because you can run the Python code without running the static type checks, which you cannot do in, say, Java.

Because of this distinction, it is never a genuine concern of a developer in Java or another statically typed language whether third-party libraries will be properly typed. There do not exist libraries without valid static types. You can't install something from Maven or what-have-you that won't type-check.

But you can do this in Python. You can install something via pip and there is no guarantee it will come with good type annotations for your type-checking tool to use, because the type checker is not part of the language proper. It's not required. So Python code exists in the real world that cannot be used with the same assumptions as you can use code in the Java ecosystem. And this is fine, by and large, because Python wasn't meant to be typed. There's nothing wrong with using code like this, nor is there anything wrong with writing code like this. You may not wish to do so personally, which is fine, but I don't think there will ever be an expectation made by the Python language proper of all Python developers to annotate their code. Python will always work without explicit types.

My point in all of this is that your original argument that "[whether there are concerns about third-party libraries having valid type annotations] is true in any type system" does not hold in the real world, because you simply won't have untyped libraries in a statically typed language's ecosystem like you will in Python. Perhaps you didn't realize that this was the point being made, but in any case you went off on a very semantic tangent that had no real bearing on the conversation at hand: the use of type annotations in practice.


> The original question was about Python the language, not Python the ecosystem.

Python doesn't have a spec, so the argument that the language doesn't support static typing is dubious at best, since a static type checker is maintained by the language itself. If I alias python to `mypy $0 && python $0` or whatnot, is it no longer python? It's certainly statically typed.

> Because of this distinction, it is never a genuine concern of a developer in Java or another statically typed language whether third-party libraries will be properly typed. There do not exist libraries without valid static types. You can't install something from Maven or what-have-you that won't type-check.

Is this enforced by the language, or is this just an aspect of the ecosystem? "Maven-packages must typecheck" is enforced by the spec?

> But you can do this in Python. You can install something via pip and there is no guarantee it will come with good type annotations for your type-checking tool to use, because the type checker is not part of the language proper. It's not required.

Are you saying that there is a guarantee in Java that the types need to be "good"? I'm not even clear that maven (which again isn't part of the java language) requires that uploads compile, much less that the type declarations be "good", whatever that means.

Which all returns to my central point: you trust the java ecosystem more that the python ecosystem to have "good" type declarations. There is nothing implicit in the type systems of those languages that enforces that one or the other have better or worse type annotations.

Your argument relies on conflating use in practice with theoretical guarantees, I'm just pointing that out. If you want to say that your experience is that python has less reliable types than java, then sure, that's probably true. But the type systems available in those languages are functionally equivalent. The use, however, differs.


It really feels like your entire contribution to this discussion amounts to "but what if we ignore the point being made and instead just focus on very strict semantic interpretations of the language other people in the conversation are using". I feel like you have not really engaged with my actual points and instead have focused on finding details to complain about in service of appearing to make a point, which effectively derails the true conversation. I find this incredibly frustrating, and I have no interest in continuing.

I would politely suggest you consider whether this is a common issue you have with people or whether it's just me (certainly a possibility, I admit). If this happens to you often, you may want to try alternative approaches to broaching your points if you'd like better engagement. I think most people don't enjoy when their main points are continually ignored in favor of bringing up other minutiae to prove that they're wrong or something.

Regardless, cheers.


A more appropriate example is:

`var list = new ArrayList<String>();` [1]

The compiler figures out the type of `list` without "annotations". Same with C++'s `auto` and Rust's `let`.

[1] https://openjdk.java.net/jeps/286


To clarify, no, this is a complete non-sequitor. The ability for a type system to do inference has no bearing on whether or not the types are checked or not. To make things even more confusing, as an example, python can have type inference (pytype) or not (mypy). In either case, annotations are type checked.


The other discussions have already pointed out some limitations in your interpretation (heh) of types.

I think the key difference between the annotations in Python and types in Java, or even more strongly in languages like Rust and Haskell, is how they are used in the language ecosystem. In Java and Rust, the types are cental to how programmers think in those languages. A large part of the dev cycle goes into ensuring the types are consistent. In python, these type annotations are a relatively recent addition that the community has not completely adopted yet. A lot of real-world python code will have no types, thus the (3rd party) python type checkers will fail to ensure the benefits of having strong consistent types.

And how can we then justify "types" like Object in Java or Any in Rust? Well, sometimes we do want to write code where we want to tell the compiler to stop checking types. However, these are more of an escape hatch rather than a central part of the language ecosystem. As someone else already pointed out, the Java or Rust community does not go about writing code with only Object or Any, even though they can in principle. But in python, that's what the community has been doing till now.

It goes the other way too. If the python community starts taking the type annotations seriously, it will start approaching the same usefulness as in these other static languages with good type systems. Then we can start expecting that the 3rd party library type annotations being accurate, which started this discussion in the first place.


> In python, these type annotations are a relatively recent addition that the community has not completely adopted yet.

I think you'll find I've said as much elsewhere ;)

I think I make essentially the same argument as you do here: https://news.ycombinator.com/item?id=24701113


Yeah sorry, I stopped reading after I felt the discussion was going round in circles. But it's funny how the same viewpoint results in different takeaways. Of course, the topic under discussion is a pedantic one.

However, "that's true of any type system" just feels dismissive of the current state of the world. The comment you were replying to was pointing out that at present, enabling strict type checking in Python, that has been bolted on as an add-on only recently, will not be able to provide benefits to the same level as the ones in Java and Rust where types are front and center. That might become true tomorrow when all Python libraries have adopted types. Hypothetically, tomorrow the Rust community could also decide to adopt Any as the central type everyone uses. But today, the state of the world is very different. Python is still known as a dynamically typed language where devs can essentially forget explicit types, and Rust is a strongly statically typed language where devs will rarely have to worry about missing types.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: