Description
Compiler version
3.3.0
Minimized code
A simple example of a code that contains behavior unexpected by the user, caused by implementation detail of boundary/break:
//> using scala 3.3
import java.net.http.HttpTimeoutException
import scala.util.boundary
import boundary.break
object Main extends App:
boundary:
while true do
try
makeRequest()
break()
catch
case _: Exception => //just retry
println("Finish")
var c = 0
def makeRequest() =
println("Making request")
if c < 3 then
c = c + 1
throw new HttpTimeoutException("timeout")
This loop would never finish, as it would first catch the real timeout exception and then all the Break
exceptions. It makes perfect sense it works that way, given how the boundary/break is implemented. However, catching all the Exceptions on retries when, e.g., waiting for a service to start responding, is a widespread pattern. Scala users writing simple programs like this must be aware of this implementation detail.
What's more - that's a basic example. Along the way, library maintainers will use the boundary/break in their libraries. Given an additional layer over this mechanism, the leaking implementation detail will be even more misleading.
To solve the antipattern of catching Exception
, we offer the NonFatal
with unapply. However, Break will be caught as NonFatal by the users, so it won't help as well. It may even cause more confusion, as the previous implementation detail of breaking (the ControlThrowable
) was not matched by NonFatal and was sometimes used as a solution.
Proposal
After talking about it, we propose that the compiler could report an error if there is a Label in the implicit scope and the Break is caught indirectly (i.e. by matching Exception). It would prevent the users from experiencing the leakage of implementation detail and the possible confusion when their code does not work as expected. Nevertheless, I would like to hear some feedback on that.
Update: Problem with the solution we proposed is that it would not solve the problem of runtime matching in unapplies (so, for example, NonFatal)