2012-04-01 3 views
0

Из-за отсутствия лучшего примера предположим, что у меня есть тип контейнера, который принимает один тип параметра. Предположим, что этот контейнер обертывает список того же типа. Я хотел бы определить метод в моем новом контейнере таким образом, что всякий раз, когда выполняется операция, он делегирует вызов во встроенный список, но в результате он возвращает мой тип контейнера (возможно, другого параметра типа). Для этого я буду использовать шаблон неявного построителя из коллекций scala. Вот основная структура:Каковы правила приоритета при выборе неявного использования для функции CanBuildFrom

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 
    } 
} 

Так что это работает, как я ожидал бы возвращая когда мой пф переходит из Int в строку Foo [String]:

scala> val f = new Foo(List(1,2,3)) 
f: Foo[Int] = [email protected] 

scala> f.foo { case x => x.toString } 
res318: Foo[java.lang.String] = [email protected] 

Хотя предыдущий пример был основан на имеющем CanBuildFrom, который принимает тип «от» Foo [_], тип элемента «A» и преобразуется в тип «to» типа «Foo [A]». То, что я хотел бы сделать, это взять «из» типа List [_], тип элемента «A» и преобразовать в «to» тип «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], я получаю List [String]. Другими словами, он не использует мой неявный, он использует общую версию.

scala> val f = new Foo(List(1,2,3)) 
f: Foo[Int] = [email protected] 

scala> f.foo { case x => x.toString } 
res319: List[java.lang.String] = List(1, 2, 3) 

Итак, мой вопрос: почему? Я бы подумал, что если мой текущий тип - Foo, и я конвертирую в Foo, и есть неявный fn в области, соответствующий входным параметрам (List в этом случае), тогда это будет наилучшее совпадение. Я делаю что-то неправильно или это по соображениям безопасности, что коллекция «из» имеет наибольшее преимущество в выборе того, для какой коллекции она конвертируется. Есть ли что-то, что я могу сделать, чтобы повысить приоритет моего неявного?

ответ

0

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

implicitly[CanBuildFrom[List[_], _, _]] 
// => res3: scala.collection.generic.CanBuildFrom[List[_], _, Any] = sca[email protected]6a3a191e 

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

val y: Foo[String] = x.foo { case x => x.toString } 
// => y: Foo[String] = [email protected] 
+0

Спасибо за комментарии , Да, я проверил неявный, который был на самом деле в области ранее до публикации. Дело в том, что я не хочу указывать тип, я хочу, чтобы тип дела выполнял работу. Я думал, что мой неявный должен иметь приоритет, но я думаю, что это будет не так, пока он не вступит в метод foo. Единственное решение, о котором я могу думать, это заменить «Это» на «Foo [C]», поэтому тип «to» явно. Это работает, но проблема в том, что если у меня нет поддержки типа «C» с «Foo», я бы хотел, чтобы он использовал более общий тип (в соответствии с шаблоном CanBuildFrom). Это не сработает. – Mike

Смежные вопросы