ひらめの日常

日常のメモをつらつらと

Scala: Ordered[T] と Ordering[T] の違い

結論

  • Ordered[T] は自身に継承し、比較可能にする
  • Ordering[T] は自身を変えることなく、外側から変換を提供する

gist.github.com

Ordered[T]

例えば、単純に比較するだけのクラスを作る。このクラスは Ordered[T] を継承していて、compareメソッドを実装する必要がある。

参考: Scala Standard Library 2.13.5 - scala.math.Ordered

case class MyOrderedNum(value: Int) extends Ordered[MyOrderedNum] {
  override def compare(that: MyOrderedNum): Int = {
    if (this.value < that.value) -1
    else if (that.value < this.value) 1
    else 0
  }
}

こんな感じで呼び出せる。

assert(MyOrderedNum(10) > MyOrderedNum(2)) // => success!

Ordering[T]

implicitなパラメータとして Ordering[T] を与えている。クラスの外側で Ordering[T] の実装を提供することで、比較ができるようになる。この方法だと、クラス自体を変えることなく、外側から実装を追加することができる。

参考: Scala Standard Library 2.13.5 - scala.math.Ordering

ScalaInt, String などもこちらの方法を用いて比較可能にされている。これは、Int のようなプリミティブなクラスに関しては AnyVal を継承しているだけに留めたく、他のクラスを継承したくないという思想があるらしい。

参考: scala - Why does Int not inherit/extend from Ordered[Int] - Stack Overflow

case class MyOrderingNum(value: Int)(implicit ord: Ordering[MyOrderingNum]) {
  def >(that: MyOrderingNum): Boolean = ord.compare(this, that) > 0
}

implicit object MyOrdering extends Ordering[MyOrderingNum] {
  override def compare(x: MyOrderingNum, y: MyOrderingNum): Int = {
    if (x.value < y.value) -1
    else if (y.value < x.value) 1
    else 0
  }
}

こんな感じで呼び出せる。

assert(MyOrderingNum(10) > MyOrderingNum(2)) // => success!

Appendix

Numeric[T]Ordering[T] を継承していて、他にも四則演算をサポートしている。

trait Numeric[T] extends Ordering[T]

参考: Scala Standard Library 2.13.5 - scala.math.Numeric

参考

kmizu.hatenablog.com

masayuki038.github.io