Using implicit objects within classes

2020-05-28 10:19发布

问题:

I am trying to write code to represent polynomials within Scala. I need this code to be type polymorphic, so I am using implicits to deal with different types. I have:

case class Mono[T](degree: Int, coeff: T) {
  def Degree: Int = return degree
  def Coeff: T = return coeff
}

class Poly[T](private val terms: List[Mono[T]]) {

  trait Semiring[T] {
    def add(x:T, y:T): T
    def mul(x:T, y:T): T
    def exponent(x: T, n:Int): T
    val unitA: T
  }

  implicit object IntSemiring extends Semiring[Int] {
    def add(x: Int, y: Int): Int = x+y
    def mul(x: Int, y: Int): Int = x*y
    def exponent(x: Int, n:Int): Int = if(n==0) 1 else x*exponent(x, n-1)
    val unitA: Int = 0
  }

  implicit object SetSemiring extends Semiring[Set[Int]] {
    def add(x: Set[Int], y: Set[Int]): Set[Int] = x.union(y)
    def mul(x: Set[Int], y: Set[Int]): Set[Int] = x.intersect(y)
    def exponent(x: Set[Int], n: Int): Set[Int] = x
    val unitA: Set[Int] = Set()
  }

  def eval(x: T)(implicit r: Semiring[T]): T = {
    var termlist = terms
    var sum = r.unitA
    var expression = terms
    while(!termlist.isEmpty) {
      val term = expression.head
      val power = r.exponent(x, term.Degree)
      val termval = r.mul(power, term.Coeff)
      sum = r.add(sum, termval)
      termlist = termlist.tail
    }
    return sum
  }      

  def add(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...

  def mul(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...
}

I chopped out a few functions for brevity there. This compiles fine but when I try and use it I get some strange errors:

scala> val p1 = new Poly(List(Mono(0,1),Mono(1,2),Mono(2,1)))
p1: Poly[Int] = Poly@450ae3fb
scala> p1 eval 3
<console>:9: error: could not find implicit value for parameter r: p1.Semiring[Int]
              p1 eval 3
                 ^

I'm not sure how to fix it. Am I defining the implicit objects in the wrong place? I tried moving them outside the class but then the complier fails. Is there something else I need to do to get it to work properly?

回答1:

Implicit resolution is done at the location where you call the function, not where you define it. You should import your implicits before calling p1.eval:

val p1 = new Poly(List(Mono(0,1),Mono(1,2),Mono(2,1)))
import p1._
p1 eval 3

Since your implicits are not really bound to an instance of Poly, you can define them outside Poly.

If you don't want to import explicitly the Semiring implicits, you can define them in the companion object of Semiring since Scala search for matching implicits in the companion object when they are missing:

case class Mono[T](degree: Int, coeff: T) {
  def Degree: Int = return degree
  def Coeff: T = return coeff
}

class Poly[T](private val terms: List[Mono[T]]) {
  def add(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...

  def mul(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...
}

trait Semiring {
    def add(x:T, y:T): T
    def mul(x:T, y:T): T
    def exponent(x: T, n:Int): T
    val unitA: T
}

object Semiring {
  implicit object IntSemiring extends Semiring[Int] {
    def add(x: Int, y: Int): Int = x+y
    def mul(x: Int, y: Int): Int = x*y
    def exponent(x: Int, n:Int): Int = if(n==0) 1 else x*exponent(x, n-1)
    val unitA: Int = 0
  }

  implicit object SetSemiring extends Semiring[Set[Int]] {
    def add(x: Set[Int], y: Set[Int]): Set[Int] = x.union(y)
    def mul(x: Set[Int], y: Set[Int]): Set[Int] = x.intersect(y)
    def exponent(x: Set[Int], n: Int): Set[Int] = x
    val unitA: Set[Int] = Set()
  }
}

Then you don't need to import them anymore:

val p1 = new Poly(List(Mono(0,1),Mono(1,2),Mono(2,1)))
p1 eval 3