21
votes

I'd like this thread to be some kind of summary of pros/cons for overriding and calling toString with or without empty parentheses, because this thing still confuses me sometimes, even though I've been into Scala for quite a while.

So which one is preferable over another? Comments from Scala geeks, officials and OCD paranoids are highly appreciated.

Pros to toString:

  • seems to be an obvious and natural choice at the first glance;
  • most cases are trivial and just construct Strings on the fly without ever modifying internal state;
  • another common case is to delegate method call to the wrapped abstraction:

    override def toString = underlying.toString
    

Pros to toString():

  • definitely not "accessor-like" name (that's how IntelliJ IDEA inspector complains every once in a while);
  • might imply some CPU or I/O work (in cases where counting every System.arrayCopy call is crucial to performance);
  • even might imply some mutable state changing (consider an example when first toString call is expensive, so it is cached internally to yield quicker calls in future).

So what's the best practice? Am I still missing something?

Update: this question is related specifically to toString which is defined on every JVM object, so I was hoping to find the best practice, if it ever exists.

4
Edited to explain, why it is not.BorisOkunskiy

4 Answers

22
votes

Here's what Programming In Scala (section 10.3) has to say:

The recommended convention is to use a parameterless method whenever there are no parameters and the method accesses mutable state only by reading fields of the containing object (in particular, it does not change mutable state). This convention supports the uniform access principle,1 which says that client code should not be affected by a decision to implement an attribute as a field or method.

Here's what the (unofficial) Scala Style Guide (page 18) has to say:

Scala allows the omission of parentheses on methods of arity-0 (no arguments):

reply() 
// is the same as 
reply 

However, this syntax should only be used when the method in question has no side-effects (purely-functional). In other words, it would be acceptable to omit parentheses when calling queue.size, but not when calling println(). This convention mirrors the method declaration convention given above.

The latter does not mention the Uniform Access Principle.

If your toString method can be implemented as a val, it implies the field is immutable. If, however, your class is mutable, toString might not always yield the same result (e.g. for StringBuffer). So Programming In Scala implies that we should use toString() in two different situations:

1) When its value is mutable

2) When there are side-effects

Personally I think it's more common and more consistent to ignore the first of these. In practice toString will almost never have side-effects. So (unless it does), always use toString and ignore the Uniform Access Principle (following the Style Guide): keep parentheses to denote side-effects, rather than mutability.

9
votes

Yes, you are missing something: Semantics.

If you have a method that simply gives back a value, you shouldn't use parens. The reason is that this blurs the line between vals and defs, satisfying the Uniform Access Principle. E.g. consider the size method for collections. For fixed-sized vectors or arrays this can be just a val, other collections may need to calculate it.

The usage of empty parens should be limited to methods which perform some kind of side effect, e.g. println(), or a method that increases an internal counter, or a method that resets a connection etc.

3
votes

I would recommend always using toString. Regarding your third "pro" to toString():

Might imply some mutable state changing (consider an example when first toString call is expensive, so it is cached internally to yield quicker calls in future).

First of all, toString generally shouldn't be an expensive operation. But suppose it is expensive, and suppose you do choose to cache the result internally. Even in that case, I'd say use toString, as long as the result of toString is always the same for a given state of the object (disregarding the state of the toString cache).

The only reason I would not recommend using toString without parens is if you have a code profiler/analyzer that makes assumptions based on the presence or absence of parens. In that case, follow the conventions set forth by said profiler. Also, if your toString is that complicated, consider renaming it to something else, like expensiveToString. It is unofficially expected that toString be a straightforward, simple function in most cases.

2
votes

Not much argumentation in this answer but GenTraversableOnce alone declares the following defs without parentheses:

toArray
toBuffer
toIndexedSeq
toIterable
toIterator
toList
toMap
toSeq
toSet
toStream
toTraversable