Skip to content

Start thinking about Scala 3 #894

Open
@cb372

Description

Scala 3 is just around the corner ("late 2020"). The macro annotation will not work, so we need to find a new way to generate servers and clients from service definitions.

I played around with Scala 3's generic derivation feature and macros today, and I think it might work for our use case. I couldn't get my code to compile (documentation is almost non-existent!) but I think it can work in theory.

My idea was to define type classes in Mu containing the various server/client factory methods, e.g.

trait RPCClientSide[S[_[_]]] {
  def unsafeClient[F[_]: Async](channelFor: ChannelFor, serializationType: SerializationType): S[F]
}

trait RPCServerSide[S[_[_]]] {
  // serialization type, compression type, other options would also be passed as arguments here
  def bindService[F[_]: Async](service: S[F]): F[ServerServiceDefinition]
}

and implement derived methods for each of them using macros and TASTy reflection:

import scala.quoted._

object RPCClientSide {

  given gen[S[_[_]]: Type](using qctx: QuoteContext) as Expr[RPCClientSide[S]] = {
    import qctx.tasty._

    '{
      new RPCClientSide[S] {
        def unsafeClient[F[_]: Async](channelFor: ChannelFor, serializationType: SerializationType): S[F] =
          throw new Exception("TODO generate the client implementation")
      }
    }
  }

  implicit inline def derived[S[_[_]]]: RPCClient[S] = ${ RPCClient.gen[S] }

}

Then the service definition trait would have a derives clause instead of a @service annotation:

trait MyService[F[_]] derives RPCServerSide, RPCClientSide {
  def sayHello(req: HelloRequest): F[HelloResponse]
}

(We don't have to split the client and server side into separate type classes, it's just an idea.)

The derived type class instance can be summoned:

val channelFor = ...
val instance = summon[RPCClientSide[MyService]]
val client = instance.unsafeClient[IO](channelFor, Protobuf)
client.sayHello(HelloRequest("Chris"))

Another option is to do two stages of source code generation, as explored in #632. But that POC is based on a Scala 2 compiler plugin, so it would need to be rewritten.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions