#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
}