Доступ к значениям параметров типа на уровне класса в Scala

#scala

#scala

Вопрос:

У меня есть признак и класс case, реализующий его. Одной из особенностей признака, которую переопределяют реализации, является значение по умолчанию. Я не могу найти хороший способ получить доступ к этому значению по умолчанию из класса, который параметризуется конкретной реализацией этого признака.

Это минимальный код, поэтому он больше не демонстрирует мотивацию, но он демонстрирует ошибку:

 import scala.language.implicitConversions

trait Distance[T] {
  val Zero: T
  def  ( that: T ): T
}

case class DoubleDistance( val v: Double ) extends Distance[DoubleDistance] {
  val Zero = DoubleDistance( 0.0 )
  def  ( that: DoubleDistance ) = DoubleDistance( v   that.v )
}

object DistanceImplicits {
  implicit def DoubleToDistance( v: Double ) = new DoubleDistance( v )
}

class User[T<:Distance[T]] {
  val default: T = T.Zero // This line gives me a compilation error
}
  

Ошибка, которую я получаю,

 not found: value T
  

Когда мне нужно было создать Array[T] внутри моего User класса, я мог заставить это работать, добавив implicit typetag:ClassTag[T] к своим аргументам, но, похоже, это здесь не имеет никакого эффекта.

Комментарии:

1. Как только стало ясно, что классы типов — это та концепция, которая мне нужна, я нашел эту статью очень полезной в дополнение к той, на которую ссылался Аарон.

Ответ №1:

Во-первых, почему это не работает: рассмотрим другую реализацию Distance .

 case class DoubleDistance1(val v: Double) extends Distance[DoubleDistance1] {
  val Zero = this
  def  (that: DoubleDistance1) = ??? // doesn't matter
}
  

Что бы вы ожидали DoubleDistance1.Zero иметь в виду? Вы можете заставить его работать, используя «класс типов»:

 trait DistanceOps[T] {
  val zero: T
  def add(d1: T, d2: T): T
}

// just to let you write distance1   distance2
implicit class RichDistance[T](d: T)(implicit ops: DistanceOps[T]) {
  def  (other: T) = ops.add(d, other)
}

case class DoubleDistance(v: Double)

object DoubleDistance {
  implicit object DoubleDistanceOps extends DistanceOps[DoubleDistance] {
    val zero = DoubleDistance(0.0)
    def add(d1: DoubleDistance, d2: DoubleDistance) = DoubleDistance(d1.v   d2.v)
  }
}

// how to use
class User[T](implicit ops: DistanceOps[T]) {
  val default: T = ops.zero
}
  

Ответ №2:

Это выглядит как классический вариант использования шаблона класса типа. Вместо того, чтобы связывать экземпляр Distance признака с каждым интересующим значением, вы связываете его с каждым интересующим типом:

 trait Distance[T] {
  val Zero: T
  def  ( a: T, b: T ): T
}

implicit object DoubleDistance extends Distance[Double] {
  val Zero = 0.0
  def  ( a: Double, b: Double ) = a   b
}

class User[T : Distance] {
  val default: T = implicitly[Distance[T]].Zero 
}
  

Ответ №3:

Откуда должен взяться этот ноль? Вы хотите сделать что-то подобное?

 class User[T<:Distance[T]] {
  self:Distance[T] =>
  val default: T = Zero
}