Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

Don't's

  • Using null in Scala code is not idiomatic at all.

    • private var author:Author = null
  • If you can't give a default value, you really should use

    • var author: Option[Author] = None
  • Don't rebind names for different uses:

    • Use vals

  • Avoid using 's to overload reserved names:

    • typ instead of 'type'

  • Don't prefix getters with get

    • money.count not money.getCount

  • Do not use relative imports from other packages :

     Avoid

          import com.twitter

          import concurrent

    In favor of the unambiguous

          import com.twitter.concurrent
  • The return keyword exits from the enclosing method, not the enclosing function. It behaves like a restricted throw, so don't use return unless it's in an exceptional situation where an API mandates error codes.  In all other situations, prefer Option, Either, case classes, or, if necessary, exceptions.

  • Do not use structural types in normal use.
Code Block
languagescala
private type FooParam = {
 val baz: List[String => String]
 def bar(a: Int, b: Int): String
}
def foo(a: FooParam) = ...

Do's

  • Use short names for small scopes:
    • is,js and ks are all but expected in loops
  • Use longer names for larger scopes:
    • External API's should have longer and explanatory names that confer meaning,  Future.collect not Future.all.
  • Use common abbreviations:
    • Ex: ok, err
  • Use active names for operations with side effects:
    • user.activate() not user.setActive()
  • Use descriptive names for methods that return values:
    • file.isDir not file.dir
  • Use private[this] = visibility to the particular instance. Sometimes it aids performance optimizations.
  • In singleton class types , visibility can be constrained by declaring the returned type:  

Code Block
languagescala
def foo(): Foo with Bar = new Foo with Bar with Baz {
            ...
      }



Traits

  • Traits that contain implementation are not directly usable from Java: extend an abstract class with the trait instead.

Code Block
languagescala
// Not directly usable from Java
        trait Animal {
           def eat(other: Animal)
           def eatMany(animals: Seq[Animal) = animals foreach(eat(_))
         }
// But this is:
abstract class JavaAnimal extends Animal


  • Keep traits short and orthogonal: don’t lump separable functionality into a trait, think of the smallest related ideas that fit together.   

    For example, imagine you have an something that can do IO:

Code Block
languagescala
  trait IOer {
          def write(bytes: Array[Byte])
          def read(n: Int): Array[Byte]
   }

separate the two behaviors:

Code Block
languagescala
  trait Reader {
        def read(n: Int): Array[Byte]
    }
   trait Writer {
       def write(bytes: Array[Byte])
   }

and mix them together to form what was an IOer:

Code Block
languagescala
new Reader with Writer…


Names

Don't repeat names that are already encapsulated in package or object name.
Prefer:

Code Block
languagescala
object User{
	def user(id: Int): Option[User]
}

Instead of:

Code Block
languagescala
object User{
	def getUser(id: Int): Option[User]
}


Pattern Matching

  • Use pattern matching directly in function definitions whenever applicable

          Use this :    

    Code Block
    languagescala
     list map {
                   case Some(x) => x
                   case None => default
              }

    Instead of :

    Code Block
    languagescala
    list map { item =>
                    item match {
                        case Some(x) => x
                        case None => default
                   } }


  • Pattern matching works best when also combined with destructuring

    Use this:

    Code Block
    languagescala
    animal match {
    	case Dog(breed) => "dog (%s)".format(breed)
    	case other => other.species
    }

    Instead of:

    Code Block
    languagescala
     animal match {
            case dog: Dog => "dog (%s)".format(dog.breed)
            case _ => animal.species
         }


Don't use pattern matching for conditional execution when defaults make more sense.

Use:

Code Block
languagescala
val x = list.headOption getOrElse default

NOT:

Code Block
languagescala
 val x = list match {
             case head :: _ => head
             case Nil => default
         }


  • Scala provides an exception facility, but do not use it for commonplace errors, Instead, encode such errors explicitly: using Option

Use: 

Code Block
languagescala
trait Repository[Key, Value] {
          def get(key: Key): Option[Value]
     }

Instead of:

Code Block
languagescala
trait Repository[Key, Value] {
          def get(key: Key): Value
      }
  • Use the following style:    
Code Block
languagescala
/**
     * ServiceBuilder builds services
     * ...
*/


Instead of the standard ScalaDoc style:

Code Block
languagescala
/** ServiceBuilder builds services
     * ...
*/

Imports

  • Sort import lines alphabetically. This makes it easy to examine visually, and is simple to automate.
  • Use braces when importing several names from a package:
    • import com.twitter.concurrent.{Broker, Offer}
  • Use wildcards when more than six names are imported:
    • import com.twitter.concurrent._
  • When using collections, qualify names by importing scala.collection.immutable and/or scala.collection.mutable

    • e.g. "immutable.Map"

  • Put imports at the top of the file. The reader can refer to all imports in one place


Return Types

  • Scala allows return type annotations to be omitted.

  • Return type is especially important for public methods.
  • Return type is especially important when instantiating objects with mixins
    Use:

Code Block
languagescala
def make(): Service = new Service{}

instead of 

Code Block
languagescala
trait Service
def make() = new Service {
  def getId = 123
}

Use the mutable namespace explicitly. Use:

Code Block
languagescala
import scala.collection.mutable
val set = mutable.Set()
  • instead of importing scala.collection.mutable._ and referring to Set.

Require and Assert

  • Require and assert both serve as executable documentation. Both are useful for situations in which the type system cannot express the required invariants. assert is used for invariants that the code assumes (either internal or external), for example: 

Code Block
languagescala
  val stream = getClass.getResourceAsStream("someclassdata")
               assert(stream != null)

Whereas require is used to express API contracts:

Code Block
languagescala
def fib(n: Int) = {
require(n > 0)
  ...
  }

Exception Handling

Some exceptions are fatal and should never be caught; the block of code below is unsafe for this reason:

Code Block
languagescala
  try {
        operation()
      } catch {
          case _ => ...
      }    // almost always wrong, as it would catch fatal errors that need to be propagated.

This can be improved by using scala.util.control.NonFatal extractor to handle only non-fatal exceptions:

Code Block
languagescala
import scala.util.control.NonFatal
...
  try {
      operation()
     } catch {
          case NonFatal(exc) => ...
    }

However, try/catch blocks are low-level control constructs which are not always as expressive or idiomatic as other aspects of Scala. Therefore, better still would be to use scala.util.Try in order to automatically only catch non-fatal errors and to have monadic access patterns for error handling (see https://www.scala-lang.org/api/current/scala/util/Try.html for more details):

Code Block
languagescala
import scala.util.{Try, Success, Failure}
...
  val result: Try[ResultType] = Try( operation() )
  result match {
    case Success(v) => ... //do something with the return value
    case Failure(ex) => ... //non-fatal error handling here
  }

Braces

Use braces for if, while for, etc. even when there's just a single line:

Code Block
scala
scala
if (cond) {
  result
} else {
  otherResult
}

for (x <- y if cond) yield {
  operation(x)
}

while (notDone()) {
  doTheThing()
}

Instead of the standard ScalaDoc style:

Code Block
languagescala
/** ServiceBuilder builds services
     * ...
*/



Collections and Arrays


  • Use the default constructor for the collection type
Code Block
languagescala
 val seq = Seq(1, 2, 3)
 val set = Set(1, 2, 3)
 val map = Map(1 -> "one", 2 -> "two", 3 -> "three")

It is often appropriate to use lower level collections in situations that require better performance or space efficiency.


  • Use arrays  instead of lists for large sequences (the immutable Vector collections provides a referentially transparent interface to arrays); and use buffers instead of direct sequence construction when performance matters.

Code Block
languagescala
for (item <- container) {
    if (item != 2) return
   }

   It is often preferable to call foreach, flatMap, map, and filter directly — but do use fors when they clarify.

Querying and Transforming Data

  • Use map, flatMap, filter and fold
  •      List(1, 2, 3).map(_ * 2)

         .filter(_ > 2)

         .foldLeft(0)(_ + _)   //> res1: Int = 10

Implicits:

Use implicits in the following situations:

  1. Extending or adding a Scala-style collection
  2. Adapting or extending an object (“pimp my library” pattern)
  3. Use to enhance type safety by providing constraint evidence
  4. To provide type evidence (typeclassing)
  5. For Manifests

Do not use implicits to do automatic conversions between similar datatypes (for example, converting a list to a stream); these are better done explicitly because the types have different semantics, and the reader should beware of these implications.  Phrasing your problem in recursive terms often simplifies it, and if the tail call optimization applies (which can be checked by the @tailrec annotation), the compiler will even translate your code into a regular loop.

Aliases

  • Don’t use subclassing when an alias will do.
Code Block
languagescala
trait SocketFactory extends (SocketAddress => Socket

A SocketFactory is a function that produces a Socket.

Using a type alias


Code Block
languagescala
type SocketFactory = SocketAddress => Socket

is better. We may now provide function literals for values of type SocketFactory and also use function composition:

Code Block
languagescala
val addrToInet: SocketAddress => Long
val inetToSocket: Long => Socket
val factory: SocketFactory = addrToInet andThen inetToSocket

Type Aliases

Use type aliases when they provide convenient naming or clarify purpose, but do not alias types that are self-explanatory.

() => Int

   is clearer than

type IntMaker = () => Int

IntMaker

   since it is both short and uses a common type. However,

Code Block
languagescala
class ConcurrentPool[K, V] {
   type Queue = ConcurrentLinkedQueue[V]
   type Map   = ConcurrentHashMap[K, Queue]
 ...
}

 is helpful since it communicates purpose and enhances brevity.


Dont know if this is good or bad - need input here.
  • Fluent” method chaining :  You will often see Scala code that looks like this:

people.sortBy(_.name)

.zipWithIndex

.map { case (person, i) => "%d: %s".format(i + 1, person.name) }

.foreach { println(_) }