Pre-SIP: improve explicit end terminator syntax for Unit return

I can do this:

def performAction(): Unit = {
}

but not this:

def performAction(): Unit = 
end performAction

Methods returning Unit are possibly the single most common type of method and are often the ones which benefit the most from having an explicit end methodName terminator, as their imperative nature often leads to relatively long implementations. I have also found that Unit-returning methods see the hardest use-- frequently having their entire body commented out, and then having lines selectively uncommented as parts are re-enabled one-by-one through experimentation.

I understand it is possible to just put a () Unit value in the body after I comment out all the code, and remove it when I uncomment something, but that still feels like too many steps when I could just use braces and avoid the whole issue instead, and I suspect that is how most others feel also; which is a shame, because it means that a more readable syntax will not be used.

So, my proposal, in short, is to allow methods which return Unit and have an explicit end terminator to have a Unit value inferred when the body is empty-- in other words, make this legal:

def performAction(): Unit = 
end performAction

I’m in favor as this seems very natural

I’m however doubtful about the prevalence of Unit methods in a functional language like Scala

(Although this could change with qualified types

def proof_modus_ponens
  (a: Boolean, b: Boolean)
  (_: Unit with a == true, _: Boolean with (!a || b) == true, ):
  Unit with b == true =
end proof_modus_ponens

From experience with Stainless, it often happens that you start with a lot of assertions to guide the proof, only to remove most of them that didn’t end up being required
Potentially ending with nothing at all, like in the above
)

I mean, that depends enormously on project standards. I tend to consider Unit-returning methods as a bad smell (typically a PR blocker), but I believe there are still plenty of Java-in-Scala projects out there in enterprise land, with relatively imperative coding styles.

While I suspect that it’s not true that Unit-returning methods are the most common in the grand scheme of things, I would also bet that there are plenty of companies where that’s true.

So while I’d probably never use this suggestion myself, I agree that it’s probably a good idea. (I’m a little surprised it doesn’t already work.)

There exist environments where the unit return method is natural, for example, when a developer wants to check something and throw an exception if preconditions are not met.

1 Like

It’s very much a question of how the project ticks. In the monadic pure-FP world I focus on nowadays (Typelevel, ZIO, etc) I would usually advise against that, even in development, because it’s easy to misunderstand how the timing functions and get yourself confused. (And it should never happen in production pathways, IMO.) Returning IO[Unit] is common, but plain Unit is generally to be avoided.

But yes, it’s not unusual in a Lightbend-styled project, and bog-standard in a more classically OO one. As always, the Scala world contains multitudes…

The 6th grader’s code is procedural on their best days. (“What’s Unit?” Later: “Why is it () when it could be Unit.unit?” I’m going to try to get them to build their own HOF for feedback loops this year.)

I’m not sure that labeled end helps make them better coders. I’ll ponder that next time I’m idle.

I would like thank everyone for their feedback so far. I probably should have worded my original post more carefully by not stating that methods returning Unit are possibly the single most common type of method: because as others have rightly pointed out, that’s entirely a matter of the codebase.

I am going to let this Pre-SIP run for a couple of weeks so there is a reasonable time window for the community to comment before proceeding to the next steps in the SIP process.

In the meantime, I do have a request to make for those that have already commented and for future commentors, so that I do not accidentally misrepresent the support (or lack thereof) this Pre-SIP has in future documentation:

  • Could everyone who wishes to register their support for this feature-- exactly as proposed-- put a “Vote: YES” at the top of a reply to this thread, and everyone who wishes to register their opposition to this feature as proposed put a “Vote: NO” at the top of a reply to this thread? Should you wish to change your vote at any time please edit the vote post to avoid accidental double-counting.
  • Continued discussion after the Vote line and without the Vote line is of course still welcomed and encouraged.

Thank you.

There was a ticket.

with links to related syntax.

The gist is that the end marker is optional.

Thank you @som-snytt for that background information. I would also draw attention to your comment in another github issue which provides more clarity on end marker syntax: Unable to define an empty trait with indentation-based syntax · Issue #10080 · scala/scala3 · GitHub .

@odersky commented that this should not be allowed in response to a query asking if it should, but his reasoning was grounded in the philosophy of the grammar where explicit end terminators are optional and the program must still compile without them. The intention of this Pre-SIP is to appeal to Scala’s history of pragmatism and a general argument of developer ergonomics to argue that an exception should be carved out of the grammar for this one case.

We’ll see what is said later in the process, but comments like yours show the value of the Pre-SIP stage.