alexandru / scala-best-practices

A collection of Scala best practices
4.39k stars 623 forks source link

2.6 issues #19

Open ejstrobel opened 9 years ago

ejstrobel commented 9 years ago

I have 2 issues here, the first is a that the sentence "And this freedom is important, because a val for example restricts the way this value can get initialized - only at construction time." is not totally correct: An abstract val may be implemented by a lazy val which is initialized on first use.

The second and more important is about interface design. Yes a parameter-less def gives more flexibility in implementing. But on the other hand, it makes less of a promise to the interface user. Sometimes I really do want to promise that a property is "stable" (or immutable), as in:

trait Foo {
 val value: String
}

def run(foo: Foo): Unit = {
  val prompt = foo.value + " >"   // or something more expensive
  while(true) {
    print(prompt)
    // read & do something
  }
}

Here Foo promises a stable value, which may not change. Therefore I do not need to re-calculate prompt on every iteration of the loop.

I suggest adding "... unless your interface needs to promise a stable value" to the rule.

alno commented 9 years ago

+1 to stable value case.

Also, text "And because we have the value defined as a val, we cannot fix the above example by overriding value as a lazy val, an otherwise decent way of fixing such a sample." is incorrect: it's ok to override val with lazy val, see faq and repl output:

scala> trait Foo { val value: String }
defined trait Foo

scala> trait Bar extends Foo { val uppercase = value.toUpperCase }
defined trait Bar

scala> trait MyValue extends Foo { lazy val value = "hello" }
defined trait MyValue

scala> new Bar with MyValue
res1: Bar with MyValue = $anon$1@1ce5ab88

scala> (new Bar with MyValue).uppercase
res2: String = HELLO
alexandru commented 8 years ago

You guys are right. Will be fixed.