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

japgolly.microlibs.cats_ext.CatsMacros.scala Maven / Gradle / Ivy

There is a newer version: 4.2.1
Show newest version
package japgolly.microlibs.cats_ext

import japgolly.microlibs.compiletime.MacroEnv.*
import scala.compiletime.*
import scala.deriving.*
import scala.quoted.*
import cats.Eq

object CatsMacros:

  inline def deriveEq[A]: Eq[A] =
    ${ deriveEqImpl[A](false) }

  inline def _deriveEq[A]: Eq[A] =
    ${ deriveEqImpl[A](true) }

  private def deriveEqImpl[A](debug: Boolean)(using Quotes, Type[A]): Expr[Eq[A]] =
    var result: Expr[Eq[A]] = null
    def log(msg: => Any) = if debug then println(msg)
    log("="*120)
    log(s"Beginning derivation of cats.Eq[${Type.show[A]}]")

    type Clause = MacroUtils.Fn2Clause[A, A, Boolean]

    def newInstance(f: Clause): Quotes ?=> Expr[Eq[A]] = '{
      Eq.instance[A]((x, y) => ${f('x, 'y)})
    }

    Expr.summon[Mirror.Of[A]] match

      case Some('{ $m: Mirror.ProductOf[A] }) =>
        result = MacroUtils.CachedGivens[Eq].mirror(m).summonGivens().use[Eq[A]] { ctx =>

          val clauses =
            ctx.fields.map[Clause](f => (x, y) => {
              val eq = ctx.lookup(f).expr
              '{ $eq.eqv(${f.onProduct(x)}, ${f.onProduct(y)}) }
            })

          MacroUtils.mergeFn2s(
            fs    = clauses,
            empty = Left(Expr(true)),
            merge = (x, y) => '{ $x && $y },
            outer = newInstance,
          )
        }

      case Some('{ $m: Mirror.SumOf[A] }) =>
        result = MacroUtils.CachedGivens[Eq].mirror(m).summonGivens().forSumType(m) { f =>
          newInstance { (x, y) => '{
              val o = ${f.ordinalOf(x)}
              (o == ${f.ordinalOf(y)}) && ${f.typeclassForOrd('o)}.eqv($x, $y)
            }
          }
        }

      case _ =>

    if result == null then
      val err = s"Don't know how to derive an Eq instance for ${Type.show[A]}: Mirror not found."
      log(err)
      log("="*120)
      quotes.reflect.report.throwError(err)
    else
      log(result.show)
      log("="*120)
      result




© 2015 - 2024 Weber Informatics LLC | Privacy Policy