简体   繁体   中英

Scala 3 macro: generic type from class

I am writing a serialization library using Scala 3 macro programming. I want to generate the serializer dynamicly. Because this is a library, so I do not know what class to serializer during compile. So I need a runtime compile feature.

Code:

object CodecMacro {
    import scala.quoted.staging.*
    given Compiler = Compiler.make(getClass.getClassLoader)
    
    // function 1: entry point: a Class object
    def buildSerializer(clazz: Class[_]): Serializer = {
        given staging.Compiler = staging.Compiler.make(getClass.getClassLoader)
        val fn = (qctx: Quotes) ?=> {
            given q: Quotes = qctx
            buildSerializerAdapter(clazz)
        }
        staging.run(fn)
    }
    
    // function 2: convert clazz to generic T
    // My main question is here
    private def buildSerializerAdapter(clazz: Class[_])(using quotes: Quotes): Expr[Serializer] = {
        import quotes.reflect.*
        val tpe: TypeRepr = TypeRepr.typeConstructorOf(clazz)
        val t = tpe.asType
        type T = t.Underlying
        buildSerializerImpl[T]  // error: Missing type parameters for T
    }
    
    // function 3: Using a generic T to generate a Serializer
    private def buildSerializerImpl[T: Type](using quotes: Quotes): Expr[Serializer] = {
        val t = summon[Type[T]]
        '{
            type T = t.Underlying
            new Serializer[T] {
            override def serialize(writer: Writer, value: T): Unit = {
            // Implemetation, not important
            writer.writeString("aaa")
        }
    }
}  

Function 1 is the entry point, which needs a Class object. Function 3 is the final implementation, which needs a generic type T.

Function 2 is to convert from Class object to generic type T and this is my main question.

My solution is

    Class ->  TypeRepr  use: TypeRepr.typeConstructorOf(clazz)
    TypeRepr -> Type    use: TypeRepr.asType
    Type -> type T      use: Type.Underlying

I thought the type T under Type will solve my problem, but the compiler gives me an error: "Missing type parameters for T"

In Function 3: type T = t.Underlying value: T
The generic type T is working fine.

Is there any way to convert Class object to generic type T?

Here is the way it should be done:

        tpe.asType  match
            case '[t] =>
                buildSerializerImpl[t]

This very strange invocation comes straight from the documentation of asType . It is necessary because asType returns a Type[?] where Type accepts any type kind, but here we want a concrete type kind. The match would fail if the type was of higher kind, such as a Type[List] (the List constructor takes type arguments).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM