Please read the Scala Style Guide carefully. The main points to consider are:
-
Use two-space indentation. No tabs.
-
Omit unnecessary blocks to reduce excessive nesting:
if (condition) { bad() } if (condition) good()
-
Avoid wrapping lines. Split long lines into multiple expressions, assigning intermediate results to
val
s. When inevitable, indent wrapped lines with two spaces:val iAm = aVeryLong + expression + thatTakes + multipleLines
-
Use lower camel case for
valName
,varName
,methodName
,functionObject
,packageObject
, andannotationName
. -
Use upper camel case for
ConstantName
,EnumerationValue
,ClassName
,TraitName
,ObjectName
,TypeParameter
. -
No
UPPERCASE_UNDERSCORE
, not even for constants or type parameters. -
No
get
/set
prefixes for accessors and mutators. -
Always use empty parentheses when, and only when, declaring or calling methods with side-effects.
-
Unless an established convention already exists (e.g, mathematical symbols), avoid symbolic method names ("operators").
-
Use type inference where possible. But put clarity first and favor explicitness when an inferred type may not be obvious.
val good = 1 // Int, obviously val bad = config.get("key") // What does it return? val better: Option[String] = config.get("key")
-
Opening curly braces (
{
) must be on the same line as the declaration. -
Constructors should be declared all on one line. If not possible, put each constructor argument on its own line, indented four spaces.
-
Extensions follow the same rule above, but indent two spaces to provide visual separation between constructor arguments and extensions:
class Platypus( name: String, age: Int) extends Beaver with Duck
-
Favor short, single-expression, single-line method bodies.
-
No procedure syntax.
def bad() { ??? } def worse { ??? } def good(): Unit = { ??? }
-
Postfix operator notation is unsafe and shall not be used. Consider it deprecated. Never import
scala.language.postfix
!val bad = seq mkString val good = seq.mkString
-
Always use infix notation for methods with symbolic names or higher-order functions (
map
,foreach
, etc.).val bad = seq.map(_ * 2) val good = seq map (_ * 2)
When the syntax does not allow it, stick to traditional method invocation syntax:
val bad = (seq map f).toSet // Starts to read like LISP val good = seq.map(f).toSet
-
Text file format: UTF-8, no BOM, Unix line endings (LF, '\n'), newline at EOF. Use the
.editorconfig
(http://editorconfig.org/) profile inside theskeleton
folder to configure your editor. -
100 characters maximum line length.
-
One blank line between method, class, and object definitions.
-
Use blank lines between statements and declarations to create logical groupings.
-
No double blank lines, anywhere.
-
No trailing whitespace at the end of lines, they cause problems when diffing between files or between versions. Configure your text editor to do this for you automatically.
-
In general, obey English rules and mathematical conventions for punctuation:
-
A single space after and no space before
,
,:
,;
,)
, etc. -
A single space before
(
, except for method invocation or declaration. Never a space after. -
Single spaces around
=
,+
,-
,*
,{
,}
,=>
,<-
, etc. -
No spaces between consecutive
(
or)
.
-
-
Do not align vertically: it requires constant realignments, making diffs longer.
-
Methods must always have explicit return types. Explicitly declaring the return type allows the compiler to verify correctness.
-
Modifiers should be declared in the following order:
override
,abstract
,private
orprotected
,final
,sealed
,implicit
,lazy
. -
Put imports at the top of the file. Imports should be grouped from most to least specific:
- The project's own classes
- Frameworks and libraries:
com.*
,net.*
,org.*
, etc. scala.*
java.*
andjavax.*
Inside each group, packages and classes must be sorted alphabetically. Separate groups with blank lines:
import myapp.util.StringUtils import org.joda.time.DateTime import play.api.Configuration import scala.concurrent.Future import java.net.URI
The script
fix-imports.go
in the skeleton project folder can be used to help you organize your imports. -
Do not use relative imports. Full imports are easier to search, and never ambiguous:
package foo.bar // Bad import baz.Qux // Good import foo.bar.baz.Qux
-
Do not wildcard-import entire packages:
import bad._
-
The bigger the scope, the more descriptive the name. Only for very small, local scopes may single-letter mnemonics be used.
-
Use
_
for simple, single line functions:val bad = seq filter (number => number % 2 == 0) val good = seq filter (_ % 2 == 0)
-
Omit the
_
for functions that take a single argument:bad foreach println(_) good foreach println
-
The formatting rules for constructors (four-space double indent) also apply to method declarations:
def delta( a: Int, b: Int, c: Int): Int = { b * b - 4 * a * c }
-
Use infix notation for single argument methods on monadic types (
contains
,getOrElse
, etc.) -
For single-line functions and for-comprehensions, use parentheses. For multiline ones, use brackets:
for (i <- 1 to 3) println(i) seq map (_ * 2) seq map { a => if (a < 0) -a else a }
-
Whenever possible, simplify pattern matching expressions by omitting the
match
keyword and using partial functions:bad map { _ match { case 1 => "one" case _ => "not one" } } good map { case 1 => "one" case _ => "not one" }
-
When passing functions, do not use inner block syntax:
(bad => { ??? }) { good => ??? }
-
For documentation comments, use Javadoc left-hand margin style instead of the Scaladoc convention:
/** Bad * convention */ /** * Good * convention */
-
Optimize for readability. Readability trumps consistency (but not by much). Consistency trumps everything else.