Hanfei Sun
Hanfei Sun

Reputation: 47041

In Scala, why can I use `Unit` as return type here?

The codes are 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 REPL(with some warnings), but they look a bit confusing..

In the first expression, f's return type is Unit, which is the subtype of AnyVal but not the Supertype of Int, therefore, I can't understand why 1 can be used as the returned value.

In the second expression, _=>2 also used 2 instead of Unit as the returned value, which conflicts with the definition.

In the third expression, _=> List(1,2) even uses List, subtype of AnyRef as the returned value, but the REPL still doesn't complain about this..

Does anyone have ideas about why Unit can tolerate the non-subtype type conversion here? Thanks!

Upvotes: 4

Views: 759

Answers (3)

Madoc
Madoc

Reputation: 5919

In addition to the answer of Ben Reich:

Conceptually, in Scala the type Unit is the super type of every other type. Therefore, every value, including Int, is assignable to Unit. Effectively, if used as a return type of a method, the compiler will throw away the result value, giving the method the JVM/Java void type.

Nothing is the exact opposite, by the way: Conceptually, it is a subtype of every other type. Since no instance of Nothing exists, no instance of any other type can therefore be assignment compatible with Nothing.

Java's void somehow is an incomplete mix of both of those.

Upvotes: -1

Ben Reich
Ben Reich

Reputation: 16324

Scala will automatically insert () (the singleton Unit value) in this case to make the typechecking work. So what you have is equivalent to:

def f(x:Int => Unit):Unit = { 1; () }

This is known as "value discarding" in Scala. From the spec:

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 in many programming languages, this is meant to facilitate just "throwing out" the return value of the expression. This allows you to make a method of type Unit that only uses the side effects of an expression.

Upvotes: 14

mkrakhin
mkrakhin

Reputation: 3486

Check implicit conversions section in SLS

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

Upvotes: 2

Related Questions