Arguments against Scala language

It’s not merely academic, because people may write real world code that runs into this issue. That doesn’t necessarily mean it is a big deal. I don’t know how hard it would be to fix this, but my guess is it would be very hard.

Wasn’t the primary motivation for rebuilding the compiler backend from scratch that Scala 2 was somehow unsound, but Dotty is sound, because it is based on DOT calculus?

I suppose there are two possibilities now: either Martin is going to invent the DASH calculus, which is really sound, and then create an experimental DASHy compiler which will morph into Scala 4, complete with Reverse Polish Notation. Or, he will prove the Odersky Incompleteness Theorems, which state that any sufficiently powerful type system is unsound, and then go on to invent new dynamically typed languages.

Am I safe if I dont use nulls?

DOT doesn’t have nulls.

So, Dotty/Scala 3 is not based on DOT, but DOT+Null, and DOT+Null is not sound?

In that case, there is a demand for a new sound DASH that replaces DOT+Null, or else proof that such a thing does not exist. Unless, of course, it has already been proven that it is impossible.

Avoiding nulls is a very hard problem. Nulls arise from interop and from initialization issues.

Interop nulls can be handled by containing methods calls that can return null and immediately handling the null.

Initialization is trickier. For example, you get null if you have initialization cycles such as:

object A { val x: String = B.y }
object B { val y: String = A.x }

Keep in mind that every type or object is compiled separately, and that the cycle may be longer than two steps, and may be obscured by inheritance, composition, type classes or chains of method calls.

Ironically, when someone asks for help with initialization issues, they usually get the advice “use lazy vals”, which solves many initialization issues, but not cycles.

Scala 3 should contain experimental support for null tracking.

Sadly it looks like @curoli’s example isn’t caught by null tracking, even when using both -Yexplicit-nulls and -Ycheck-init.

I’m not sure if this is expected, as the doc for explicit nulls states, that the unsoundness of the type system in regards to null should be caught by -Ycheck-init, so I filed a bug

4 Likes

I would like to add to this topic that if I ask if you can program in a purely functional way in Scala (also without OOP) I often hear that Scala is not 100% functional and I would like to ask what exactly they are mean. For example in Functional programming in Scala, I found some constructs like functions that are referentially transparent but inside them, they use mutable objects and in this way, I can reason about that function like if it was 100% pure and functional but it is a little more efficient and I started to wonder if Scala is that language that (also if I drop OOP) I can program mostly in a functional way (especially with libs like Cats or Scalaz) and when I cant its because more imperative/mutable (but for example referentially transparent) constructs are even more suited for building and implementing real-world systems and programs than the purely functional counterpart. What do you think about that? Is it the case that often some kind of fusion of mutable, imperative, and functional programming like in Scala seems the best way to solve real-world problems?

2 Likes

It is definitely possible to write 100% functional code in Scala. Cats Effect and ZIO both provide an alternative to a normal main method, where your entry point returns an IO monad, like in e.g. Haskell.
But Scala does not enforce functional programming. A function signature will not tell you for sure, that a function is side effect free, so in contrast to Haskell the compiler will allow impure functions in otherwise functional code.

Yes, basically you can write everything functional, but you always have the option to trade purity for efficiency / clarity, if a piece of code would be faster / simpler in an imperative way. And in many cases, if your side effects are restricted to mutating local variables, your function will behave the same as a functional implementation from the outside. An example are the implementations of immutable collections in the standard library, the results of their methods are often generated with a mutable builder for efficiency.

As someone not writing libraries, I often keep everything functional, and only use mutable / imperative code, if something turns out to be a performance bottleneck. But there are also people, that write most logic functionally, but don’t want to go as far as using Monads for IO, so files, networking and databases are written imperatively. The point is, in Scala you can choose, how pure you want your code to be, which depends on your specific project’s requirements.

10 Likes