Search code examples
scalaimplicitdottyscala-3

Syntax for named given/using objects in Scala 3 (Dotty)


Is there a one-line way to provide a named reference to a value which is to be implicitly available (i.e. available with using syntax) without the soon-to-be-deprecated implicit keyword? According to the docs, I'd expect the following to work (under SBT, scalaVersion := "3.0.0-M2":

trait Greeter {
  def sayHello(username: String): Unit
  def shutdown(): Unit
}

def greet(username: String)(using g: Greeter): Unit = {
  g.sayHello(username)
}

object Foo extends App {
  given greeter: Greeter = new Greeter {
    override def sayHello(username: String): Unit = println(s"Hello, ${username}!")

    override def shutdown(): Unit = println("Shutting down")
  }
  
  try {
    greet("world")
  } finally {
    greeter.shutdown()
  }
}

but this fails with

[error] -- Error: /path/to/project/src/main/scala/Foo.scala:12:15 
[error] 12 |  given greeter: Greeter = new Greeter {
[error]    |               ^
[error]    |               end of statement expected but ':' found
[error] -- [E040] Syntax Error: /path/to/project/src/main/scala/Foo.scala:12:17 
[error] 12 |  given greeter: Greeter = new Greeter {
[error]    |                 ^^^^^^^
[error]    |                 ';' expected, but identifier found

Now: I can work around this in many ways, but either the docs are confusing (or wrong) or I'm misunderstanding something rather fundamental.

Workaround 1 (as suggested here):

lazy val greeter: Greeter = new Greeter {
  override def sayHello(username: String): Unit = println(s"Hello, ${username}!")
  override def shutdown(): Unit = println("Shutting down")
}
given Greeter = greeter
...

but I'd like to be able to do this in a single expression rather than two. The same comment applies to defining the given first and then binding it to a name:

given Greeter with {
  override def sayHello(username: String): Unit = println(s"Hello, ${username}!")

  override def shutdown(): Unit = println("Shutting down")
}
val greeter: Greeter = implicitly

especially since I think implicitly will be deprecated in 3.1 and gone in 3.2.

We could also wrap our invocations of the given object in functions to get around this:

def greet(username: String)(using g: Greeter): Unit = {
  g.sayHello(username)
}

def shutdown(using greeter: Greeter): Unit = {
  greeter.shutdown()
}  

given Greeter with {
  override def sayHello(username: String): Unit = println(s"Hello, ${username}!")

  override def shutdown(): Unit = println("Shutting down")
}

try {
  greet("world")
} finally {
  shutdown
}

but that seems boilerplateful to me.


Solution

  • Turns out that the

    given x: T = ...
    

    syntax was added between revision M2 and revision RC1. The following works with scalaVersion := "3.0.0-RC1":

    trait Greeter {
      def sayHello(username: String): Unit
      def shutdown(): Unit
    }
    
    def greet(username: String)(using g: Greeter): Unit = {
      g.sayHello(username)
    }
    
    object Foo extends App {
      given greeter: Greeter with {
        override def sayHello(username: String): Unit = println(s"Hello, ${username}!")
      
        override def shutdown(): Unit = println("Shutting down")
      }
      
      try {
        greet("world")
      } finally {
        greeter.shutdown()
      }
    }