Reputation: 27200
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?
Upvotes: 2
Views: 197
Reputation: 170735
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.
val sb = new StringBuilder
val strings: List[String] = ...
for (str <- strings) { sb.append(str) }
By analogy with for
loops in other languages, we would expect it to compile. But without value discarding it wouldn't: this is equivalent to strings.foreach(str => sb.append(str))
, the type of str => sb.append(str)
is String => StringBuilder
(because all append
methods on StringBuilder
return the builder itself) and foreach
on List[String]
takes String => Unit
.
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.
Upvotes: 5
Reputation: 32309
Unit
is not a supertype of everything! Scala actually has a pretty wide variety of conversions that happen automatically and this is one of them. From section 6.26.1 Value Conversions of the Scala language spec, one of the conversions is
Value Discarding
If
e
has some value type and the expected type isUnit
,e
is converted to the expected type by embedding it in the term{ e; () }
.
So when you write something like val a: Unit = 1
, it gets processed into val a: Unit = { 1; () }
, which is quite different. The warning is actually very helpful here - it is warning you that you probably did something wrong: the expression you are trying to put into statement position is pure (has no side-effects), so executing it has no effect (except possibly to cause the program to diverge) on the final output.
Upvotes: 1