0

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 Answers1

3

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)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thanks for your clarifications @DmytroMitin. The reason for the type constraint T <: Ordered[T] is that I want to delegate the `compare` method in Value to the "field" `value: T`. – Rodrigo Bonifacio Aug 18 '21 at 15:59
  • 1
    @RodrigoBonifacio Well, `Int` doesn't extend `Ordered`. 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`. – Dmytro Mitin Aug 18 '21 at 16:23
  • 1
    @RodrigoBonifacio By the way, approach with type class can be used for type member. See one more update. – Dmytro Mitin Aug 18 '21 at 17:19