Skip to content

Mirror.ProductOf instance for Value class #17800

Closed
@cchantep

Description

@cchantep

For now, Value classes are not provided Mirror.ProductOf instances.

Something as below could be used.

  transparent inline def valueClassProduct[
      T <: AnyVal
    ]: Mirror.Product with Mirror.ProductOf[T] = ${ valueClassProductImpl[T] }

  private def valueClassProductImpl[T: Type](
      using
      q: Quotes
    ): Expr[Mirror.Product with Mirror.ProductOf[T]] = {
    import q.reflect.*

    val tpr = TypeRepr.of[T]

    if (defn.ScalaPrimitiveValueClasses.contains(tpr.typeSymbol)) {
      report.errorAndAbort(s"Cannot derive primitive value class: ${tpr.show}")
    }

    // ---

    val tprSym = tpr.typeSymbol
    val ctor = tprSym.primaryConstructor

    type IsString[U <: String] = U

    ctor.paramSymss match {
      case List(v: Symbol) :: Nil =>
        v.tree match {
          case vd: ValDef => {
            (
              ConstantType(StringConstant(tprSym.name)).asType,
              vd.tpt.tpe.asType,
              ConstantType(StringConstant(v.name)).asType
            ) match {
              case (
                    '[IsString[label]],
                    '[fieldType],
                    '[IsString[fieldLabel]]
                  ) =>
                type ProductOfValue = Mirror.ProductOf[T] {
                  type MirroredType = T
                  type MirroredMonoType = T

                  type MirroredLabel = label
                  type MirroredElemTypes = Tuple1[fieldType]
                  type MirroredElemLabels = Tuple1[fieldLabel]
                }

                '{
                  (new Mirror.Product {
                    type MirroredType = T
                    type MirroredMonoType = T

                    type MirroredLabel = label
                    type MirroredElemTypes = Tuple1[fieldType]
                    type MirroredElemLabels = Tuple1[fieldLabel]

                    def fromProduct(p: Product): MirroredMonoType = ???
                  }): ProductOfValue
                }
            }
          }

          case term =>
            report.errorAndAbort(
              s"Value class is expected to have a single field: ${term.show}"
            )
        }

      case args =>
        report.errorAndAbort(
          s"Value class is expected to have a single field: ${args mkString ", "}"
        )
    }
  }

Usage:

final class Bar(val value: String) extends AnyVal

valueClassProduct[Bar]
// defined class Bar
val res0: 
  deriving.Mirror.ProductOf[Bar]{
    MirroredType = Bar; MirroredMonoType = Bar; MirroredLabel = "Bar"; 
      MirroredElemTypes = Tuple1[String]
    ; MirroredElemLabels = Tuple1["value"]
  } = anon$1@4b8832f9

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