Scala Generics и числовые последствия - PullRequest
8 голосов
/ 14 октября 2011

Мне нужно передать две функции в качестве параметров функции scala. Эта функция должна затем оценить их и получить число от них, где она затем будет работать. Это число может быть Int, Double или любым другим числовым типом. Я бы хотел, чтобы функция работала независимо от того, с какими типами она работает.

Пример ниже объясняет проблему.

import Numeric.Implicits._

class Arithmetic[T : Numeric](val A: Connector[T], val B: Connector[T]) {
  val sum  = new Connector({ A.value + B.value })
}

class Constant[T](var x: T) {
  val value = new Connector({ x })
}

class Connector[T](f: => T) {
  def value: T = f
  override def toString = value.toString()
}

object Main extends App{
  val n1 = new Constant(1)

  // works
  val n5 = new Constant(5)
  val a = new Arithmetic( n1.value, n5.value )
  println(a.sum)

 // no works
 val n55 = new Constant(5.5)
 val b = new Arithmetic( n1.value, n55.value )
 println(b.sum)

}

Я тоже пробовал

class Arithmetic[T,R : Numeric](val A: Connector[T], val B: Connector[R]) {

и несколько других комбинаций, но я закончил с

error: could not find implicit value for parameter num: scala.math.Numeric[Any]
val sum  = new Connector({ A.value + B.value })

1 Ответ

4 голосов
/ 14 октября 2011

Вы видите сообщение об ошибке, потому что Numeric[T].plus может использоваться только для добавления двух значений одного типа T. Ваш код написан в предположении, что расширение чисел происходит автоматически - что не произойдет в этом случае, так как компилятор ничего не знает о типах, за исключением того, что существует экземпляр Numeric[T].

Если вам нужно, чтобы sum было стабильным значением, вы должны будете предоставить необходимую информацию о типе в конструкторе следующим образом:

class Arithmetic[A : Numeric, R <% A, S <% A](val a: Connector[R], b: Connector[S]) {
  val sum = new Connector[A]((a.value:A) + (b.value:A))
}

Для этого требуется, чтобы типы R и S были преобразованы в некоторый тип A, для которого известно значение Numeric[A]. При создании экземпляра вам всегда нужно будет указывать все параметры типа, поскольку они не могут быть выведены.

Если вам не нужна sum для стабильности, вы можете изменить свой класс на:

class Arithmetic[A,B](val a: Connector[A], val b: Connector[B]) {

  // if A and B are the same types
  def sum(implicit e: B =:= A, n: Numeric[A]): Connector[A] =
    new Connector(n.plus(a.value, b.value))

  // else widen to C
  def wideSum[C](implicit f: A => C, g: B => C, n: Numeric[C]) =
    new Connector(n.plus(a.value, b.value))
}

val a = new Connector(1)

val b = new Connector(2)

val c = new Connector(3.0)

val d = (new Arithmetic(a,b)).sum

// val e = (new Arithmetic(b,c)).sum // <-- does not compile

val e = (new Arithmetic(b,c)).wideSum[Double]

При расширении вам все равно придется предоставить информацию о типе.

...