End markers were one of the new things that came with indentation syntax in Scala 3. They were largely unknown territory, so until now there are no hard rules where to put them. Here’s what the docs said:
An end marker makes sense if
- the construct contains blank lines, or
- the construct is long, say 15-20 lines or more,
- the construct ends heavily indented, say 4 indentation levels or more.
Now, with one year of experience, I find the first point is by far the most important. An end marker “feels right” if the definition it closes contains blank lines. In that case, leaving out the end marker seems almost always awkward. So I would be ready to recommend to make that a rule that can be enforced by linters and style tools. By contrast, the second and third points feel much more situational.
This brings me to another point. An end marker for definitions has to repeat the definition name. That helps to sync the programmer’s and the compiler’s understanding what is closed, and it is great reading aid for long classes and methods that span a page or more. But it can get in the way for shorter definitions. Example:
def longwindedlyNamedMethod =
// an inner method
def recur(x: Int, y: Int) =
if x == 0 then y else recur(x - 1, x * y)
recur(1, 1)
end longwindedlyNamedMethod
Here, I think the second use of longwindedlyNamedMethod
is overkill; it is obvious what is closed so the name following the end
just adds clutter. I have found myself hesitating whether to put an end marker in situations like this. Sometimes I decided to drop the blank lines instead. So, maybe we need a third option. We could be a but more flexible and allow end markers closing definitions to come without repeating the defined name. I.e. like this:
def longwindedlyNamedMethod =
// an inner method
def recur(x: Int, y: Int) =
if x == 0 then y else recur(x - 1, x * y)
recur(1, 1)
end
An end marker that is not followed by anything must close a definition that starts on the same column; it cannot close an expression.
If that choice is available, I think I could reformulate the recommendations when to use end markers as follows:
- always use an end marker if the closing definition contains blank lines
- the end marker should be followed by an identifier if the closing definition is longer than ~20 lines.
What do people think about these choices?