WelcomeTo
WelcomeTo

Reputation: 20571

When should a Scala method def use an explicitly defined return type?

Scala-style-guide suggest to always explicitly define return type expect when used Unit:

Methods should be declared according to the following pattern:

def foo(bar: Baz): Bin = expr

The only exceptions to this rule are methods which return Unit. Such methods should use Scala's syntactic sugar to avoid accidentally confusing return types:

def foo(bar: Baz) { // return type is Unit
  expr
}

Upvotes: 4

Views: 3478

Answers (1)

0__
0__

Reputation: 67280

As commentators have pointed out, Martin Odersky discusses this in the linked Keynote. Of course, as "when should..." indicates, this is ultimately a stylistic question, so one can only give an opinion.

Two things: (1) Types yes/no, and (2) Procedure syntax yes/no.


(1) Obviously with pure API (without implementations) you must specify the return type. I would extend this to any method implementation which is also at the same time API. I.e., if your method is not implementing a trait—in which case the compiler will give you an error when your return type differs—, you should annotate the method. Private and local methods are fine to have the return type inferred, unless you find it difficult to figure out the type at the first glance, or you are forced (e.g. in a recursive method call). It is also said that the compilation is faster when you give explicit return types.

If you have short methods with an "easy" return type, I think inference is fine. Ex.

trait Foo {
  def sqrt(d: Double) = math.sqrt(d)

  def toString = "Foo"

  def bar = new Bar(123)
}

(But already someone could argue that it might not be obvious that math.sqrt returns a Double).

Although more verbose, you make the code more readable and you avoid (a) leaking information about an implementation or sub-type where a super-type sufficies:

trait Before {
  def bar = new BarImpl

  def seq = Vector(1, 2, 3)
}

trait After {
  def bar: Bar = new BarImpl

  def seq: IndexedSeq[Int] = Vector(1, 2, 3)
}

And (b) you avoid accidentally returning something you didn't intend to, from structural type to wrong collection type etc.


(2) Until recently I preferred procedure syntax, but after renewed discussion and many people voicing their discontent with it, I tried using the explicit : Unit = annotation, and I like it more now. I thought procedure syntax makes it clear that a method has a side effect, but indeed : Unit = does carry that indication quite clearly, too. Also it often removes the amount of curly braces:

trait Before {
  private var _x = 0
  def x: Int = _x
  def x_=(value: Int) { _x = value }
}

versus

trait After {
  private var _x = 0
  def x: Int = _x
  def x_=(value: Int): Unit = _x = value  // if double = confuses you, use new line?
}

I found many cases where the body begins with an if, a pattern match, synchronized block or future generation, a collection map expression etc., and in these cases removing the curly braces is nice:

trait Before {
  def connect()(implicit tx: Tx) {
    values.foreach { case (x, _) =>
      x.changed += this
    }
  }
}

trait After {
  def connect()(implicit tx: Tx): Unit =
    values.foreach { case (x, _) =>
      x.changed += this
    }
}

Upvotes: 2

Related Questions