Disambiguating Runnable and Callable

ScheduledExecutorService.html#schedule has overloads that take a Runnable or a Callable<V>. Java seems to be able to disambiguate these without issue. I suppose it checks if your lambda has a return value (non-void) and then uses Callable, otherwise Runnable. With Scala I have to wrap my lambda in brackets and add : Runnable. Is there something more convenient I could do?

Supply an extension, perhaps?

extension (execService: ScheduledExecutorService)
  def scheduleUnit(
      command: Runnable,
      delay: Long,
      unit: TimeUnit
  ): ScheduledFuture[?] =
    execService.schedule(command, delay, unit)

val execService: ScheduledExecutorService = ???
execService.scheduleUnit(() => (), 42L, TimeUnit.SECONDS)

IDK, but JLS 15.12.2.1 makes the distinction between what is “potentially applicable”.

  • If the function type of T has a void return, then the lambda body is either a
    statement expression (§14.8) or a void-compatible block (§15.27.2).
  • If the function type of T has a (non-void) return type, then the lambda body is
    either an expression or a value-compatible block (§15.27.2).

That section §15.27.2 defines:

  • A block lambda body is void-compatible if every return statement in the block has
    the form return;.
  • A block lambda body is value-compatible if it cannot complete normally (§14.22)
    and every return statement in the block has the form return Expression;.

The section §14.22 is a bunch of rote rules which I do not care to indulge. It says normal completion means reachable.