How to convert an Int into Ordered (Scala)

154 Views Asked by At

I have declared a Value class in Scala as:

sealed abstract class Value extends Expression with Ordered[Value] {
  type T <: Ordered[T]
  def value: T

  override def compare(that: Value): Int = { ... } 
}

The reason for the type constraint T <: Ordered[T] is that I want to delegate the compare method in Value to value: T.

And then a concrete IntValue as:

case class IntValue(value: Int) extends Value {
  type T = Int
  // ... 
}

However, when I try to compile this code I get the following: ''

... incompatible type in overriding
[error] type T <: Ordered[IntValue.this.T] (defined in class Value);
[error]  found   : Int
[error]  required:  <: Ordered[IntValue.this.T]
[error]     (which expands to)   <: Ordered[Int]
[error]   type T = Int

I think that I should use some implicit conversion here, but it is not clear to me how to proceed.

1

There are 1 best solutions below

3
On

The error is about incompatible type in overriding, not about implicits.

You should remove upper bound type T <: Ordered[T].

Int doesn't extend Ordered.

Try type parameter rather than type member

sealed abstract class Value[T] extends /*Expression with*/ Ordered[Value[T]] {
  def value: T
  override def compare(that: Value[T]): Int
}

case class IntValue(value: Int) extends Value[Int] {
  override def compare(that: Value[Int]): Int = value.compare(that.value)
}

Similar code with type member

sealed abstract class Value extends /*Expression with*/ Ordered[Value {type T = self.T}] { self =>
  type T
  def value: T   
  override def compare(that: Value {type T = self.T}): Int
}

case class IntValue(value: Int) extends Value {
  type T = Int  
  override def compare(that: Value {type T = Int}): Int = value.compare(that.value)
}

is not possible because of Error: illegal cyclic reference.

Scala: Abstract types vs generics

https://typelevel.org/blog/2015/07/13/type-members-parameters.html

https://www.youtube.com/watch?v=R8GksuRw3VI

https://tpolecat.github.io/2015/04/29/f-bounds.html


If you want to delegate then you should use type class Ordering rather than OOP trait Ordered. Int doesn't extend Ordered but there is an instance of Ordering for Int

sealed abstract class Value[T] {
  def value: T
}

case class IntValue(value: Int) extends Value[Int]

implicit def valueOrdering[T, A](implicit 
  ev: A <:< Value[T], 
  ordering: Ordering[T]
): Ordering[A] = ordering.on(_.value)

import Ordering.Implicits._
IntValue(1) < IntValue(2) // -1

or

import Ordered._
IntValue(1) < IntValue(2) // true
IntValue(1).compare(IntValue(2)) // -1

Approach with type class can be used for type member

sealed abstract class Value {
  type T
  def value: T
}

case class IntValue(value: Int) extends Value {
  override type T = Int
}

implicit def valueOrdering[A <: Value](implicit 
  ordering: Ordering[A#T]
): Ordering[A] = ordering.on(_.value)

// implicit def valueOrdering[_T, A](implicit 
//   ev: A <:< Value { type T = _T }, 
//   ordering: Ordering[_T]
// ): Ordering[A] = ordering.on(_.value)