Каковы правила приоритета при выборе неявного значения для функции CanBuildFrom?

Из-за отсутствия лучшего примера предположим, что у меня определен тип контейнера, который принимает единственный параметр типа. Предположим, этот контейнер является оболочкой для списка того же типа. Я хотел бы определить метод в моем новом контейнере, чтобы при выполнении операции он делегировал вызов встроенному списку, но в результате возвращал мой тип контейнера (возможно, с другим параметром типа). Для этого я буду использовать шаблон неявного построителя из коллекций 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, который принимает тип «from» из Foo [_], тип элемента «A» и преобразует его в тип «в» из «Foo [A]». Я бы хотел взять тип «из» списка [_], тип элемента «A» и преобразовать его в тип «в» типа «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] я получаю список [Нить]. Другими словами, он не использует мой неявный, он использует общую версию.

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 имеет наибольший приоритет при выборе коллекции, в которую она будет преобразована. Могу ли я что-то сделать, чтобы повысить свой неявный приоритет?


person Mike    schedule 01.04.2012    source источник


Ответы (1)


Он использует первый подходящий. Поскольку в области видимости уже есть canBuildFrom, который соответствует CanBuildFrom [List [_], C, That], он его использует. Вы можете увидеть это, набрав:

implicitly[CanBuildFrom[List[_], _, _]]
// => res3: scala.collection.generic.CanBuildFrom[List[_], _, Any] = scala.collection.generic.GenTraversableFactory$GenericCanBuildFrom@6a3a191e

Однако вы можете заставить компилятор искать тот, который возвращает Foo, указав тип переменной, в которой вы сохраняете результат:

val y: Foo[String] = x.foo { case x => x.toString }
// => y: Foo[String] = Foo@76faf7d6
person drexin    schedule 01.04.2012
comment
Спасибо за комментарии. Да, я проверил неявное, что было на самом деле раньше, перед публикацией. Дело в том, что я не хочу указывать тип, я хочу, чтобы вывод типа выполнял всю работу. Я думал, что мое неявное должно иметь приоритет, но я думаю, что этого не произойдет, пока он не войдет в метод foo. Единственное решение, которое я могу придумать, - это заменить «That» на «Foo [C]», чтобы тип «to» был явным. Это работает, но проблема в том, что если у меня нет поддержки типа «C» с «Foo», я бы хотел, чтобы он использовал более общий тип (в соответствии с шаблоном CanBuildFrom). Это не сработает. - person Mike; 02.04.2012