案例类scala的隐式排序

时间:2014-07-23 17:37:25

标签: scala implicit case-class shapeless

案例类在scala中似乎没有隐式排序。

scala> case class A(i:Int)
defined class A

scala> implicitly[Ordering[A]]
<console>:10: error: No implicit Ordering defined for A.
              implicitly[Ordering[A]]

我想知道是否总是为所有案例类定义一个隐式排序,如果不是,至少有一种方法可以为相同成员类型的案例类/案例类的每个arity定义隐式排序。 / p>

2 个答案:

答案 0 :(得分:8)

案例类的排序可以使用shapeless

自动导出
import GenericOrdering._

case class Foo(i : Int, s : String)

implicitly[Ordering[Foo]]
val fs = List(
  Foo(2, "b"),
  Foo(2, "a"),
  Foo(1, "c")
).sorted
assert(fs == List(
  Foo(1, "c"),
  Foo(2, "a"),
  Foo(2, "b")
))

有关完整示例,请参阅here。完整的机制和PartialOrdering的扩展将成为即将发布的2.1.0版本的一部分。

答案 1 :(得分:3)

为了完整起见,我将成为Miles良好解决方案的坏天使。实际上,您可以使用宏轻松地滚动自己的此功能版本:

import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

object OrderingHelper {
  implicit def apply[A]: Ordering[A] = macro apply_impl[A]

  def apply_impl[A: c.WeakTypeTag](c: Context) = {
    import c.universe._

    val A = weakTypeOf[A]

    val fields = A.decls.collect {
      case sym: MethodSymbol if sym.isCaseAccessor => q"a.${sym.name}"
    }

    if (fields.isEmpty) c.abort(c.enclosingPosition, "Not a case class!") else
      A.baseClasses.collectFirst {
        case sym
          if sym.name.decodedName.toString.startsWith("Tuple")
          && sym.owner == typeOf[Any].typeSymbol.owner =>
            c.abort(c.enclosingPosition, "Not needed for tuples!")
      } getOrElse c.Expr[Ordering[A]](q"Ordering.by((a: $A) => (..$fields))")
  }
}

然后:

scala> import OrderingHelper._
import OrderingHelper._

scala> case class B(i: Int, s: String)
defined class B

scala> Ordering[B]
res0: scala.math.Ordering[B] = scala.math.Ordering$$anon$9@2c9df057

scala> Ordering[B].compare(B(1, "foo"), B(1, "bar"))
res1: Int = 1

上面的代码将在REPL中工作,2.11中没有额外的依赖项,对于2.10,您只需要一些小的调整和一个编译器插件(详见my blog post)。

我绝对推荐使用Shapeless方法,但是 - Shapeless为这种通用编程提供了一个更有用的约束工具包。