2017-01-02 1 views
1

Рассмотрим следующую REPL сессию:CanBuildFrom не найден, когда вызывается неявно

@ def test[C[X] <: TraversableOnce[X]](implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = cbf() 
defined function test 

@ test[List] 
res32: collection.mutable.Builder[Int, List[Int]] = ListBuffer() 

@ def test[C[X] <: TraversableOnce[X]] = implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 
cmd33.sc:1: Cannot construct a collection of type C[Int] with elements of type Int based on a collection of type C[Int]. 
def test[C[X] <: TraversableOnce[X]] = implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 
               ^
Compilation Failed 

Первое определение test функции компилирует и работает, а второй один не компилируется. Единственная разница между ними - это способ получения экземпляра CanBuildFrom. В первом случае он объявлен как неявный параметр, требующий от компилятора его найти. Во втором случае он вызывается функцией implicitly, которая теоретически должна вести себя одинаково с точки зрения неявного поиска. Что вызывает такое поведение?

ответ

1

Определение implicitlyPredef) является:

def implicitly[A](implicit ev: A): A = A 

Он просто делает явным вам неявное уже в рамках (в использовании сайта).

Теперь, когда вы пишете это:

import collection.generic.CanBuildFrom 

def test[C[X] <: TraversableOnce[X]] 
    (implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = ??? 

Вы спрашиваете, звонящих на обеспечить неявное (на месте вызова).

Когда вы пишете

def test[C[X] <: TraversableOnce[X]] = 
    implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 

Вы просите компилятор с вызовом implicitly смотреть вверх неявное уже в рамках. Но у вас нет никакого подразумеваемого данного типа в области! Итак, два определения test делают что-то совершенно другое.

Обычно вы используете implicitly, чтобы получить неявный, для которого у вас нет имени, потому что оно было задано с использованием ограничений контекста или типа, например def test[A: TypeClass]. Вы не можете использовать эту нотацию здесь, потому что CanBuildFrom имеет три параметра типа, а не один. Таким образом, вы не можете многое сделать с implicitly здесь.

Вы можете использовать implicitly с первым случаем:

def test[C[X] <: TraversableOnce[X]] 
    (implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = { 

    implicit val onceAgain = implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 
    assert(onceAgain == cbf) 
} 

Но тогда вы уже знаете, что у вас есть, что неявный с именем cbf ...


Обратите внимание, что вы можете разжиться неявный CanBuildFrom для известного типа коллекции:

implicitly[CanBuildFrom[List[Int], Int, List[Int]]] // works! 

Но это не работает, если ваш тип коллекции (C[X]) является абстрактным.

+0

Но в чем разница между неявными областями в этих двух ситуациях? Как я уже сказал, я сделал это в REPL, поэтому в первом случае был найден общий «CanBuildFrom» (в Predef, я полагаю), когда я вызвал функцию. Это означает, что в области видимости имеется подходящий неявный экземпляр «CanBuildFrom». Почему один и тот же нельзя использовать при определении автономной функции в REPL? – Haspemulator

+0

В одном случае у вас есть конкретный тип, например. 'List', в другом вы имеете абстрактный тип параметра' C [_] '.Неявное разрешение будет выглядеть, среди прочих, у сопутствующего объекта класса типа («CanBuildFrom') и его параметров типа (например, здесь« List »- где он находит экземпляр типа!), Но, очевидно, не может разрешить абстрактный тип , –

+0

Правильно, теперь это имеет смысл. – Haspemulator