В отсутствие лучшего примера, скажем, у меня определен тип контейнера, который принимает параметр одного типа. Предположим, этот контейнер упаковывает список того же типа. Я хотел бы определить метод в моем новом контейнере так, чтобы при каждом выполнении операции он делегировал вызов встроенному списку, но в результате возвращал мой тип контейнера (возможно, с другим параметром типа). Для этого я буду использовать шаблон неявного построителя из коллекций Scala. Вот основная структура:
class Foo[A](val data: List[A]) {
def foo[C, That](pf: PartialFunction[A, C])(
implicit bf: CanBuildFrom[Foo[_], C, That]
): That = {
bf(new Foo(data.collect(pf))).result
}
}
object Foo {
def newBuilder[A]: Builder[A, Foo[A]] =
new ArrayBuffer[A] mapResult { r => new Foo(r.toList) }
implicit def canBuildFrom[A]: CanBuildFrom[Foo[_], A, Foo[A]] =
new CanBuildFrom[Foo[_], A, Foo[A]] {
def apply(from: Foo[_]) = newBuilder
def apply() = newBuilder
}
}
Так что это работает так, как я ожидаю, возвращая Foo [String], когда мой pf преобразуется из Int в String:
scala> val f = new Foo(List(1,2,3))
f: Foo[Int] = Foo@15172301
scala> f.foo { case x => x.toString }
res318: Foo[java.lang.String] = Foo@6ff763fa
В то время как предыдущий пример был основан на использовании CanBuildFrom, который принимает тип 'Foo' из _ ', тип элемента' A 'и преобразовывает в тип' Foo 'в' Foo [A] '. То, что я хотел бы сделать, это взять тип «из» List [_], тип элемента «A» и преобразовать в «F» тип «Foo [A]». Что-то вроде этого:
class Foo[A](val data: List[A]) {
def foo[C, That](pf: PartialFunction[A, C])(
implicit bf: CanBuildFrom[List[_], C, That]
): That = {
data.collect(pf)(bf)
}
}
object Foo {
def newBuilder[A]: Builder[A, Foo[A]] =
new ArrayBuffer[A] mapResult { r => new Foo(r.toList) }
implicit def canBuildFrom[A]: CanBuildFrom[List[_], A, Foo[A]] =
new CanBuildFrom[List[_], A, Foo[A]] {
def apply(from: List[_]) = newBuilder
def apply() = newBuilder
}
}
Здесь я передал свой неявный параметр CanBuildFrom в класс List напрямую, чтобы он мог построить мой класс Foo для хранения результатов. Однако, когда я запускаю тот же тест, вместо получения Foo [String], я получить список [String]. Другими словами, он не использует мой неявный, он использует универсальную версию.
scala> val f = new Foo(List(1,2,3))
f: Foo[Int] = Foo@236f7a59
scala> f.foo { case x => x.toString }
res319: List[java.lang.String] = List(1, 2, 3)
Итак, мой вопрос: почему? Я бы подумал, что если мой текущий тип - Foo, и я конвертирую в Foo, и в области видимости есть неявный fn, соответствующий типам входных параметров (в данном случае List), то это будет наилучшим соответствием. Я делаю что-то не так или это по соображениям безопасности, что коллекция 'from' имеет наибольшее преимущество при выборе коллекции, в которую она преобразуется. Есть ли что-то, что я могу сделать, чтобы повысить приоритет моего скрытого?