What Scala 3 syntax can match on a Type and its Type parameters in the context of a macro?

Viewed 116

From the context of a Scala 3 Macro:

  def aMacroImpl[T](using Type[T], Quotes): Expr[SpecialMap[_, _, _]] = {
    import quotes.reflect._
    val TRepr: TypeRepr = TypeRepr.of[T]
    val TSym: Symbol = TRepr.typeSymbol
    val SrtdMapSym: Symbol = TypeRepr.of[scala.collection.SortedMap].typeSymbol

    TSym.declaredFields.map{ f => TRepr.memberType(f) }.collect {
      case fieldTyepRepr if fieldTyepRepr.derivesFrom(SrtdMapSym) =>
        val fieldType = Inferred(fieldTyepRepr).tpe.asType
        fieldType match {
          ////////////////////////////////////////////////////////////
          case ... => // <-- This line is the subject of the question.
          ////////////////////////////////////////////////////////////
            '{SpecialMap.apply[k, v, sm](/* omitted for brevity*/)}
        }
    }
  }

  // More context.
  object SpecialMap {
    import scala.collection.mutable.Builder
    def apply[K, V, SM <: SortedMap[K, V]](bldr: Builder[(K, V), SM]): SpecialMap[K, V, SM] = {
      SpecialMap[K, V, SM](bldr.result)
    }
  }
  case class SpecialMap[K, V, SM <: SortedMap[K, V]](wrapedMap: SM)

What kind of syntax can replace "..." in the code snippet above, that can match on the given SortedMap type such that it can retrieve all three type parameters: k, v, and sm?

The enquirer has already tried the following:

  1. Incomplete Solutions:
/*a*/  case '[sm] => // provides sm, but not k or v

/*b*/  case '[SortedMap[k, v]] => // provides k and v but not sm
  1. Direct Compiler errors:
/*a*/  case '[sm[k, v]] => // sm$given1.Underlying does not take type parameters

/*b*/  case '[_[k, v]] => // ']' expected, but '[' found 

/*c*/  case 'sm[k, v] => /* symbol literal 'sm is no longer supported, use a string literal
"sm" or an application Symbol("sm") instead, or enclose in braces '{sm} if you want a
quoted expression. For now, you can also `import language.deprecated.symbolLiterals` to
accept the idiom, but this possibility might no longer be available in the future. */

/*d*/
  import language.deprecated.symbolLiterals
  fieldType match {
    case 'sm[k, v] => // '=>' expected, but '[' found

/*e*/
  type SM[k, v] = fieldType match {  // Not found: type fieldType
    case '[sm] => Type[sm]
    case _ => quotes.reflect.report.errorAndAbort("uh oh")
  }

/*h*/
  case '[sm] =>
   type SM[k1, v1] <: SortedMap[k1, v1] = Type[sm] // cannot combine bound and alias

/*f*/
case '[sm] =>
  type SM = Type[sm]
  fieldType match {
    case '[SM[k, v]] => // SM does not take type parameters
  1. Subtle Compiler Errors:
// Examples in this snippet lose the association between k, v, sm, and SortedMap[k,v].

// All yield the following error:
// Type argument sm does not conform to upper bound collection.SortedMap[k, v]

/*a*/
  case '[SortedMap[k, v]] =>
    fieldType match {
      case '[sm] if TypeRepr.of[sm].derivesFrom((TypeRepr.of[SortedMap[k, v]]).typeSymbol) =>
        '{SpecialMap.apply[k, v, sm](/***/)} // ... sm does not conform ...

/*b*/
  fieldType match {
    case '[sm] =>
      fieldType match {
      case '[SortedMap[k, v]] if TypeRepr.of[sm].derivesFrom((TypeRepr.of[SortedMap[k, v]]).typeSymbol) =>
        '{SpecialMap.apply[k, v, sm](/***/)} // ... sm does not conform ...

/*c*/
  (fieldType, fieldType) match {
    case ('[sm], '[SortedMap[k, v]]) =>
      '{SpecialMap.apply[k, v, sm](/***/)} // ... sm does not conform ...

/*d*/
  fieldType match {
    case '[sm1] =>
      type SM[k1,v1] = Type[sm1]
      fieldType match {
        case '[SM[k, v]] =>
          '{SpecialMap.apply[k, v, SM[k, v]](/***/)} // ... SM does not conform ...

/*e*/
  fieldType match {
    case '[sm1] =>
    fieldType match {
      case '[SortedMap[k, v]] =>
        type SM[k,v] = Type[sm1]
        '{SpecialMap.apply[k, v, SM[k, v]](/***/)}  // ... SM does not conform ...

Of course, explicitly writing every known concrete class derived from SortedMap works for any specific project, but libraries should accommodate unknown inheritors of the SortedMap trait.

Is there any way to capture all three type parameters along with their interdependent relationships?

Thank you for your consideration.

1 Answers

I guess you can't guarantee at the compile time of macro that in

val fieldTypeTree = Inferred(fieldTyepRepr).tpe
val fieldType = fieldTypeTree.asType

(fieldType, fieldTypeTree.baseType(SrtdMapSym).asType) match {
  case ('[sm], '[SortedMap[k, v]]) =>
    '{SpecialMap.apply[k, v, sm](???)}
}

k, v, sm satisfy sm <: SortedMap[k, v].

So you can't construct the tree via quotation '{SpecialMap.apply[k, v, sm](???)}, you should construct it manually.

Alternatively you can return '{SpecialMap.apply[k, v, sm & SortedMap[k, v]](???)}.

Related