Inference

Use type inference where possible, but put clarity first, and favourexplicitness in public APIs.

You should almost never annotate the type of a private field or a localvariable, as their type will usually be immediately evident intheir value:

  1. private val name = "Daniel"

However, you may wish to still display the type where the assigned value has acomplex or non-obvious form.

All public methods should have explicit type annotations. Type inference maybreak encapsulation in these cases, because it depends on internal methodand class details. Without an explicit type, a change to the internalsof a method or val could alter the public API of the class without warning,potentially breaking client code. Explicit type annotations can also helpto improve compile times.

Function Values

Function values support a special case of type inference which is worthcalling out on its own:

  1. val ls: List[String] = ...
  2. ls map (str => str.toInt)

In cases where Scala already knows the type of the function value we aredeclaring, there is no need to annotate the parameters (in this case,str). This is an intensely helpful inference and should be preferredwhenever possible. Note that implicit conversions which operate onfunction values will nullify this inference, forcing the explicitannotation of parameter types.

Annotations

Type annotations should be patterned according to the followingtemplate:

  1. value: Type

This is the style adopted by most of the Scala standard library and allof Martin Odersky’s examples. The space between value and type helps theeye in accurately parsing the syntax. The reason to place the colon atthe end of the value rather than the beginning of the type is to avoidconfusion in cases such as this one:

  1. value :::

This is actually valid Scala, declaring a value to be of type ::.Obviously, the prefix-style annotation colon muddles things greatly.

Ascription

Type ascription is often confused with type annotation, as the syntax inScala is identical. The following are examples of ascription:

  • Nil: List[String]
  • Set(values: _*)
  • "Daniel": AnyRef

Ascription is basically just an up-cast performed at compile-time forthe sake of the type checker. Its use is not common, but it does happenon occasion. The most often seen case of ascription is invoking avarargs method with a single Seq parameter. This is done by ascribingthe _* type (as in the second example above).

Ascription follows the type annotation conventions; a space follows thecolon.

Functions

Function types should be declared with a space between the parametertype, the arrow and the return type:

  1. def foo(f: Int => String) = ...
  2. def bar(f: (Boolean, Double) => List[String]) = ...

Parentheses should be omitted wherever possible (e.g. methods ofarity-1, such as Int => String).

Arity-1

Scala has a special syntax for declaring types for functions of arity-1.For example:

  1. def map[B](f: A => B) = ...

Specifically, the parentheses may be omitted from the parameter type.Thus, we did not declare f to be of type (A) => B, as this wouldhave been needlessly verbose. Consider the more extreme example:

  1. // wrong!
  2. def foo(f: (Int) => (String) => (Boolean) => Double) = ...
  3. // right!
  4. def foo(f: Int => String => Boolean => Double) = ...

By omitting the parentheses, we have saved six whole characters anddramatically improved the readability of the type expression.

Structural Types

Structural types should be declared on a single line if they are lessthan 50 characters in length. Otherwise, they should be split acrossmultiple lines and (usually) assigned to their own type alias:

  1. // wrong!
  2. def foo(a: { def bar(a: Int, b: Int): String; val baz: List[String => String] }) = ...
  3. // right!
  4. private type FooParam = {
  5. val baz: List[String => String]
  6. def bar(a: Int, b: Int): String
  7. }
  8. def foo(a: FooParam) = ...

Simpler structural types (under 50 characters) may be declared and usedinline:

  1. def foo(a: { val bar: String }) = ...

When declaring structural types inline, each member should be separatedby a semi-colon and a single space, the opening brace should befollowed by a space while the closing brace should be preceded by aspace (as demonstrated in both examples above).

Structural types are implemented with reflection at runtime, and areinherently less performant than nominal types. Developers shouldprefer the use of nominal types, unless structural types provide aclear benefit.