Type class derivation in Scala 3
Chris Birchall
47 Degrees
Agenda
- Type class derivation in Scala 2
- In Haskell and Rust
- New features in Scala 3
- Show
Functor- Macros: generating gRPC clients and servers
Status quo in Scala 2
- Implementation choices
- Magnolia
- Shapeless
- Hand-rolled macros
- A few standard usage patterns
- define in TC's companion object
-
import io.circe.generic.auto._
-
import io.circe.generic.semiauto._
Haskell
- `deriving` keyword to generate TC instances
- Language extensions galore
- DeriveFunctor, DeriveFoldable, DeriveTraversable
- DeriveGeneric
- DeriveAnyClass
- DeriveDataTypeable
- DerivingStrategies
- Sky's the limit with DerivingVia
- Print derived instances with -ddump-deriv
Rust
- #[derive(...)] attribute
- Auto-derivation of stock traits (e.g. Eq)
- Derive custom traits using procedural macros
New features in Scala 3
- derives keyword
- Mirror
Live coding 😅
gRPC client/server generation
- Feature of Mu, a microservices framework
- Currently implemented as a macro annotation
gRPC client/server generation
(in Scala 2)
message HelloRequest {
string name = 1;
}
message HelloResponse {
string name = 1;
}
service Greeter {
rpc SayHello(HelloRequest)
returns (HelloResponse);
}
greeter.proto
case class HelloRequest(name: String)
case class HelloResponse(greeting: String)
@service(Protobuf)
trait Greeter[F[_]] {
def SayHello(req: HelloRequest): F[HelloResponse]
}
Greeter.scala
- srcgen using sbt plugin
- write by hand
gRPC client/server generation
(in Scala 2)
@service(Protobuf)
trait Greeter[F[_]] {
def SayHello(req: HelloRequest): F[HelloResponse]
}
// Generated by macro annotation
object Greeter {
def toServerServiceDefinition[F: ConcurrentEffect](
service: Greeter[F]
): io.grpc.ServerServiceDefinition
def client[F: ConcurrentEffect](
host: String, port: Int
): Resource[F, Greeter[F]]
}
gRPC client/server generation
(in Scala 2)
Macro takes care of
- mapping methods to RPC endpoints
- Protobuf marshalling of requests/responses
gRPC client/server generation
Plan for Scala 3
- move server/client factory methods to type classes
- derive instances using macros
trait GrpcServerSide[S[_[_]]] {
def [F[_]: ConcurrentEffect] (service: S[F])
.toServerServiceDefinition: io.grpc.ServerServiceDefinition
}
trait GrpcClientSide[S[_[_]]] {
def client[F: ConcurrentEffect](
host: String, port: Int
): Resource[F, S[F]]
}
gRPC client/server generation
(in Scala 3)
message HelloRequest {
string name = 1;
}
message HelloResponse {
string name = 1;
}
service Greeter {
rpc SayHello(HelloRequest)
returns (HelloResponse);
}
greeter.proto
case class HelloRequest(name: String)
case class HelloResponse(greeting: String)
@service(Protobuf)
trait Greeter[F[_]] derives GrpcServerSide, GrpcClientSide {
def SayHello(req: HelloRequest): F[HelloResponse]
}
Greeter.scala
- srcgen using sbt plugin
- write by hand
gRPC client/server generation
(in Scala 3)
class MyGreeter[F[_]: Applicative] extends Greeter[F] {
def SayHello(req: HelloRequest): F[HelloResponse] =
HelloResponse(s"Hi, ${req.name}!").pure
}
val serverServiceDef =
new MyGreeter[IO].toServerServiceDefinition
val client =
summon[GrpcClientSide[Greeter]].client[IO]("localhost", 8080)
Summary
-
`derives` clause + `derived` method
- Extension methods
- `using` and `given`
- `inline`
- Derivation using mirrors
- Derivation using a macro
Type class derivation in Scala 3
By Chris Birchall
Type class derivation in Scala 3
- 3,238