Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What special rules does the scala compiler have for the unit type within the type system

Tags:

The Unit gets special handling by the compiler when generating byte code because it's analogous to void on the jvm. But conceptually as a type within the scala type system, it seems like it also gets special treatment in the language itself (examples below).

So my question is about clarifying this and understanding what mechanisms are used and if there really is special treatment for the Unit type.


Example 1:

For "normal" scala types like Seq, if a method returns Seq, then you must return Seq (or a more specific type that extends Seq)

def foo1: Seq[Int] = List(1, 2, 3) def foo2: Seq[Int] = Vector(1, 2, 3) def foo3: Seq[Int] = "foo" // Fails 

The first two examples compile because List[Int] and Vector[Int] are subtypes of Seq[Int]. The third one fails because String isn't.

But if I change the third example to return Unit though, it will compile and run without issue even though String isn't a subtype of Unit:

def foo3(): Unit = "foo" // Compiles (with a warning) 

I don't know of any other type which this exception would be allowed for in scala. So does the compiler have special rules for the Unit type at the type system level, or is there some kind of more general mechanism at work e.g. an implicit conversion.


Example 2:

I'm also not clear how unit interacts in situations where variance rules would normally be applied.

For example, we sometimes hit this bug with Future[Unit] where we accidentally use map instead of flatMap and create a Future[Future]:

def save(customer: Customer): Future[Unit] = ... // Save to database  def foo: Future[Unit] = save(customer1).map(_ => save(customer2)) 

The map is creating a Future[Future[Unit]] and the compiler requires a Future[Unit]. Yet this compiles!

At first I thought this was because Future[+T] is covariant, but actually Future[Unit] isn't a subtype of Unit so it doesn't seem to be that.

If the type gets changed to Boolean for example, the compiler detects the bug:

def save(customer: Customer): Future[Boolean] = ...  def foo: Future[Boolean] = save(customer1).map(_ => save(customer2)) // Compiler fails this 

And for every other non-Unit type it won't compile (except Any because Future[Any] happens to be a subtype of Any by coincidence).

So does the compiler have special rules in this case? Or is there a more general process happening?

like image 535
rmin Avatar asked Dec 20 '16 09:12

rmin


People also ask

How does the Scala compiler work?

Scala has both a compiler and an interpreter which can execute Scala code. The Scala compiler compiles your Scala code into Java Byte Code which can then be executed by the scala command. The scala command is similar to the java command, in that it executes your compiled Scala code.

What is type safety in Scala?

Type safety means that the compiler will validate types while compiling, and throw an error if you try to assign the wrong type to a variable.


1 Answers

As written in the scala language specification chapter 6.26.1:

Value Discarding

If e has some value type and the expected type is Unit, e is converted to the expected type by embedding it in the term { e; () }.

like image 103
rethab Avatar answered Oct 02 '22 17:10

rethab