Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mirror.ProductOf instance for Value class #17800

Closed
cchantep opened this issue Dec 7, 2021 · 6 comments
Closed

Mirror.ProductOf instance for Value class #17800

cchantep opened this issue Dec 7, 2021 · 6 comments

Comments

@cchantep
Copy link
Contributor

cchantep commented Dec 7, 2021

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
@Lasering
Copy link

Instead of using value classes why not use opaque types?

@cchantep
Copy link
Contributor Author

Instead of using value classes why not use opaque types?

Both are supported, so derivation should be available for both.

@Lasering
Copy link

Yes it should be available for both. I was not trying to say otherwise.

Opaque types have the guarantee of no overhead, value classes don't because some times there is boxing involved. The way I see it is: you will eventually have to migrate to opaque types why not migrate right now and benefit from no overhead and support for Mirrors?

@cchantep
Copy link
Contributor Author

This rationale of asking people to migrate all value classes to opaque types could make sense in the end ... Could ... But not sure ... Otherwise value classes would have been removed or at least been deprecated.

Anyway, that's not a good strategy to me to ask to migrate all the value classes whereas that's quite easy to have derivation for.

@Lasering
Copy link

From Scala 3 Overview

Value classes (superseded by opaque type aliases) are a special case. There are currently no deprecation plans for value classes, since we might bring them back in a more general form if they are supported natively by the JVM as is planned by project Valhalla.

@SethTisue
Copy link
Member

Value classes also remain relevant when cross-building for Scala 2 and 3.

@ckipp01 ckipp01 transferred this issue from lampepfl/dotty-feature-requests Jun 5, 2023
@scala scala locked and limited conversation to collaborators Jun 5, 2023
@ckipp01 ckipp01 converted this issue into discussion #17801 Jun 5, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants