All Downloads are FREE. Search and download functionalities are using the official Maven repository.

perspective.derivation.exprHkdGeneric.scala Maven / Gradle / Ivy

The newest version!
package perspective.derivation

import scala.language.implicitConversions

import scala.compiletime.constValue
import scala.deriving.Mirror
import scala.quoted.{Expr, Quotes, Type}

import cats.data.Validated
import cats.kernel.{BoundedEnumerable, Order}
import cats.{Applicative, FlatMap, Foldable, Functor, Monad}
import perspective.*

sealed trait ExprHKDGeneric[A] extends GenHKDGeneric[A]:

  def genType: Type[Gen]

  def types: Gen[Type]

  def summonInstancesOpt[F[_]: Type]: Option[Gen[Compose2[Expr, F]]]

  def summonInstances[F[_]: Type]: Gen[Compose2[Expr, F]]

  extension [B[_]: Type](gen: Gen[B]) def traverseKExprId[D[_]: Type](f: B :~>: Compose2[Expr, D]): Expr[Gen[D]]

  def tabulateTraverseKExprId[B[_]: Type](f: Index :~>: Compose2[Expr, B]): Expr[Gen[B]]

  def tabulateTraverseKExpr[B[_]: Type, D[_]: Type](
      f: Index :~>: Compose3[Expr, B, D],
      BAppExpr: Expr[Applicative[B]]
  ): Expr[B[Gen[D]]]

object ExprHKDGeneric:
  def tabulateTraverseKExprId[ElemTypes <: Tuple: Type, B[_]: Type](using typeLength: TypeLength[ElemTypes])(
      f: Finite[typeLength.Length] :#~>: Compose2[Expr, B]
  )(
      using q: Quotes
  ): Expr[ProductK[B, ElemTypes]] =
    val acc    = new collection.mutable.ArrayBuffer[Expr[Object]](typeLength.length)
    var i: Int = 0
    while (i < typeLength.length) {
      acc += '{
        ${ f[Object](Finite.unsafeApply(i)) }.asInstanceOf[Object]
      }
      i += 1
    }

    '{
      val objArr = Array(${ Expr.ofSeq(acc.toSeq) }*)
      ProductK.ofProductUnsafe(ArrayProduct.ofArrayUnsafe(objArr))
    }

  def tabulateTraverseKExpr[ElemTypes <: Tuple: Type, B[_], D[_]](using typeLength: TypeLength[ElemTypes])(
      f: Finite[typeLength.Length] :#~>: Compose3[Expr, B, D],
      BAppExpr: Expr[Applicative[B]]
  )(using q: Quotes, b: Type[B], d: Type[D]): Expr[B[ProductK[D, ElemTypes]]] =
    var acc: Expr[B[List[Object]]] = '{ $BAppExpr.pure(List.empty[Object]) }

    var i: Int = 0
    while (i < typeLength.length) {
      acc = '{ $BAppExpr.map2(${ f[Object](Finite.unsafeApply(i)) }, $acc)((v, a) => v.asInstanceOf[Object] :: a) }
      i += 1
    }

    '{
      $BAppExpr.map($acc) { a =>
        ProductK.ofProductUnsafe(ArrayProduct.ofArrayUnsafe(a.reverseIterator.toArray))
      }
    }

  def summonInstances[ElemTypes <: Tuple, F[_]: Type](typesArr: IArray[Type[_]])(
      using q: Quotes
  ): ProductK[Compose2[Expr, F], ElemTypes] =
    import cats.syntax.all._
    import q.reflect.*
    val instancesValidated = typesArr.toSeq.traverse { case '[t] =>
      Implicits.search(TypeRepr.of[F[t]]) match
        case iss: ImplicitSearchSuccess => iss.tree.asExprOf[F[t]].validNel
        case isf: ImplicitSearchFailure => isf.explanation.invalidNel
    }

    instancesValidated match
      case Validated.Valid(instances) =>
        ProductK.ofProductUnsafe[Compose2[Expr, F], ElemTypes](ArrayProduct(IArray.from(instances)))
      case Validated.Invalid(errors) =>
        errors.init.foreach(e => report.error(e))
        report.errorAndAbort(errors.last)

  def summonInstancesOpt[ElemTypes <: Tuple, F[_]: Type](typesArr: IArray[Type[_]])(
      using q: Quotes
  ): Option[ProductK[Compose2[Expr, F], ElemTypes]] =
    import cats.syntax.all._
    typesArr.toSeq
      .traverse { case '[t] =>
        Expr.summon[F[t]]
      }
      .map(instances => ProductK.ofProductUnsafe[Compose2[Expr, F], ElemTypes](ArrayProduct(IArray.from(instances))))

trait ExprHKDProductGeneric[A] extends GenHKDProductGeneric[A] with ExprHKDGeneric[A]:
  type Cat[B] = Expr[B]

  def idFrom(gen: Cat[Gen[Id]]): Cat[A]

  def idTo(a: Cat[A]): Cat[Gen[Id]]

  def tabulateFlatMappableExpr[B[_]: Type, D[_]: Type, R: Type](using q: Quotes)(
      f: Index :~>: Compose3[Expr, B, D],
      transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R],
      extractFlatMap: Quotes ?=> [X] => (Expr[B[D[X]]], Index[X], Quotes ?=> Expr[D[X]] => Expr[B[R]]) => Expr[B[R]],
      extractMap: Quotes ?=> [X] => (Expr[B[D[X]]], Index[X], Quotes ?=> Expr[D[X]] => Expr[R]) => Expr[B[R]]
  ): Expr[B[R]]

  def tabulateFlatMapExpr[B[_]: Type, D[_]: Type, R: Type](using q: Quotes)(
      f: Index :~>: Compose3[Expr, B, D],
      transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R],
      BFlatMapExpr: Expr[Monad[B]]
  ): Expr[B[R]]

  def tabulateMatchExprOption[D[_]: Type, R: Type](using q: Quotes)(
      f: Index :~>: Compose3[Expr, Option, D],
      transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R]
  ): Expr[Option[R]]

  def tabulateMatchExprEither[E: Type, D[_]: Type, R: Type](using q: Quotes)(
      f: Index :~>: Compose3[Expr, [X] =>> Either[E, X], D],
      transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R]
  ): Expr[Either[E, R]]

object ExprHKDProductGeneric:

  transparent inline given derived[A](using q: Quotes, aType: Type[A]): ExprHKDProductGeneric[A] =
    Expr.summon[Mirror.ProductOf[A]] match
      case Some('{
            type elemTypes <: Tuple
            type label <: String
            type labels <: Tuple
            $m: Mirror.ProductOf[A] {
              type MirroredElemTypes  = `elemTypes`
              type MirroredLabel      = `label`
              type MirroredElemLabels = `labels`
            }
          }) =>
        import q.reflect.*
        val labels  = Type.valueOfTuple[labels].get.toIArray.map(e => (e: Any).asInstanceOf[String])
        val label   = Type.valueOfConstant[label].get
        val lengthV = labels.length

        derivedImpl[A, elemTypes, label, labels](
          label,
          labels,
          labels.toSet,
          IArray.from(Helpers.typesOfTuple(TypeRepr.of[elemTypes], Nil).map(_.asType)),
          m
        )(
          using q,
          aType,
          summon[Type[label]],
          summon[Type[labels]],
          summon[Type[elemTypes]],
          new TypeLength[elemTypes] {
            type Length = lengthV.type
            override def length: Length = lengthV
          }
        )

      case _ =>
        q.reflect.report.errorAndAbort(s"Could not find mirror for ${Type.show[A]}")

  def derivedImpl[A, ElemTypes <: Tuple, Label <: String, Labels <: Tuple](
      label: Label,
      namesArr: IArray[String],
      namesSet: Set[String],
      typesArr: IArray[Type[_]],
      m: Expr[
        Mirror.ProductOf[A] {
          type MirroredElemTypes  = ElemTypes
          type MirroredLabel      = Label
          type MirroredElemLabels = Labels
        }
      ]
  )(
      using q: Quotes,
      aType: Type[A],
      labelType: Type[Label],
      labelsType: Type[Labels],
      elemTypes: Type[ElemTypes],
      typeLength: TypeLength[ElemTypes]
  ): ExprHKDProductGeneric[A] {
    type Gen[F[_]] = ProductK[F, ElemTypes]
    type Index[_]  = Finite[typeLength.Length]
    type TypeName  = Label
    type TupleRep  = ElemTypes
  } = new ExprHKDProductGeneric[A]:
    override type Gen[F[_]] = ProductK[F, ElemTypes]
    override type Index[_]  = Finite[typeLength.Length]

    override def genType: Type[Gen] = Type.of[Gen]

    override def types: Gen[Type] = ProductK.ofProductUnsafe[Type, ElemTypes](ArrayProduct(typesArr))

    override type TypeName = Label

    override def typeName: TypeName = label

    opaque type Names <: String = String

    override def names: Gen[Const[Names]] =
      ProductK.ofProductUnsafe[Const[Names], ElemTypes](ArrayProduct(namesArr))

    override def stringToName(s: String): Option[Names] = Option.when(namesSet(s))(s)

    private lazy val nameMap = namesArr.zipWithIndex.toMap

    override def nameToIndex(name: Names): IdxWrapper[_ <: ElemTop] =
      HKDGeneric.IdxWrapper(Finite.unsafeApply(nameMap(name)))

    override type TupleRep = ElemTypes

    override def genToTuple[F[_]](gen: Gen[F]): Helpers.TupleMap[TupleRep, F] = gen.tuple

    override def tupleToGen[F[_]](tuple: Helpers.TupleMap[TupleRep, F]): Gen[F] = ProductK.ofTuple(tuple)

    override def idFrom(gen: Expr[Gen[Id]]): Expr[A] = '{ $m.fromProduct($gen.product) }

    override def idTo(a: Expr[A]): Expr[Gen[Id]] = '{ ProductK.ofProductUnsafe($a.asInstanceOf[Product]) }

    override def catTo(a: Cat[A]): Gen[Cat] = tabulateK([X] => (idx: Index[X]) => a.productElementCat(idx))

    override def catFrom(a: Gen[Cat]): Cat[A] =
      import q.reflect.*
      val exprs   = (a.product.productIterator: Iterator[Any]).asInstanceOf[Iterator[Cat[_]]].toList
      val aClass  = TypeRepr.of[A].classSymbol.getOrElse(report.errorAndAbort(s"Can't find class of ${Type.show[A]}"))
      val newExpr = Select(New(TypeTree.ref(aClass)), aClass.primaryConstructor)
      val types   = TypeRepr.of[A].typeArgs

      val (term, remainingArgs, remainingTypes) =
        aClass.primaryConstructor.paramSymss.foldLeft((newExpr: Term, exprs, types)) {
          case ((term, args, types), params) =>
            if (params.exists(_.isType))
              val (applyTypes, remainingTypes) = types.splitAt(params.length)
              (TypeApply(term, applyTypes.map(t => TypeTree.of(using t.asType))), args, remainingTypes)
            else
              val (applyArgs, remainingArgs) = args.splitAt(params.length)
              (Apply(term, applyArgs.map(_.asTerm)), remainingArgs, types)
        }
      if remainingArgs.nonEmpty then report.errorAndAbort(s"Unconsumed arguments for new $remainingArgs")
      if remainingTypes.nonEmpty then report.errorAndAbort(s"Unconsumed arguments for new $remainingTypes")

      term.asExprOf[A]

    extension [B[_]: Type](gen: Gen[B])
      override def traverseKExprId[D[_]: Type](f: B :~>: Compose2[Expr, D]): Expr[Gen[D]] =
        tabulateTraverseKExprId([X] => (idx: Index[X]) => f(gen.indexK(idx)): Compose2[Expr, D][X])

    override def tabulateTraverseKExprId[B[_]: Type](f: Index :~>: Compose2[Expr, B]): Expr[Gen[B]] =
      ExprHKDGeneric.tabulateTraverseKExprId(f)

    override def tabulateTraverseKExpr[B[_]: Type, D[_]: Type](
        f: Index :~>: Compose3[Expr, B, D],
        BAppExpr: Expr[Applicative[B]]
    ): Expr[B[Gen[D]]] =
      ExprHKDGeneric.tabulateTraverseKExpr(f, BAppExpr)

    override def tabulateFlatMappableExpr[B[_]: Type, D[_]: Type, R: Type](using q: Quotes)(
        f: Index :~>: Compose3[Expr, B, D],
        transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R],
        extractFlatMap: Quotes ?=> [X] => (Expr[B[D[X]]], Index[X], Quotes ?=> Expr[D[X]] => Expr[B[R]]) => Expr[B[R]],
        extractMap: Quotes ?=> [X] => (Expr[B[D[X]]], Index[X], Quotes ?=> Expr[D[X]] => Expr[R]) => Expr[B[R]]
    ): Expr[B[R]] =
      val ts = types

      def lastStep(using q: Quotes)(argsRev: List[Expr[Any]]): Expr[R] =
        import q.reflect.*
        val gen: Gen[Const[Expr[Any]]] =
          ProductK.ofProductUnsafe[Const[Expr[Any]], ElemTypes](
            ArrayProduct.ofArrayUnsafe(argsRev.reverseIterator.toArray)
          )

        transform(using q)(
          tabulateK[Compose2[Expr, D], Nothing] {
            [X] =>
              (idx: Index[X]) =>
                given Type[X] = ts.indexK(idx)
                gen.indexK(idx).asExprOf[D[X]]
          }
        )

      def rec[X](using q: Quotes)(idx: Index[X], argsRev: List[Expr[Any]]): Expr[B[R]] =
        val nextIdx = idx.value + 1
        if nextIdx >= typeLength.length then
          extractMap(using q)[X](
            f(idx),
            idx,
            newQ ?=>
              x =>
                import newQ.reflect.*
                lastStep(using newQ)(x :: argsRev)
          )
        else
          extractFlatMap(using q)[X](
            f(idx),
            idx,
            newQ ?=>
              x =>
                import newQ.reflect.*
                rec(using newQ)(Finite.unsafeApply(nextIdx), x :: argsRev)
          )

      rec(using q)(Finite.unsafeApply(0), Nil)

    override def tabulateFlatMapExpr[B[_]: Type, D[_]: Type, R: Type](using q: Quotes)(
        f: Index :~>: Compose3[Expr, B, D],
        transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R],
        BFlatMapExpr: Expr[Monad[B]]
    ): Expr[B[R]] =
      val ts = types
      tabulateFlatMappableExpr[B, D, R](using q)(
        f,
        transform,
        newQ ?=>
          [X] =>
            (ex: Expr[B[D[X]]], idx: Index[X], cont: Quotes ?=> Expr[D[X]] => Expr[B[R]]) =>
              import newQ.reflect.*
              val flatMapFunSym = TypeRepr.of[FlatMap[B]].typeSymbol.declaredMethod("flatMap").head
              val flatMapSelect = Select(BFlatMapExpr.asTerm, flatMapFunSym)
              given Type[X]     = ts.indexK(idx)

              val app = Apply(
                TypeApply(flatMapSelect, List(TypeTree.of[D[X]], TypeTree.of[R])),
                List(ex.asTerm)
              )

              Apply(
                app,
                List(
                  Lambda(
                    Symbol.spliceOwner,
                    MethodType(List(s"arg$idx"))(_ => List(TypeRepr.of[D[X]]), _ => TypeRepr.of[B[R]]),
                    {
                      case (newOwner, List(arg)) => cont(using newOwner.asQuotes)(arg.asExprOf[D[X]]).asTerm
                      case (_, _)                => sys.error("Got invalid args")
                    }
                  )
                )
              ).asExprOf[B[R]]
        ,
        newQ ?=>
          [X] =>
            (ex: Expr[B[D[X]]], idx: Index[X], cont: Quotes ?=> Expr[D[X]] => Expr[R]) =>
              import newQ.reflect.*
              val mapFunSym = TypeRepr.of[Functor[B]].typeSymbol.declaredMethod("map").head
              val mapSelect = Select(BFlatMapExpr.asTerm, mapFunSym)
              given Type[X] = ts.indexK(idx)

              val app = Apply(
                TypeApply(mapSelect, List(TypeTree.of[D[X]], TypeTree.of[R])),
                List(ex.asTerm)
              )

              Apply(
                app,
                List(
                  Lambda(
                    Symbol.spliceOwner,
                    MethodType(List(s"arg$idx"))(_ => List(TypeRepr.of[D[X]]), _ => TypeRepr.of[R]),
                    {
                      case (newOwner, List(arg)) => cont(using newOwner.asQuotes)(arg.asExprOf[D[X]]).asTerm
                      case (_, _)                => sys.error("Got invalid args")
                    }
                  )
                )
              ).asExprOf[B[R]]
      )

    override def tabulateMatchExprOption[D[_]: Type, R: Type](using q: Quotes)(
        f: Index :~>: Compose3[Expr, Option, D],
        transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R]
    ): Expr[Option[R]] =
      val ts = types
      tabulateFlatMappableExpr(using q)(
        f,
        transform,
        newQ ?=>
          [X] =>
            (ex: Expr[Option[D[X]]], idx: Index[X], cont: Quotes ?=> Expr[D[X]] => Expr[Option[R]]) =>
              given Type[X] = ts.indexK(idx)
              import newQ.reflect.*

              ValDef
                .let(Symbol.spliceOwner, s"arg${idx.value}", ex.asTerm) { vTerm =>
                  val v = vTerm.asExprOf[Option[D[X]]]
                  '{
                    // Simpler bytecode
                    if $v != None then ${ cont(using newQ)('{ $v.asInstanceOf[Some[D[X]]].value }) }
                    else None
                  }.asTerm
                }
                .asExprOf[Option[R]]
        ,
        newQ ?=>
          [X] =>
            (ex: Expr[Option[D[X]]], idx: Index[X], cont: Quotes ?=> Expr[D[X]] => Expr[R]) =>
              given Type[X] = ts.indexK(idx)
              import newQ.reflect.*

              ValDef
                .let(Symbol.spliceOwner, s"arg${idx.value}", ex.asTerm) { vTerm =>
                  val v = vTerm.asExprOf[Option[D[X]]]
                  '{
                    // Simpler bytecode
                    if $v != None then Some(${ cont(using newQ)('{ $v.asInstanceOf[Some[D[X]]].value }) })
                    else None
                  }.asTerm
                }
                .asExprOf[Option[R]]
      )

    override def tabulateMatchExprEither[E: Type, D[_]: Type, R: Type](using q: Quotes)(
        f: Index :~>: Compose3[Expr, [X] =>> Either[E, X], D],
        transform: Quotes ?=> Gen[Compose2[Expr, D]] => Expr[R]
    ): Expr[Either[E, R]] =
      val ts = types
      tabulateFlatMappableExpr(using q)(
        f,
        transform,
        newQ ?=>
          [X] =>
            (ex: Expr[Either[E, D[X]]], idx: Index[X], cont: Quotes ?=> Expr[D[X]] => Expr[Either[E, R]]) =>
              given Type[X] = ts.indexK(idx)
              import newQ.reflect.*

              ValDef
                .let(Symbol.spliceOwner, s"arg${idx.value}", ex.asTerm) { vTerm =>
                  val v = vTerm.asExprOf[Either[E, D[X]]]
                  '{
                    // Simpler bytecode
                    if $v.isInstanceOf[Right[_, _]] then
                      ${ cont(using newQ)('{ $v.asInstanceOf[Right[E, D[X]]].value }) }
                    else $v.asInstanceOf[Either[E, R]]
                  }.asTerm
                }
                .asExprOf[Either[E, R]]
        ,
        newQ ?=>
          [X] =>
            (ex: Expr[Either[E, D[X]]], idx: Index[X], cont: Quotes ?=> Expr[D[X]] => Expr[R]) =>
              given Type[X] = ts.indexK(idx)
              import newQ.reflect.*

              ValDef
                .let(Symbol.spliceOwner, s"arg${idx.value}", ex.asTerm) { vTerm =>
                  val v = vTerm.asExprOf[Either[E, D[X]]]
                  '{
                    // Simpler bytecode
                    if $v.isInstanceOf[Right[_, _]] then
                      Right(${ cont(using newQ)('{ $v.asInstanceOf[Right[E, D[X]]].value }) })
                    else $v.asInstanceOf[Either[E, R]]
                  }.asTerm
                }
                .asExprOf[Either[E, R]]
      )

    override def tabulateFoldLeft[B](start: B)(f: B => [X] => Index[X] => B): B =
      HKDGeneric.tabulateFoldLeftImpl(typeLength.length, start, f)

    override def tabulateTraverseK[G[_]: Applicative, B[_]](f: Index :~>: Compose2[G, B]): G[Gen[B]] =
      HKDGeneric.tabulateTraverseKImpl(typeLength.length, f)

    override def tabulateTraverseKOption[B[_]](
        f: Index :~>: Compose2[Option, B]
    ): Option[Gen[B]] = HKDGeneric.tabulateTraverseKOptionImpl(typeLength.length, f)

    override def tabulateTraverseKEither[E, B[_]](
        f: Index :~>: Compose2[[X] =>> Either[E, X], B]
    ): Either[E, Gen[B]] = HKDGeneric.tabulateTraverseKEitherImpl(typeLength.length, f)

    extension (a: Cat[A])
      def productElementCat[X](index: Index[X]): Cat[X] =
        import q.reflect.*
        given Type[X] = (typesArr(index.value): Type[_]).asInstanceOf[Type[X]]
        Select.unique(a.asTerm, namesArr(index.value)).asExprOf[X]

    override def summonInstances[F[_]: Type]: Gen[Compose2[Expr, F]] =
      ExprHKDGeneric.summonInstances(typesArr)

    override def summonInstancesOpt[F[_]: Type]: Option[Gen[Compose2[Expr, F]]] =
      ExprHKDGeneric.summonInstancesOpt(typesArr)

    private val instance: BoundedRepresentableKC.Aux[Gen, Index] & TraverseKC[Gen] =
      ProductK.productKInstance[ElemTypes]

    override val representable: BoundedRepresentableKC.Aux[Gen, Index] = instance
    override val traverse: TraverseKC[Gen]                             = instance

trait ExprHKDSumGeneric[A] extends GenHKDSumGeneric[A] with ExprHKDGeneric[A]:
  type Cat[B] = Expr[Option[B]]

  /**
    * Returns the index of a value. Because of soundness, this method can not be
    * used if X = A. In that case, use [[indexOfA]] instead.
    */
  def indexOf[X <: ElemTop: Type](x: Expr[X]): Expr[Index[X]]

  /** Same as [[indexOf]] but also works for values of type A. */
  def indexOfA(a: Expr[A]): Expr[IdxWrapper[_ <: ElemTop]]

  /**
    * Same as [[indexOfA]] but also essentially casts the value to the unknown
    * type, allowing further operations on it that requires that it is a subtype
    * of A.
    */
  def indexOfACasting(a: Expr[A]): ExprHKDSumGeneric.IndexOfACasting[Index, ElemTop]

  /**
    * Widen the higher kinded representation to a [[Const]] type of the top
    * type.
    */
  inline def widenConst[F[+_]](gen: Gen[F]): Gen[Const[F[A]]] =
    // This is safe. We can't use the widen method as it can't know about the contents of Gen, we do
    gen.asInstanceOf[Gen[Const[F[A]]]]

  /**
    * Convert a value of [[A]] to the higher kinded representation. It will be
    * Some in only one field, corresponding to the subtype passed in, and None
    * in all the others.
    */
  def to(a: Expr[A]): Gen[Cat]

  override def catTo(a: Cat[A]): Gen[Cat]

  /**
    * Convert a value of the higher kinded representation to [[A]]. Will only
    * return Some if only one of the fields is Some and the rest is None.
    */
  def from(a: Gen[Cat]): Cat[A] = catFrom(a)

  def catFrom(a: Gen[Cat]): Cat[A]

  extension (a: Cat[A]) def productElementCat[X](index: Index[X]): Cat[X]

object ExprHKDSumGeneric:
  trait IndexOfACasting[Index[_], ElemTop] {
    type X0 <: ElemTop
    val index: Expr[Index[X0]]
    val value: Expr[X0]
  }

  object IndexOfACasting {
    class IndexOfACastingImpl[Index[_], ElemTop, X1 <: ElemTop](
        val index: Expr[Index[X1]],
        val value: Expr[X1]
    ) extends IndexOfACasting[Index, ElemTop] {
      type X0 = X1
    }
  }

  transparent inline given derived[A](using q: Quotes, aType: Type[A]): ExprHKDSumGeneric[A] =
    Expr.summon[Mirror.SumOf[A]] match
      case Some('{
            type elemTypes <: Tuple
            type label <: String
            type labels <: Tuple
            $m: Mirror.SumOf[A] {
              type MirroredElemTypes  = `elemTypes`
              type MirroredLabel      = `label`
              type MirroredElemLabels = `labels`
            }
          }) =>
        import q.reflect.*
        val labels  = Type.valueOfTuple[labels].get.toIArray.map(e => (e: Any).asInstanceOf[String])
        val label   = Type.valueOfConstant[label].get
        val lengthV = labels.length

        derivedImpl[A, elemTypes, label, labels](
          label,
          labels,
          labels.toSet,
          IArray.from(Helpers.typesOfTuple(TypeRepr.of[elemTypes], Nil).map(_.asType)),
          m
        )(
          using q,
          aType,
          summon[Type[label]],
          summon[Type[labels]],
          summon[Type[elemTypes]],
          new TypeLength[elemTypes] {
            type Length = lengthV.type

            override def length: Length = lengthV
          },
          summon[Type[lengthV.type]]
        )

      case _ =>
        q.reflect.report.errorAndAbort(s"Could not find mirror for ${Type.show[A]}")

  def derivedImpl[A, ElemTypes <: Tuple, Label <: String, Labels <: Tuple](
      label: Label,
      namesArr: IArray[String],
      namesSet: Set[String],
      typesArr: IArray[Type[_]],
      m: Expr[
        Mirror.SumOf[A] {
          type MirroredElemTypes  = ElemTypes
          type MirroredLabel      = Label
          type MirroredElemLabels = Labels
        }
      ]
  )(
      using q: Quotes,
      aType: Type[A],
      labelType: Type[Label],
      labelsType: Type[Labels],
      elemTypes: Type[ElemTypes],
      typeLength: TypeLength[ElemTypes],
      typeLengthType: Type[typeLength.Length]
  ): ExprHKDSumGeneric[A] {
    type Gen[F[_]] = ProductK[F, ElemTypes]
    type Index[_]  = Finite[typeLength.Length]
    type TypeName  = Label
    type TupleRep  = ElemTypes
    type ElemTop   = A & Helpers.TupleUnion[TupleRep, Nothing]
  } = new ExprHKDSumGeneric[A]:
    override type Gen[F[_]] = ProductK[F, ElemTypes]
    override type Index[_]  = Finite[typeLength.Length]
    override type ElemTop = A & Helpers.TupleUnion[TupleRep, Nothing]

    override def genType: Type[Gen] = Type.of[Gen]

    override def types: Gen[Type] = ProductK.ofProductUnsafe[Type, ElemTypes](ArrayProduct(typesArr))

    override type TypeName = Label

    override def typeName: TypeName = label

    opaque type Names <: String = String

    override def names: Gen[Const[Names]] =
      ProductK.ofProductUnsafe[Const[Names], ElemTypes](ArrayProduct(namesArr))

    override def stringToName(s: String): Option[Names] = Option.when(namesSet(s))(s)

    private lazy val nameMap = namesArr.zipWithIndex.toMap

    override def nameToIndex(name: Names): IdxWrapper[_ <: ElemTop] =
      HKDGeneric.IdxWrapper(Finite.unsafeApply(nameMap(name)))

    override type TupleRep = ElemTypes

    override def genToTuple[F[_]](gen: Gen[F]): Helpers.TupleMap[TupleRep, F] = gen.tuple

    override def tupleToGen[F[_]](tuple: Helpers.TupleMap[TupleRep, F]): Gen[F] = ProductK.ofTuple(tuple)

    override def to(a: Expr[A]): Gen[Cat] = catTo('{ Some($a) })

    override def catTo(a: Cat[A]): Gen[Cat] =
      val ts = types
      representable.tabulateK {
        [Z] =>
          (i: Index[Z]) =>
            given Type[Z] = ts.indexK(i)
            '{
              val ao = $a
              ao.fold(None: Option[Z]) { a =>
                // This cast is safe as we know A = Z
                if ${ Expr(i.value) } == $m.ordinal(a) then Some((a: A).asInstanceOf[Z]) else None
              }
          }
      }

    override def catFrom(a: Gen[Cat]): Cat[A] =
      val ts = types
      val ret = tabulateFoldLeft('{ (false, None: Option[A]) }) { acc =>
        [X] =>
          (idx: Index[X]) =>
            given Type[X] = ts.indexK(idx)
            '{
              val (moreThanOne, v) = $acc
              if moreThanOne then (true, None: Option[A])
              else
                val v2 = ${ a.indexK(idx) }
                if v.isDefined && v2.isDefined then (true, None)
                else (false, v.orElse(v2.asInstanceOf[Option[A]]))
          }
      }
      '{ $ret._2 }


    override def indexOf[X <: ElemTop: Type](x: Expr[X]): Expr[Index[X]] =
      '{ Finite.unsafeApply($m.ordinal($x)) }

    override def indexOfA(a: Expr[A]): Expr[IdxWrapper[_ <: ElemTop]] =
      '{ new HKDGeneric.IdxWrapper[Index, ElemTop](Finite.unsafeApply($m.ordinal($a))) }

    override def indexOfACasting(a: Expr[A]): IndexOfACasting[Index, ElemTop] =
      val idx = '{ Finite.unsafeApply[typeLength.Length]($m.ordinal($a)) }
      new IndexOfACasting.IndexOfACastingImpl[Index, ElemTop, ElemTop](idx, a.asInstanceOf[Expr[ElemTop]])

    extension [B[_]: Type](gen: Gen[B])
      override def traverseKExprId[D[_]: Type](f: B :~>: Compose2[Expr, D]): Expr[Gen[D]] =
        tabulateTraverseKExprId([X] => (idx: Index[X]) => f(gen.indexK(idx)): Compose2[Expr, D][X])

    override def tabulateTraverseKExprId[B[_]: Type](f: Index :~>: Compose2[Expr, B]): Expr[Gen[B]] =
      ExprHKDGeneric.tabulateTraverseKExprId(f)

    override def tabulateTraverseKExpr[B[_]: Type, D[_]: Type](
        f: Index :~>: Compose3[Expr, B, D],
        BAppExpr: Expr[Applicative[B]]
    ): Expr[B[Gen[D]]] =
      ExprHKDGeneric.tabulateTraverseKExpr(f, BAppExpr)

    override def tabulateFoldLeft[B](start: B)(f: B => [X] => Index[X] => B): B =
      HKDGeneric.tabulateFoldLeftImpl(typeLength.length, start, f)

    override def tabulateTraverseK[G[_]: Applicative, B[_]](f: Index :~>: Compose2[G, B]): G[Gen[B]] =
      HKDGeneric.tabulateTraverseKImpl(typeLength.length, f)

    override def tabulateTraverseKOption[B[_]](
        f: Index :~>: Compose2[Option, B]
    ): Option[Gen[B]] = HKDGeneric.tabulateTraverseKOptionImpl(typeLength.length, f)

    override def tabulateTraverseKEither[E, B[_]](
        f: Index :~>: Compose2[[X] =>> Either[E, X], B]
    ): Either[E, Gen[B]] = HKDGeneric.tabulateTraverseKEitherImpl(typeLength.length, f)

    extension (a: Cat[A])
      override def productElementCat[X](index: Index[X]): Cat[X] =
        given Type[X] = types.indexK(index)
        '{
          val v = $a
          if v != None && $m.ordinal(v.asInstanceOf[Some[A]].value) == ${ Expr(index.value) } then
            v.asInstanceOf[Option[X]]
          else None
        }

    override def summonInstances[F[_]: Type]: Gen[Compose2[Expr, F]] =
      ExprHKDGeneric.summonInstances(typesArr)

    override def summonInstancesOpt[F[_]: Type]: Option[Gen[Compose2[Expr, F]]] =
      ExprHKDGeneric.summonInstancesOpt(typesArr)

    private val instance: BoundedRepresentableKC.Aux[Gen, Index] & TraverseKC[Gen] =
      ProductK.productKInstance[ElemTypes]

    override val representable: BoundedRepresentableKC.Aux[Gen, Index] = instance
    override val traverse: TraverseKC[Gen]                             = instance




© 2015 - 2024 Weber Informatics LLC | Privacy Policy