2017-01-02 1 views
1

Tenez compte de la session REPL suivante:CanBuildFrom introuvable lorsqu'il est invoqué implicitement

@ 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 

La première définition de test fonction et compile des œuvres, tandis que le second ne compile pas. La seule différence entre eux est la façon dont l'instance de CanBuildFrom est obtenue. Dans le premier cas, il est déclaré comme paramètre implicite, ce qui oblige le compilateur à en trouver un. Dans le second cas, il est appelé via la fonction implicitly, qui, en théorie, devrait se comporter de la même manière en termes de portée de recherche implicite. Quelles sont les causes de ce comportement?

Répondre

1

La définition de implicitly (en Predef) est:

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

Il est tout simplement explicite vous un implicite déjà portée (au site d'utilisation).

Maintenant, quand vous écrivez ceci:

import collection.generic.CanBuildFrom 

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

Vous demandez à l'appelant de fournir une implicite (sur le site d'appel).

Lorsque vous écrivez

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

Vous demandez au compilateur avec l'appel implicitly à Recherch une implicite déjà portée. Mais vous n'avez aucun implicite du type donné dans la portée! Donc les deux définitions de test font quelque chose de complètement différent.

Vous utilisez implicitly normalement pour mettre la main sur un implicite auquel vous n'avez pas le nom, parce qu'il a été spécifié en utilisant le contexte des limites ou la notation de type classe, comme def test[A: TypeClass]. Vous ne pouvez pas utiliser cette notation ici car CanBuildFrom a trois paramètres de type et pas un. Donc, vous ne pouvez pas faire beaucoup avec implicitly ici.

Vous pouvez utiliser implicitly avec votre premier cas:

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

Mais alors vous savez déjà que vous avez cette implicite avec le nom cbf ...


Notez que vous pouvez mettre la main sur un CanBuildFrom implicite pour un type de collection connu:

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

Mais cela ne fonctionne pas si votre type de collection (C[X]) est abstraite.

+0

Mais quelle est la différence entre les champs d'application implicites dans ces 2 situations? Comme je l'ai dit, je l'ai fait dans REPL, donc dans le premier cas un 'CanBuildFrom 'générique a été trouvé (dans Predef, je suppose) quand j'ai appelé la fonction. Cela signifie qu'il existe une instance implicite appropriée de CanBuildFrom dans la portée. Pourquoi le même ne peut-il pas être utilisé lors de la définition d'une fonction autonome dans REPL? – Haspemulator

+0

Dans un cas, vous avez un type de béton, par ex. 'List', dans l'autre vous avez un paramètre de type abstrait' C [_] '.La résolution implicite portera, entre autres, sur l'objet compagnon de la classe type (CanBuildFrom) et de ses paramètres de type (par exemple ici List - où il trouve l'instance de classe de type!), Mais ne peut évidemment pas résoudre un type abstrait . –

+0

Bon, maintenant c'est logique. – Haspemulator