Search code examples
scalamethodsfielduap

What does "fields and methods are equivalent" mean?


In the Laziness section of 'Effective Scala', it says:

Fields in scala are computed by need when val is prefixed with lazy. Because fields and methods are equivalent in Scala (lest the fields are private[this])

What does it mean by 'fields' and 'methods' being equivalent? Isn't it a rather strong statement?


Solution

  • Well it simply means that you can define an abstract val by a def and an abstract def by a val.

    For example

    trait Server {
      def port:Int
    }
    

    has an abstract function, namely port. You can for sure implement (or define or override) this with a def, like this

    object DefaultServer extends Server {
      override def port: Int = 80
    }
    

    But in this case every access to port will cause a function application (or a method call) which is simply unnecessary. For this simple reason Scala gives us the possibility of implementing the abstract def with a value:

    object DefaultServer extends Server {
      override val port: Int = 80
    }
    

    The same applies to abstract values. You can define abstract values with the same syntax:

    trait Server {
      val port: Int
    }
    

    And you can override them with a def:

    object DefaultServer extends Server {
      override def port: Int = 80
    }
    

    Stability

    You may wonder what will happen if you override an abstract val with a def which gives you a different value each time you call it. Will you get the first computed value because the item is a val or you will get a different value each time you call it because the actual implementation is a def.

    For example:

    object DefaultServer extends Server {
      override def port: Int = scala.util.Random.nextInt()
    }
    

    Fortunately Scala compiler detects this and throws the following error:

    error: overriding value a in trait Server of type Int;
    method a needs to be a stable, immutable value
       override def port:Int = scala.util.Random.nextInt()
    

    Laziness

    When it comes to laziness this uniform behavior (treating fields and methods in the same way) is very useful.

    First note that a lazy abstract value does not exist, i.e. you can not define an abstract val as lazy.

    Implementing an abstract def by a lazy val, on the other hand, is perfectly legal and useful. The lazy value will be computed only on the first call and memoized (cached and used for future calls).