Quite confused about this code snippet return types with & without =

k r picture k r · Aug 21, 2013 · Viewed 10.8k times · Source
object A extends App {

def closure1(x:Int) = {
  object O {
   def getX = x
   def add(y:Int) = x+y
  }
  val foo = O
  foo
}

def closure2(x:Int) {
  object O {
   def getX = x
   def add(y:Int) = x+y
  }
  val foo = O
  foo
}

println(closure1(4).getClass)
println(closure2(4).getClass)
}

result:
 $scala A
 class A$O$3$
 void
  1. Why are the return types different ?
  2. I really don't care about the return types. I want to call getX and add - I want to do something like closure1(4).getX - this is illegal. How do I do that ?

Answer

Rex Kerr picture Rex Kerr · Aug 22, 2013

Scala has a shorthand notation for code that doesn't return any interesting value: omit the = sign. In Java, this would return void, i.e. nothing at all; but in Scala it's actually (), the only member of the type called Unit. Either way, it's the same: nothing or a meaningless placeholder.

The reason you want a meaningless placeholder is that when you write generic code you'd rather not have to handle the cases of something vs. nothing differently.

Anyway:

def f(): Unit = println("Hi")

is a function that explicitly returns only the content-free () value (which is what println returns also). And the shorthand is

def f() { println("Hi") }

Now there's one sneaky addition which is that in Scala, as with many C-derived languages, you're allowed to just throw away the return value from whatever you do. And when you throw it away, all that's left is (). Scala will warn you on closure2 that you are doing something suspicious:

<console>:16: warning: a pure expression does nothing in statement position
you may be omitting necessary parentheses
         foo
         ^
defined module A

but will still let you do it (as it is historically expected that this will work).

So, to summarize:

def f {}

is a method that returns only the content-free placeholder (). If you write it out in full the syntax would be

def f: Unit = {}

and when you try to return a value of the wrong type, instead of complaining it throws away the value and gives you type Unit, but typically emits a warning:

def f: Unit = 5
def f { 5 }

(Note that opinion is mostly against the short form these days (these days being 2.10-is-stable), in large part because in the absence of a clear explanation of the difference, new users often omit = without realizing it and then wonder why things don't work. So in the future (2.11 or 2.12 for deprecation?) the def f {} form may not work.)

If you actually want a return value--because, for example, you want to return your object O (which you can do directly without first assigning it to foo, by the way), make sure you include =:

def f = { object O { def g = 5 }; O }

scala> f.g
res0: Int = 5

(Hint: the compiler will complain at you that you're using structural types here. You're better of with trait HasG { def g: Int } and then object O extends HasG; otherwise Scala actually uses reflection to call f.g for some rationale that I've never quite been able to follow.)