Первая проблема с вашим кодом — это небольшое упущение: вам нужно импортировать члены экземпляра Numeric
, чтобы ввести +
в область видимости (на самом деле это поместит num.mkNumericOps
в область видимости, включив метод +
): Давайте попробуем это:
def defaultParamFunc[B](z: B, y: B)(f: (B, B) => B = (v1: B,v2: B) => { v1 + v2 })(implicit num: Numeric[B]) = {
// Brings `+` into scope.
// Note that you can also just import Numeric.Implicits._ for the same effect
import num._
val ans = f(z,y)
println("ans : " + ans)
ans
}
К сожалению, это все еще не компилируется. Проблема здесь в том, что значение параметра по умолчанию может ссылаться только на параметр из более раннего списка параметров. Поскольку num
находится в последнем списке параметров, его невозможно использовать в значении по умолчанию. Вот незадача.
Итак, давайте попробуем перехитрить компилятор и разделить метод на две части, чтобы мы могли иметь неявный параметр num
перед параметром f
:
def defaultParamFunc[B](z: B, y: B)(implicit num: Numeric[B]) = new {
// NOTE: using structural typing here is rather inefficient.
// Let's ignore that for now.
import num._
def apply(f: (B, B) => B = (v1: B, v2: B) => { v1 + v2 }) = {
val ans = f(z,y)
println("ans : " + ans)
ans
}
}
Сладко, он компилируется. Что мы сделали здесь, так это то, что defaultParamFunc
фактически возвращает (псевдо) экземпляр функции. Эта функция принимает f
в качестве параметра, и, поскольку мы создаем экземпляр функции в теле defaultParamFunc
, нет проблем со ссылкой на num
.
Однако не будем рано радоваться. Если мы попытаемся вызвать его, не указывая параметр f
, компилятору это не понравится:
scala> defaultParamFunc(5, 7)()
<console>:17: error: not enough arguments for method defaultParamFunc: (implicit num: Numeric[Int])((Int, Int) => Int) => Int{def apply$default$1: (Int, Int) => Int}.
Unspecified value parameter num.
defaultParamFunc(5, 7)()
Упс. Компилятор считает, что пустой список параметров относится ко второму (неявному) списку параметров в defaultParamFunc
, а не к списку параметров экземпляра (псевдо) функции.
Придется найти другой способ, не требующий неявного параметра в методе defaultParamFunc
. Магнитные узоры спешат на помощь!
trait MyMagnet[B] extends ((B,B) => B)
object MyMagnet {
implicit def fromFunc[B]( f: (B, B) => B ) = new MyMagnet[B] {
def apply(z: B, y: B): B = {
val ans = f(z,y)
println("ans : " + ans)
ans
}
}
implicit def fromUnit[B](unit: Unit)(implicit num: Numeric[B]) = new MyMagnet[B]{
import num._
def apply(v1: B, v2: B): B = { v1 + v2 }
}
}
def defaultParamFunc[B](z: B, y: B)(magnet: MyMagnet[B]): B = magnet(z, y)
Конечно, это немного надумано для такого крошечного результата. Но это работает:
scala> defaultParamFunc(2,3)()
res15: Int = 5
scala> defaultParamFunc(2,3){(x:Int, y:Int) => x*y }
ans : 6
res16: Int = 6
Однако обратите внимание, что, используя шаблон магнита, мы потеряли преимущества вывода типов. Поэтому мы не можем просто сделать следующее:
scala> defaultParamFunc(2,3)(_ * _)
<console>:18: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$times(x$2))
defaultParamFunc(2,3)(_ * _)
person
Régis Jean-Gilles
schedule
19.04.2014