St.Antario
St.Antario

Reputation: 27455

Scala type hierarchy

I looked at Scala Type Hierarchy enter image description here

It's pretty clear that Unit is a subtype of AnyVal. So I tried this:

object Main extends App {

    val u : Unit = Seq[String]()

}

and it compiled fine. But I expected some error. DEMO.

Why? Unit is not a supertype of a Seq

Upvotes: 0

Views: 586

Answers (2)

Jörg W Mittag
Jörg W Mittag

Reputation: 369623

Why? Unit is not a supertype of a Seq

You are assuming that the Seq is assigned to u. But it isn't:

println(u)
# ()

As you can see, the value of u is () (i.e. the unique singleton instance of Unit), not an empty Seq.

This is due to Value Discarding, which is described in clause 5 of section 6.26.1 Value Conversions of the Scala Language Specification:

The following seven implicit conversions can be applied to an expression e which has some value type T and which is type-checked with some expected type pt.

[…]

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; () }.

In plain English as opposed to "Speclish", what this means is: if you say "this is of type Unit", but it actually isn't, the compiler will return () for you.

The Unit type is sort-of the equivalent to a statement: statements have no value. Well, in Scala, everything has a value, but we have () which is a useless value, so what would in other languages be a statement which has no value, is in Scala an expression which has a useless value. And just like other languages which distinguish between expressions and statements have such things like statement expressions and expression statements (e.g. ECMAScript, Java, C, C++, C♯), Scala has Value Discarding, which is essentially its analog for an Expression Statement (i.e. a statement which contains an expression whose value is discarded).

For example, in C you are allowed to write

printf("Hello, World");

even though printf is a function which returns a size_t. But, C will happily allow you to use printf as a statement and simply discard the return value. Scala does the same.

Whether or not that's a good thing is a different question. Would it be better if C forced you to write

size_t ignoreme = printf("Hello, World");

or if Scala forced you to write

val _ = processKeyBinding​(…)
// or
{ processKeyBinding​(…); () }

instead of just

processKeyBinding​(…)

I don't know. However, one of Scala's design goals is to integrate well with the host platform and that does not just mean high-performance language interoperability with other languages, it also means easy onboarding of existing developers, so there are some features in Scala that are not actually needed but exist for familiarity with developers from the host platform (e.g. Java, C♯, ECMAScript) – Value Discarding is one of them, while loops are another.

Upvotes: 3

Luka Jacobowitz
Luka Jacobowitz

Reputation: 23532

This happens because Unit can also be inferred from statements (in contrast with expressions). This means that if you use the Unit type annotation, the compiler will ignore the last value in your expression and return Unit after your actual code.

You can simply testing this, by casting your u back to its original type. You will get a cast error, because u isn't actually bound to your Seq.

Here's what happens if you run this in the REPL:

scala> u.asInstanceOf[Seq[String]]
java.lang.ClassCastException: scala.runtime.BoxedUnit cannot be cast to scala.collection.Seq

As Jasper-M correctly pointed out, the compiler will rewrite this to

val u: Unit = { Seq[String](); () }

Upvotes: 4

Related Questions