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

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

The newest version!
package perspective.derivation

import scala.language.implicitConversions

import scala.compiletime.*
import scala.deriving.*
import scala.quoted.*

import cats.Applicative
import cats.kernel.{BoundedEnumerable, Order}
import perspective.*

trait GenHKDGeneric[A]:
  type Cat[_]

  /** A representation of [[A]] supporting higher kinded types. */
  type Gen[_[_]]

  /** The index of the [[Gen]] type. */
  type Index[B]

  /** The top type for the inner type of [[Index]] and [[Gen]]. */
  type ElemTop

  /** A wrapper for [[Index]] where we want wildcards of it. */
  type IdxWrapper[X] = HKDGeneric.IdxWrapper[Index, X]
  given [X]: Conversion[IdxWrapper[X], Index[X]] = _.idx
  given [X]: Conversion[Index[X], IdxWrapper[X]] = HKDGeneric.IdxWrapper(_)

  /** Upcast an index to its bound. */
  inline def upcastIndex[X](idx: Index[X]): IdxWrapper[_ <: ElemTop] =
    HKDGeneric.IdxWrapper(idx).asInstanceOf[IdxWrapper[_ <: ElemTop]]

  /** The name of the [[A]] type. */
  type TypeName <: String

  /** The name of the [[A]] type. */
  def typeName: TypeName

  /**
    * The name of the fields of [[A]] type. Field in this case can mean either
    * the children of a sum type, or the fields of a product type.
    */
  type Names <: String

  /**
    * The name of the fields of [[A]] type. Field in this case can mean either
    * the children of a sum type, or the fields of a product type.
    */
  def names: Gen[Const[Names]]

  /** Given a index, return the name of the index. */
  def indexToName[X](idx: Index[X]): Names = names.indexK(idx)

  /** Validates a string as a name if it matches the name of a field. */
  def stringToName(s: String): Option[Names]

  /** Returns the index of the field a name corresponds to. */
  def nameToIndex(name: Names): IdxWrapper[_ <: ElemTop]

  /** A tuple representation of [[A]]. */
  type TupleRep <: Tuple

  /** Converts [[Gen]] to the tuple representation. */
  def genToTuple[F[_]](gen: Gen[F]): Helpers.TupleMap[TupleRep, F]

  /** Converts the tuple representation to Gen. */
  def tupleToGen[F[_]](tuple: Helpers.TupleMap[TupleRep, F]): Gen[F]

  /** Converts [[Gen]] to the scala tuple representation. */
  def genToScalaTuple[F[_]](gen: Gen[F]): Tuple.Map[TupleRep, F] = genToTuple(gen).asInstanceOf[Tuple.Map[TupleRep, F]]

  /** Converts the scala tuple representation to Gen. */
  def scalaTupleToGen[F[_]](tuple: Tuple.Map[TupleRep, F]): Gen[F] = tupleToGen(
    tuple.asInstanceOf[Helpers.TupleMap[TupleRep, F]]
  )

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

  given BoundedRepresentableKC.Aux[Gen, Index] = representable
  given TraverseKC[Gen]                        = traverse

  export representable.*
  export traverse.{asK => _, liftK => _, mapConst => _, mapK => _, voidK => _, widen => _, imapK => _, *}

  // Cat generic functions

  /** Convert a value of `Cat[A]` to the higher kinded representation. */
  def catTo(a: Cat[A]): Gen[Cat]

  /** Convert a value of the higher kinded representation to `Cat[A]`. */
  def catFrom(gen: Gen[Cat]): Cat[A]

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

  // Extra ops

  def tabulateFoldLeft[B](start: B)(f: B => [X] => Index[X] => B): B

  def tabulateTraverseK[G[_], B[_]](f: [X] => Index[X] => G[B[X]])(using Applicative[G]): G[Gen[B]]

  def tabulateTraverseKOption[B[_]](f: [X] => Index[X] => Option[B[X]]): Option[Gen[B]]

  def tabulateTraverseKEither[E, B[_]](f: [X] => Index[X] => Either[E, B[X]]): Either[E, Gen[B]]

/**
  * A type somewhat like [[Mirror.Of]] allowing manipulating a type as if it was
  * defined as a higher kinded type.
  * @tparam A
  *   The type being abstracted over.
  */
sealed trait HKDGeneric[A] extends GenHKDGeneric[A]

object HKDGeneric:
  type Aux[A, Gen0[_[_]]] = HKDGeneric[A] {
    type Gen[B[_]] = Gen0[B]
  }

  case class IdxWrapper[Index[_], X](idx: Index[X])

  transparent inline given derived[A](using m: Mirror.Of[A], typeLength: TypeLength[A])(
      using Finite.NotZero[typeLength.Length] =:= true
  ): HKDGeneric[A] = inline m match
    case m: Mirror.ProductOf[A] =>
      HKDProductGeneric.derived(using m)

    case m: Mirror.SumOf[A] =>
      HKDSumGeneric.derived[A](using m)

  private[derivation] def tabulateFoldLeftImpl[N <: Int, B](size: N, start: B, f: B => [X] => Finite[N] => B): B =
    var res: B = start
    var i: Int = 0
    while i < size do
      res = f(res)(Finite.unsafeApply(i))
      i += 1

    res

  private[derivation] def makeArrayObjectArray[A](arr: Array[A]): Array[Object] = arr match
    case arr: Array[Object] => arr
    case _                  => arr.map(Helpers.boxAny(_))

  private[derivation] def tabulateTraverseKImpl[N <: Int, ElemTop, T <: Tuple, Gen[_[_]], G[_], B[_]](
      size: N,
      f: [X] => Finite[N] => G[B[X]]
  )(
      using G: Applicative[G]
  ): G[ProductK[B, T]] =
    var acc: G[List[B[ElemTop]]] = G.pure(List.empty[B[ElemTop]])
    var i: Int                   = 0
    while (i < size) {
      acc = G.map2(f[ElemTop](Finite.unsafeApply(i)), acc)((v, a) => v :: a)
      i += 1
    }

    G.map(acc) { a =>
      val objArr = makeArrayObjectArray(a.reverseIterator.toArray)
      ProductK.ofProductUnsafe(ArrayProduct.ofArrayUnsafe(objArr))
    }

  private[derivation] def tabulateTraverseKOptionImpl[N <: Int, ElemTop, T <: Tuple, B[_]](
      size: N,
      f: [X] => Finite[N] => Option[B[X]]
  ): Option[ProductK[B, T]] =
    val arr    = new Array[Object](size)
    var i: Int = 0
    while (i < size) {
      val res = f[ElemTop](Finite.unsafeApply(i))

      if res.isEmpty
      then
        // Early return
        return None
      else arr(i) = Helpers.boxAny(res.get)

      i += 1
    }

    Some(ProductK.ofProductUnsafe(ArrayProduct.ofArrayUnsafe(arr)))

  private[derivation] def tabulateTraverseKEitherImpl[N <: Int, ElemTop, T <: Tuple, E, B[_]](
      size: N,
      f: [X] => Finite[N] => Either[E, B[X]]
  ): Either[E, ProductK[B, T]] =
    val arr    = new Array[Object](size)
    var i: Int = 0
    while (i < size) {
      val res = f[ElemTop](Finite.unsafeApply(i))
      res match
        case Right(v) =>
          arr(i) = Helpers.boxAny(v)
        case Left(e) =>
          // Early return
          return Left(e)

      i += 1
    }

    Right(ProductK.ofProductUnsafe(ArrayProduct.ofArrayUnsafe(arr)))

end HKDGeneric

/**
  * A type somewhat like [[Mirror.ProductOf]] allowing manipulating a product
  * type as if it was defined as a higher kinded type.
  * @tparam A
  *   The type being abstracted over.
  */
trait GenHKDProductGeneric[A] extends GenHKDGeneric[A]:

  /** Convert a value of [[A]] to the higher kinded representation. */
  def to(a: Cat[A]): Gen[Cat] = catTo(a)

  /** Convert a value of the higher kinded representation to [[A]]. */
  def from(gen: Gen[Cat]): Cat[A] = catFrom(gen)

/**
  * A type somewhat like [[Mirror.ProductOf]] allowing manipulating a product
  * type as if it was defined as a higher kinded type.
  * @tparam A
  *   The type being abstracted over.
  */
trait HKDProductGeneric[A] extends GenHKDProductGeneric[A] with HKDGeneric[A]:
  type Cat[B] = B

  extension (a: A) def productElementId[X](index: Index[X]): X = a.productElementCat(index)

object HKDProductGeneric:
  transparent inline def apply[A](using gen: HKDProductGeneric[A]): HKDProductGeneric.Aux[A, gen.Gen] = gen

  type Aux[A, Gen0[_[_]]] = HKDProductGeneric[A] {
    type Gen[B[_]] = Gen0[B]
  }

  transparent inline given derived[A](using m: Mirror.ProductOf[A], typeLength: TypeLength[A]): HKDProductGeneric[A] =
    val labels = Helpers.constValueTupleToIArray[m.MirroredElemLabels, String]
    derivedImpl[A, m.MirroredElemTypes, m.MirroredLabel, typeLength.Length](
      constValue[m.MirroredLabel],
      labels,
      labels.toSet
    )

  def derivedImpl[A, ElemTypes <: Tuple, Label <: String, L <: Int](
      label: Label,
      namesArr: IArray[String],
      namesSet: Set[String]
  )(
      using m: Mirror.ProductOf[A] {
        type MirroredElemTypes = ElemTypes; type MirroredLabel = Label
      },
      typeLength: TypeLength.Aux[A, L]
  ): HKDProductGeneric[A] {
    type TupleRep  = ElemTypes
    type Gen[F[_]] = ProductK[F, TupleRep]
    type Index[_]  = Finite[typeLength.Length]
    type TypeName  = Label
  } =
    new HKDProductGeneric[A]:
      override type Gen[F[_]] = ProductK[F, TupleRep]
      override type Index[_]  = Finite[typeLength.Length]

      override type TypeName = Label
      override def typeName: TypeName = label

      opaque type Names <: String = String
      override def names: Gen[Const[Names]] =
        ProductK.ofProductUnsafe[Const[Names], TupleRep](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 catTo(a: A): Gen[Id] = ProductK.ofProductUnsafe(a.asInstanceOf[Product])

      override def catFrom(a: Gen[Id]): A =
        m.fromProduct(a.product)

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

      override def tabulateTraverseK[G[_], B[_]](f: [X] => Finite[typeLength.Length] => G[B[X]])(
          using Applicative[G]
      ): G[Gen[B]] =
        HKDGeneric.tabulateTraverseKImpl(typeLength.length, f)

      override def tabulateTraverseKOption[B[_]](
          f: [X] => Finite[typeLength.Length] => Option[B[X]]
      ): Option[ProductK[B, TupleRep]] = HKDGeneric.tabulateTraverseKOptionImpl(typeLength.length, f)

      override def tabulateTraverseKEither[E, B[_]](
          f: [X] => Finite[typeLength.Length] => Either[E, B[X]]
      ): Either[E, ProductK[B, TupleRep]] = HKDGeneric.tabulateTraverseKEitherImpl(typeLength.length, f)

      extension (a: A)
        def productElementCat[X](index: Index[X]): X =
          (a.asInstanceOf[Product].productElement(index.value): Any).asInstanceOf[X]

      private val instance: BoundedRepresentableKC.Aux[Gen, Index] & TraverseKC[Gen] =
        ProductK.productKInstance[TupleRep](using typeLength.asInstanceOf[TypeLength.Aux[TupleRep, typeLength.Length]])

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

trait GenHKDSumGeneric[A] extends GenHKDGeneric[A]:
  override type ElemTop <: A

/**
  * A type somewhat like [[Mirror.SumOf]] allowing manipulating a sum type as if
  * it was defined as a higher kinded type.
  * @tparam A
  *   The type being abstracted over.
  */
trait HKDSumGeneric[A] extends HKDGeneric[A] with GenHKDSumGeneric[A]:
  type Cat[B] = 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](x: X): Index[X]

  /** Same as [[indexOf]] but also works for values of type A. */
  def indexOfA(a: A): IdxWrapper[_ <: ElemTop] = indexOf(a.asInstanceOf[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: A): HKDSumGeneric.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: A): Gen[Option] = catTo(Some(a))

  override def catTo(ao: Option[A]): Gen[Option] =
    ao.fold(
      representable.pure([Z] => () => None: Option[Z])
    ) { a =>
      val index = indexOf((a: A).asInstanceOf[ElemTop])
      // This cast is safe as we know A = Z
      representable.tabulateK([Z] => (i: Index[Z]) => if i == index then Some((a: A).asInstanceOf[Z]) else None)
    }

  /**
    * 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[Option]): Option[A] = catFrom(a)

  def catFrom(a: Gen[Option]): Option[A] =
    traverse.toListK(widenConst(a)).flatten match
      case Nil      => None    // No values present
      case a :: Nil => Some(a) // One value present
      case _        => None    // More than one value present

  extension (ao: Option[A])
    def productElementCat[X](index: Index[X]): Option[X] = ao.flatMap { a =>
      val aIdx = indexOfA(a)
      if index == aIdx.idx then Some((a: A).asInstanceOf[X]) else None
    }

object HKDSumGeneric:
  def apply[A](using gen: HKDSumGeneric[A]): HKDSumGeneric.Aux[A, gen.Gen] = gen

  type Aux[A, Gen0[_[_]]] = HKDSumGeneric[A] {
    type Gen[B[_]] = Gen0[B]
  }

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

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

  transparent inline given derived[A](using m: Mirror.SumOf[A], typeLength: TypeLength[A])(
      using Finite.NotZero[typeLength.Length] =:= true
  ): HKDSumGeneric[A] =
    val labels = Helpers.constValueTupleToIArray[m.MirroredElemLabels, String]
    derivedImpl[A, m.MirroredElemTypes, m.MirroredLabel](
      constValue[m.MirroredLabel],
      labels,
      labels.toSet
    )

  def derivedImpl[A, ElemTypes <: Tuple, Label <: String](
      label: Label,
      namesArr: IArray[String],
      namesSet: Set[String]
  )(
      using m: Mirror.SumOf[A] {
        type MirroredElemTypes = ElemTypes; type MirroredLabel = Label;
      },
      typeLength: TypeLength[A],
      nz: Finite.NotZero[typeLength.Length] =:= true
  ): HKDSumGeneric[A] {
    type TupleRep  = ElemTypes
    type Gen[F[_]] = ProductK[F, TupleRep]
    type Index[_]  = Finite[typeLength.Length]
    type TypeName  = Label
    type ElemTop   = A & Helpers.TupleUnion[TupleRep, Nothing]
  } =
    new HKDSumGeneric[A]:
      type Gen[F[_]] = ProductK[F, TupleRep]
      type Index[_]  = Finite[typeLength.Length]
      type ElemTop = A & Helpers.TupleUnion[TupleRep, Nothing]

      override type TypeName = Label
      override def typeName: TypeName = label

      opaque type Names <: String = String
      override def names: Gen[Const[Names]] = ProductK.ofProductUnsafe(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 def indexOf[X <: ElemTop](x: X): Index[X] = Finite(typeLength.length, m.ordinal(x))

      override def indexOfACasting(a: A): HKDSumGeneric.IndexOfACasting[Index, ElemTop] =
        val idx = indexOfA(a)
        new HKDSumGeneric.IndexOfACasting.IndexOfACastingImpl[Index, ElemTop, ElemTop](idx, a.asInstanceOf[ElemTop])

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

      override def tabulateTraverseK[G[_], B[_]](f: [X] => Finite[typeLength.Length] => G[B[X]])(
          using Applicative[G]
      ): G[Gen[B]] =
        HKDGeneric.tabulateTraverseKImpl(typeLength.length, f)

      override def tabulateTraverseKOption[B[_]](
          f: [X] => Finite[typeLength.Length] => Option[B[X]]
      ): Option[ProductK[B, TupleRep]] = HKDGeneric.tabulateTraverseKOptionImpl(typeLength.length, f)

      override def tabulateTraverseKEither[E, B[_]](
          f: [X] => Finite[typeLength.Length] => Either[E, B[X]]
      ): Either[E, ProductK[B, TupleRep]] = HKDGeneric.tabulateTraverseKEitherImpl(typeLength.length, f)

      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)

      private val instance: BoundedRepresentableKC.Aux[Gen, Index] & TraverseKC[Gen] =
        ProductK.productKInstance[m.MirroredElemTypes](
          using typeLength.asInstanceOf[TypeLength.Aux[m.MirroredElemTypes, typeLength.Length]]
        )

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy