Scala Conventions


Basics

These rules apply strictly only to mmt-api. In other subprojects, especially those contributed by other programmers, the personal style of the subproject leader or the individual programmer may be chosen instead.

Nobody should change styling of other programmers’ code if it is only a matter of personal style.

Some of the following conventions may violate default settings in IDEs. Configure your IDEs accordingly and do not change styling recklessly.

Indentation

All indentation must be by spaces (no tabs).

The depth of indentation is not fixed. It is typically 2-4 spaces. It does not have to be consistent throughtout a file.

Imports

In the presence of info.kwarc.mmt.api._ write import XXX instead of import info.kwarc.mmt.api.XXX

Method types

Declare methods without arguments without brackets: def f: A instead of def f(): A

Declare non-abstract methods without return type as def f {…} instead of def f : Unit = {…}

Robustness against API changes

The MMT API changes frequently. Therefore, it is good to use a few tricks for more robust code, i.e., code that remains correct when the API changes. Several such tricks can be identified.

Bundling multiple arguments into a case class

A major component that takes a bunch of parameters for modifying its behavior should not list these individually. Instead, a new case class should be introduced to hold those arguments.

That way we can later add a parameter to the case class without having to adapt all methods.

Examples: the main interface methods for

  • parsing (case class: ParsingUnit and ParsingStream)
  • checking (case class: CheckingUnit and CheckingEnvironment)
  • building (case class: BuildTask)
  • etc.

Keeping the set of fields of a case class flexible

When a small change to an object is needed, a copy method can be used. This are auto-generated by Scala for all case classes.

Example for case class C(a: A, b: B); val c: C: use c.copy(a=a’) instead of C(a’, c.b).

Similarly, when pattern-matching, it is preferable to use case c:C => instead of case C(,) =>.

That way the code remains correct if the case class changes.

Robustness against semantic errors

Use default arguments carefully

When adding a parameter to a class or function, do not give it a default value. Doing so masks errors in all previous calls to the function/class: many of those should not use the default value. The only way to find out is to force a typing error due to the missing argument.

Instead, it is better to use default arguments in convenience methods in the companion object. (These should have a different signature than the primary constructor to avoid accidentally well-typed code.)