简体   繁体   中英

Accessing class-level values of type parameters in Scala

I have a trait and a case class implementing it. One of the features of the trait that the implementations override is a default value. I can't find a good way to access this default value from a class that is parametrized by a specific implementation of that trait.

This is minimal code, so it doesn't really demonstrate the motivation anymore, but it does demonstrate the error:

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
}

The error I get is

not found: value T

When I needed to construct an Array[T] inside my User class I could get that to work by adding implicit typetag:ClassTag[T] to my arguments, but that doesn't seem to have any effect here.

First, why this doesn't work: consider a different implementation of Distance .

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

What would you expect DoubleDistance1.Zero to mean? You can make it work using a "type class":

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
}

This looks like a classic use case for the type class pattern . Rather than associating an instance of the Distance trait with each value of interest, you associate one with each type of interest:

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 
}

Where is that Zero supposed to come from? Are you looking to do something like this?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM