简体   繁体   中英

Is it possible in scala to pass on a generic type dynamically?

I want to model arithmetic operations for the sake of writing a DSL.

A multiply operation on an Int and a Double should obviously lead to a Double , on an Int and a Int it should lead to an Int and so on.

Is there any way in scala to decide a type and pass it on dynamically? In below code I added, as a way of demonstration the function call arithmeticReturnType(TL,TR) which should then be replaced with something that would actually work in Scala.

trait NumericCol[Repr]

case class Divide[R](l: NumericCol[_], r: NumericCol[_]) extends NumericCol[R]

def divide[TL,TR](left: NumericCol[TL], right: NumericCol[TR]) = Divide[**arithmeticReturnType(TL,TR)**](left, right)

If you ever looked into how scala collections are implemented, you probably have noticed all the "CanBuildFrom"-thingies that are used to determine the most specific return type for operations on collections. You can apply the same trick to ensure type safety in your DSL:

trait CanDivideInto[A, B, C] {
  def divide(a: A, b: B): C
}

implicit object DoubleIntCanDivideIntoDouble 
extends CanDivideInto[Double, Int, Double] {
  def divide(a: Double, b: Int): Double = a / b
}

implicit object InIntCanDivideIntoInt
extends CanDivideInto[Int, Int, Int] {
  def divide(a: Int, b: Int): Int = a / b
}

trait NumericCol[Repr]

case class Divide[A, B, C](
  l: NumericCol[A], 
  r: NumericCol[B], 
  divider: CanDivideInto[A, B, C]
) extends NumericCol[C]

def divide[A, B, C](
  left: NumericCol[A], 
  right: NumericCol[B]
)(implicit div: CanDivideInto[A, B, C]): NumericCol[C] = 
  Divide[A, B, C](left, right, div)

val a = new NumericCol[Double]{}
val b = new NumericCol[Int]{}

val c: NumericCol[Double] = divide(a, b)
// val nope: NumericCol[Int] = divide(a, b) // won't compile, good.

The different cases like Int/Double , Double/Int , Int/Int etc. might lead to some boilerplate, but I don't see any way around it, because dividing integers by doubles or integers by floats or integers by integers should result in different assembly instructions in the end of the day...

Type classes is one possibility, but in this case I don't see why not just use function overloads. It does the job, and saves a bunch of boilerplate:

def divide(a: NumericColumn[Int], b: NumericColumn[Int]) = Divide[Int](a,b)
def divide(a: NumericColumn[Double], b: NumericColumn[Double]) = Divide[Double](a,b)

(I think, these two is all you need to cover the divide case)

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