Alan Thomas
Alan Thomas

Reputation: 1034

How do I explicitly return Unit?

What is the proper way to explicitly return the Unit type from a method, using () or Unit? It appears to me that both work in all cases I've tried myself.

For context, this often occurs if I'm writing a method with side effects and returns Unit that calls another method, which also performs side effects but returns some value instead of Unit. e.g.

def effectAndReturn(): String = {
  val msg = "Hello, SO"
  println(msg)
  msg
}

def doEffect(): Unit = {
  val _ = effectAndReturn()
  () // `Unit` also works here
}

From my understanding () is the only value of type Unit that exists. Returning the token Unit in doEffect() is referencing the Unit companion object; I'm confused how this would return a value as there's not even an apply method defined on it. Returning the companion object for a given abstract class's type isn't valid as a return value as far as I know.

Plugging these into a Scala REPL is also interesting

scala> val parenUnit = ()
parenUnit: Unit = ()

scala> parenUnit
// Returns blank line

scala> val wordUnit = Unit
wordUnit: Unit.type = object scala.Unit

scala> wordUnit
res1: Unit.type = object scala.Unit

scala> res1
res2: Unit.type = object scala.Unit

() is simply a Unit value, whereas Unit gives back a type, which doesn't make sense to me as no other companion objects do this as far as I can tell. My guess is that the compiler handles Unit in a particular and unique way compared to any other type, but how exactly?

Upvotes: 9

Views: 10424

Answers (2)

Duelist
Duelist

Reputation: 1572

Suppose we have two methods in a class called TestUnit:

class TestUnit {
  def foo(): Unit = 2217
  def bar(): Int = 1478
}

Let's look at its bytecode:

  // access flags 0x1
  public foo()V
   L0
    LINENUMBER 4 L0
    SIPUSH 2217
    POP
    RETURN // returns void
   L1
    LOCALVARIABLE this Lunit/TestUnit; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  public bar()I
   L0
    LINENUMBER 5 L0
    SIPUSH 1478
    IRETURN // returns integer because it is declared in method
   L1
    LOCALVARIABLE this Lunit/TestUnit; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

My assumption is - the Scala compiler just put RETURN instruction (which returns void) in every method where Unit is declared as returning type. (you can look listings here)

So you can return any type in doEffect(). But as @ghik said, it's better to use ().

Upvotes: 1

ghik
ghik

Reputation: 10764

So, we have:

  • The Unit type
  • The unit value, i.e. () which is of type Unit
  • The Unit companion object which causes the confusion. It is not of type Unit. It is of type Unit.type (its own singleton type). However... Scala automatically implicitly converts everything to Unit and that's why you can use it where Unit type is expected.

TLDR: Use ()

Upvotes: 20

Related Questions