8

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?

Joe
  • 365
  • 5
  • 11
  • You might want to look at Spire too: https://github.com/non/spire: "Spire is a numeric library for Scala which is intended to be generic, fast, and precise. Using features such as specialization, macros, type classes, and implicits, Spire works hard to defy conventional wisdom around performance and precision trade-offs. A major goal is to allow developers to write efficient numeric code without having to "bake in" particular numeric representations. In most cases, generic implementations using Spire's specialized type classes perform identically to corresponding direct implementations." – James Moore May 14 '16 at 14:05

1 Answers1

16

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
Stephane Godbillon
  • 1,856
  • 13
  • 12
  • I see. Is there any way to get the to happen automatically though, without me having to explicitly call import p1._ ? – Joe May 13 '13 at 12:48