Scala 类型安全等于宏?

Scala 类型安全等于宏?,scala,macros,equals,Scala,Macros,Equals,Scala是否有一个类型安全的equals==实现,其开销为零?也就是说,与Scalaz和ScalaUtils中的=不同,Scalaz和ScalaUtils是一种使用直接宏执行检查的实现 我想在很多地方使用==,但这些地方都是热点,所以我不希望这会带来任何额外的运行时成本(比如构造类型类等)。我认为您可以轻松地使用 GitHub上的自述文件给出了确切的示例: import scala.{specialized => sp} import machinist.DefaultOps tra

Scala是否有一个类型安全的equals
==
实现,其开销为零?也就是说,与Scalaz和ScalaUtils中的
=
不同,Scalaz和ScalaUtils是一种使用直接宏执行检查的实现


我想在很多地方使用
==
,但这些地方都是热点,所以我不希望这会带来任何额外的运行时成本(比如构造类型类等)。

我认为您可以轻松地使用

GitHub上的自述文件给出了确切的示例:

import scala.{specialized => sp}

import machinist.DefaultOps

trait Eq[@sp A] {
  def eqv(lhs: A, rhs: A): Boolean
}

object Eq {
  implicit val intEq = new Eq[Int] {
    def eqv(lhs: Int, rhs: Int): Boolean = lhs == rhs
  }

  implicit class EqOps[A](x: A)(implicit ev: Eq[A]) {
    def ===(rhs: A): Boolean = macro DefaultOps.binop[A, Boolean]
  }
}
然后您可以在
==
上使用开销为零的
==
(无额外分配,无额外间接寻址)


如果您正在寻找开箱即用的实现(machinist的发源地)提供了一个

也提供了一个


它们都是基于宏的,因为它们使用了
machinist
来实现。

基于machinist的答案可能是最好的。这里有一个更为粗俗的变体,用于检测诸如推断
Any
AnyRef
或两个不相关案例类的典型组合(
可序列化的产品)
)之类的案例:

导入scala.collection.breakOut
导入scala.language.experimental.macros
导入scala.reflect.macros.blackbox
对象隐式{
隐式类三重相等[A](A:A){
def==[B>:A](B:B):布尔=宏宏。equalsImpl[A,B]
}
}
对象宏{
private val positiveList=Set(“scala.Boolean”、“scala.Int”、“scala.Long”,
“scala.Float”、“scala.Double”、“scala.Option”)
private val negativeList=Set(“java.lang.Object”、“java.io.Serializable”,
"")
def equalsImpl[A:c.WeakTypeTag,B:c.WeakTypeTag](c:blackbox.Context)
(b:c.Expr[A]):c.Tree={
导入c.universe_
val bTpe=weakTypeOf[B]
val base=bTpe.baseClasses
val名称:Set[String]=base.collect{
如果sym.isClass=>sym.fullName,则为案例sym
}(突破)
//如果推断出一个基元,我们就很好。否则:
if(names.intersect(positiveList.isEmpty){
//排除所有,如scala.Product、scala.Equals
val withoutTopLevel=names.filterNot{n=>
val i=n.lastIndexOf('.'))
i==5&&n.startsWith(“scala”)
}
//排除细化和已知Java类型
val excl=无顶层差异(负列表)
如果(不包括i空){
c、 中止(c.EnclosuringPosition,s“推断类型太泛型:`$bTpe`”)
}
}
//现在简单地重写为'a==b'`
val q“$(($a)”=c.prefix.tree
q“$a==$b”
}
}
这不适用于更高级的类型,因此元组故意失败,而不幸的是
Some(1)==Some(“hello”)
编译



编辑:一个在此基础上改进以支持更高级类型的构建。

相对于隐式值类,宏实现能为您带来什么?@TravisBrown值类节省分配成本,但不节省间接成本,如果我没有弄错的话。这可能是相关的:spire和cats中的实现都需要不过,类型类实例,所以您必须注意不要在每次使用时都实例化这些实例,等等@TravisBrown,我不确定是否遵循。只要您不使用
ev
from
EqOps
就可以了,因为类型类只被用作等式操作可用性的证明。我错了吗?
import scala.collection.breakOut
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object Implicits {
  implicit class TripleEquals[A](a: A) {
    def === [B >: A](b: B): Boolean = macro Macros.equalsImpl[A, B]
  }
}

object Macros {
  private val positiveList = Set("scala.Boolean", "scala.Int", "scala.Long",
                                 "scala.Float", "scala.Double", "scala.Option)
  private val negativeList = Set("java.lang.Object", "java.io.Serializable",
                                 "<refinement>")

  def equalsImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: blackbox.Context)
                                                    (b: c.Expr[A]): c.Tree = {
    import c.universe._
    val bTpe = weakTypeOf[B]
    val base = bTpe.baseClasses
    val names: Set[String] = base.collect {
      case sym if sym.isClass => sym.fullName
    } (breakOut)

    // if a primitive is inferred, we're good. otherwise:
    if (names.intersect(positiveList).isEmpty) {
      // exclude all such as scala.Product, scala.Equals
      val withoutTopLevel = names.filterNot { n =>
        val i = n.lastIndexOf('.')
        i == 5 && n.startsWith("scala")
      }
      // exclude refinements and known Java types
      val excl = withoutTopLevel.diff(negativeList)
      if (excl.isEmpty) {
        c.abort(c.enclosingPosition, s"Inferred type is too generic: `$bTpe`")
      }
    }

    // now simply rewrite as `a == b`
    val q"$_($a)" = c.prefix.tree
    q"$a == $b"
  }
}