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

A sound type system is one that enforces certain runtime behaviors at compile-time.

Using a null reference is not a behavior ether the Java or Scala type systems promise to prevent, just as Rust's type system does not promise to prevent panic!.

Java's type system is unsound if, say, without casting or other "unsafe" operations, we could have a compile-time Integer that is actually a runtime String.




So if I'm expecting my variable to contain a String, and I try to do Stringy operations on it, what difference does it make to me if that fails because it actually contained an Integer or actually contained a null? My program is just as meaningless either way. Maybe from a standpoint of what's officially promised by the runtime, it's sound, but that feels to me like moving the goalposts. In terms of useful guarantees, Java's type system is, let's just say, kind of useless, if "unsound" is technically incorrect.


(1) I 100% agree that null is a terrible, terrible idea https://www.lucidchart.com/techblog/2015/08/31/the-worst-mis...

(2) It's all relative. Most languages allow lots of invalid operations on data.

What if I'm expecting my variable to contain a non-empty list and I try to do non-empty list things to it and fail? Dependently-typed languages like Idris say everyone else is moving the goal posts by accepting this behavior in their type systems.


It's not necessarily a matter of "do these things behave the same at runtime on the JVM I ran my program on", it's a matter of semantics -- what these programs mean -- and that actually does have a direct impact on what happens at runtime, if you widen your horizons a little. Focus on the cause, not the effect. It's not really moving the goalposts, considering nobody was talking about dereferencing a `null` String in the first place, least of all, the paper itself. `null` is crucial to their argument, but injecting your own impressions about how "it's nothing new, because I had a runtime error before, take that, dumb dumb academics" is mostly irrelevant posturing, it's not what the paper is about.

Imagine suddenly if your setting was changed, and e.g. you were not running Java code on the JVM, but directly compiled to native code. It's very well defined what happens when you try to use a `String` but it's actually a `null`, even at the bare metal level - you get a NullPointerException. Easy. Catch the exception somewhere, or don't, it's all very straightforward.

And despite what you said in your post -- to the contrary, a program which dereferences `null` is completely well defined and totally meaningful! It is not nonsense from a semantics point of view. Does a program which dereferences null do anything useful, all on its own? No. But it absolutely is a program that has meaning and you can describe what will happen by looking at it (roughly speaking), because its behavior is fully defined.

What happens at the bare-metal level when you try to use this Unsound module to convert an `Integer` to a `String`? Well... Bad things will probably happen, up-to and possibly including things like memory corruption or outright termination, depending on how the implementation works. Maybe there is an optimization so small `Integer`s are represented with a different object layout than a large `String` is, at runtime. The runtime system will likely clobber itself if it tried to directly coerce these two different objects, with different sizes, fields, layouts, etc -- because like most typed languages, it would likely rely on the object type to describe memory layout (for things like the GC, so it knows where to find pointers). Classic memory corruption scenario.

The JVM itself is not broken by this problem, so it is "correctly" handled for most practical purposes, but only by that coincidence that the JVM retains type safety here. Despite that, there is still no true "meaning" to this program. It is, like the prior program, useless -- but the prior program was meaningful. This program is not.

Similarly, it's necessary to understand these kinds of corner cases if we want to do things like formalize compilers, semantics, and design find better points in the design space that can ameliorate these problems (either by tooling, language revisions/changes, or entirely new languages).

For most end users, you're basically right, things like this will "only" manifest as a runtime error, and a runtime error is a runtime error is a runtime error. But the paper is about the actual semantics of Java the language, which is a much more broad topic, as opposed to the fact that yes, there's a new way to get a runtime exception.


I wouldn't say that the JVM works on this by coincidence. Java handles polymorphism by converting the code into casts which the JVM typechecks. The JVM is sound. One of the constraints with introducing polymorphism was not changing the JVM.


> Does a program which dereferences null do anything useful, all on its own? No.

Yes it does. It produces an exception, which contains a stack trace. I've actually used that, when I had a program that was reaching some code, and I couldn't figure out what the path it took to get there. It was easier to create an exception, catch it, and print the stack trace than to fire up a debugger and set a breakpoint.




Registration is open for Startup School 2019. Classes start July 22nd.

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

Search: