In Scala, why can I use the "Unit" type here?
Codes below:
scala> def f(x:Int => Unit):Unit = 1
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
def f(x:Int => Unit):Unit = 1
^
f: (x: Int => Unit)Unit
scala> f(_=>2);
<console>:9: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
f(_=>2);
^
scala> f(_=>List(1,2));
All three expressions above worked in the REPL (with some warnings), but they look a little confusing.
In the first expression, the return type f
Unit
, which is a subtype AnyVal
but not a supertype Int
, so I can't figure out why 1
it can be used as a return value.
In the second expression _=>2
also used 2
instead Unit
as a return value, which is contrary to the definition.
The third expression _=> List(1,2)
even uses the List
subtype AnyRef
as its return value, but the REPL still doesn't complain about that.
Does anyone have any idea why Unit
can tolerate no subtype type conversion here? Thank!
source to share
Scala will automatically insert ()
(singleton value Unit
) in this case to do the type checking operation. So you have the equivalent:
def f(x:Int => Unit):Unit = { 1; () }
This is called "dropping a value" in Scala. From the spec :
Value Drop
If it
e
is of some value type and the expected typeUnit
,e
converts to the expected type by inserting it into the member{ e; () }
As with many programming languages, this simply means "throwing out" the return value of the expression. This allows you to create a type method Unit
that only uses the side effects of the expression.
source to share
Flag Implicit Conversions in SLS
ValueDiscarding. If e is of some value type and the expected type is Unit, e is converted to the expected type by inserting it into the {e; ()} member.
source to share
In addition to Ben Reich's answer:
Conceptually, in Scala, a type Unit
is a supertype of any other type. Therefore, every value, including Int
, is assigned Unit
. Effectively, if used as the return type of a method, the compiler will throw out the result value by providing the JVM / Java method void
.
Nothing
is the exact opposite, by the way: conceptually it is a subtype of any other type. Since no instance Nothing
exists, no instance of any other type can be made compatible with Nothing
.
Java is void
somehow an incomplete mix of both of them.
source to share