Home > database >  What does `import ev._` for implicit evidence mean, and when should I use it?
What does `import ev._` for implicit evidence mean, and when should I use it?

Time:11-17

the Arithmetic.scala is:

package Arithmetic
// The Arithmetic typeclass which implements various arithmetic operations on custom datatypes
abstract class Arithmetic[T <: Data] {
  implicit def cast(t: T): ArithmeticOps[T]
}

abstract class ArithmeticOps[T <: Data](self: T) {
  def *(t: T): T
  def mac(m1: T, m2: T): T // Returns (m1 * m2   self)
  def  (t: T): T
  ...
}

object Arithmetic {
  implicit object UIntArithmetic extends Arithmetic[UInt] {
    override implicit def cast(self: UInt) = new ArithmeticOps(self) {
      ...
    }  

  implicit object SIntArithmetic extends Arithmetic[SInt] {
    override implicit def cast(self: SInt) = new ArithmeticOps(self) {
      ...
    }
}

in another scala file:

import Arithmetic._

class PE[T <: Data](inputType: T, ...)(implicit ev: Arithmetic[T]) extends Module {
  import ev._
  ...
}

my question is:

  1. Can an abstract class has a companion object?(such as the Arithmetic)
  2. I know there is some implicit conversion. what makes me confused is that when I use import Arithmetic._, what is the ev refer to? the companion object or the abstract class?

CodePudding user response:

First, regarding whether we can define an object Arithmetic if there is already a trait / class Arithmetic: yes. There are two independent kinds of names: type-names and value-names. Abstract class declarations and trait declarations such as

abstract class Arithmetic[A] // or 
trait Arithmetic[A]

introduce a named type with name Arithmetic. The companion object declaration

object Arithmetic

introduces a value name Arithmetic. The type-names and the value-names live in separate realms, and do not collide.

Having a companion object Foobar for a trait Foobar[X] is quite common if Foobar[X] is modeling a typeclass, because the compiler will look for instance definitions preferentially in the object Foobar.


Second, regarding the various imports:

import Arithmetic._

is just a package import. Confusingly, because you have a classArithmetic.Arithmetic and object Arithmetic.Arithmetic, this brings the class and the object into scope.

On the other hand, this here

import ev._

says essentially: "In this block of code, when you need or *, go ask the ev: Arithmetic[T] how and * are defined".

How ev gets instantiated depends on the type T at the instantiation site where PE[T] is constructed. If you're constructing

val myUInt: UInt = ...
new PE[UInt](myUInt)

, you're essentially telling the compiler: "Dear compiler, I'm lazy, go search for an instance of Arithmetic[UInt] in some of the packages that I've imported". The compiler will go and see where it can get an compilerGenerated_ev: Arithmetic[UInt], and pass it automatically as the implicit argument:


val myUInt: UInt = ...

//            
  • Related