How were weak and strong defined in the literature you read? Anyway, more on soundness. I can recommend a couple papers: Wadler (inventor of the monad) wrote a paper called "Well-Typed Programs Can’t Be Blamed" [0] about soundness. There was a recent paper that made the rounds about how java's type-system is provably unsound as well that's worth reading [1]. I second the parent comment that weak and strong are ill-defined in my experience, while sound/unsound have well-accepted definitions and are commonly used in academia. Your experience obviously differs for whatever reason. You can find many other references to soundness in the citations of those papers, as well as scattered around the internet. [0]: https://link.springer.com/chapter/10.1007%2F978-3-642-00590-... [1]: https://dl.acm.org/doi/10.1145/2983990.2984004
Java and scala's type systems are unsound: the existential crisis of null pointers
Nada Amin, Ross Tate
2016 · Proceedings of the 2016 ACM SIGPLAN International Conference on Object-Oriented Programming, Systems, Languages, and Applications
- HN score
- 1
- Mentions
- 4
- Readers
- 4
- Months
- 4
In the conversation
Original HN comments, with labels from simple text rules. A label describes the whole comment and may miss context or irony. Read the discussion to make up your own mind. About the labels
4 selected excerpts · 4 total mentions
Generics ended up making Java's type system unsound. Not a huge deal in practice, but the following paper sheds some light on just how complex Java's type system really is one you start poking around a bit: https://dl.acm.org/doi/pdf/10.1145/2983990.2984004
> The language actually provides quite a strong safety guarantee for generics, as long as we follow the rules: If a program compiles with no unchecked or raw warnings, the synthetic casts inserted by the compiler will never fail. Huh. That was written in 2020, four years after it was shown how to write a very small program that "compiles with no unchecked or raw warnings", and yet "the synthetic casts inserted by the compiler" will fail at run time [0]: class Unsound { static class Constrain<A, B extends A> {} static class Bind<A> { <B extends A> A upcast(Constrain<A, B> constrain, B b) { return b; } } static <T, U> U coerce(T t) { Constrain<U, ?…
Read the full comment
super T> constrain = null; Bind<U> bind = new Bind<U>(); return bind.upcast(constrain, t); } public static void main(String[] args) { String zero = Unsound.<Integer, String>coerce(0); } } [0] N. Amin, R. Tate "Java and Scala's type systems are unsound: the existential crisis of null pointers", https://dl.acm.org/doi/pdf/10.1145/2983990.2984004
I agree with everything you say, except I have a lower bar of "meaningful". > one possibility is simply that “dynamic type checking” is meaningless. If I take your quotation out of context, this was the assumption of some people who separated languages into simply "strongly" vs "weakly" typed, by which they meant "checked by compiler" vs "not checked". It's incorrect though, and is the reason for separate "static" vs "dynamic" distinction. C is a famous example of static (checked at compile time) but weak (≈ unsound) typing — compiler will happily accept programs that corrupt memory in all kinds of ways. mutating "const" variable, freeing used memory, crashing, remote…
Read the full comment
code execution, and more... I don't think there are any invariants that a C compiler can enforce. Scheme, Python, Lua, Javascript etc. OTOH, have no static (= compile-time) type checking, yet they maintain some invariants! An object that has pointers to it will not be freed; An object's type is known, and will not change (well, some class transmutation is allowed but some not, an int will not turn into an array); Some types are immutable; You can never divide a string / string! etc... Moreover, by maintaining run-time metadata about object's types, they can tell you specifically that an operation raised a TypeError. Thus these languages are strongly typed at run time. IOW, I'm arguing that anything that blows up at run time is checked IFF it tells you it was a type error. This is meaningful compared to C segfaults that tell you nothing :-) --- The Java paper https://dl.acm.org/doi/pdf/10.1145/2983990.2984004 shows an interesting subtlety. "Fortunately, parametric polymorphism was not integrated into the Java Virtual Machine (JVM), so these examples do not demonstrate any unsoundness of the JVM" — yet they present unsound programs that "type-checks according to the Java Language Specification and is compiled by javac, version 1.8.0_25". What gives? If a bad program compiles, how come JVM is still sound? See, Java has two type systems! - JVM is the runtime, intended to be capable of loading even untrusted bytecode yet still maintain some type invariants. It manages memory and type metadata, and for this bad program will correctly identify type violation at run-time: "When executed, a ClassCastExceptionis thrown inside the main method with the message “java.lang.Integer cannot be cast to java.lang.String”" - The compilers uses a distinct more complex static type system. The question of soundness is: can any program that passed the compiler cause JVM run-time type errors? + Java language allows you to write type casts. These are deliberate "trust me" holes in the *static* type system, whose specified semantics is: JVM will check type at run time and raise exception if not as programmer promised. As https://typing-is-hard.ch/#what-about-unsafe-casts says, since these are deliberate, and fall back to meaningful run-time type checking(!), let's ignore them — redefine "soundness" as: can a program with no exclicit casts cause a run-time type error? + Generics only exist in static type system! They are invisible to JVM (aka "type erasure"), they compile into dynamically checked casts. Their soundness goal was that the compiler can prove these implicit casts will never fail — e.g. you can only put candies into ArrayList<Candy>, so arr.get(0).eat() is guaranteed to give you a candy you can eat. That paper demonstrates a simple 17-line program with no explicit casts that compiles yet causes run time type error. --- If you think what type soundness means, ALL statically typed languages have 2 type systems! There are execution semantics — what it means to, say, compute number + number. And there is a static language of talking about types that aims to predict / prove the types that will be involved at run time. Static checking fails if they don't match. Dynamic checking largely fails when you don't do it :-) But also when you erased info you needed to do it.
Keep following the thread.
More in Programming languagesMore from the same topics.
Copy-and-Patch Compilation: A fast compilation algorithm for high-level languages and bytecode
Haoran Xu, Fredrik Kjolstad
Fast compilation is important when compilation occurs at runtime, such as query compilers in modern database systems and WebAssembly virtual machines in modern browsers.
Spectre is here to stay: An analysis of side-channels and speculative execution
Ross Mcilroy, Jaroslav Sevcik, Tobias Tebbi +2
The recent discovery of the Spectre and Meltdown attacks represents a watershed moment not just for the field of Computer Security, but also of Programming Languages.
The evolution of Smalltalk: from Smalltalk-72 through Squeak
Daniel Ingalls
This paper presents a personal view of the evolution of six generations of Smalltalk in which the author played a part, starting with Smalltalk-72 and progressing through Smalltalk-80 to Squeak and Etoys.
Structured Programming with go to Statements
Donald E. Knuth