Rogach Rogach - 1 month ago 14
Scala Question

Why Unit is a supertype of anything else?

Here's an example:

$ scala
Welcome to Scala 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_112).
Type in expressions for evaluation. Or try :help.

scala> val a: Unit = 1
<console>:11: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
val a: Unit = 1
^
a: Unit = ()


In Scala documentation:

There is only one value of type Unit, ()


Why is Scala compiler silently coercing values to Unit?

A bit of context: I used
Future[Unit]
type to describe some procedure which does not return anything. And since
Future[Unit]
is now effectively a subtype of
Unit
, I got bit by some funny bugs (
someFuture.map(a => Future(a))
silently skips calling the operation instead of giving compilation warning). What am I supposed to use as a type of operation that does not return any meaningful result?

Answer

Unit is not a supertype of other types. What happens instead is called value discarding: when the expected type of an expression e is Unit, the compiler replaces it by {e; ()}. This is done to make some behavior more familiar. E.g. foreach methods generally take A => Unit functions. So consider

val sbOpt: Option[StringBuffer] = ...
for (sb <- sbOpt) { sb.append("a") }

Without value discarding it wouldn't compile because sb.append("a") returns sb itself.

You can add -Ywarn-value-discard compiler option to warn you when it happens (and write for (sb <- sbOpt) { sb.append("a"); () } explicitly).

Or you can actually go with a trick of defining your own Unit (possibly changing the name to avoid confusion for anyone reading your code):

 object Unit 
 type Unit = Unit.type 

 implicit def unit2scalaunit(a: Unit): scala.Unit = () 
 implicit def scalaunit2unit(a: scala.Unit): Unit = Unit 

This should avoid running into the problem with Future you describe.

Comments