2009-11-12 6 views
26

Opierając się na innym pytaniem spytałem Scala 2.8 breakout, chciałem zrozumieć nieco więcej o sposobie Scala TraversableLike[A].map której podpis jest w następujący sposób:Scala 2,8 CanBuildFrom

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That 

zauważyć kilka rzeczy na temat tej metody :

  • zajmuje funkcję skrętu każdy A w przesuwny do B.
  • Zwraca That i przyjmuje niejawny argument typu CanBuildFrom[Repr, B, That].

mogę nazwać to w następujący sposób:

> val s: Set[Int] = List("Paris", "London").map(_.length) 
s: Set[Int] Set(5,6) 

Co nie mogę zupełnie pojąć sposób fakt, że That jest związany do B (to znaczy, że jest jakiś zbiór B) jest wymuszany przez kompilator. Parametry Typ wyglądać być niezależny zarówno od podpisania powyżej podpisania cechą CanBuildFrom samego:

trait CanBuildFrom[-From, -Elem, +To] 

Jak kompilator Scala zapewniającej That nie może być zmuszony do czegoś, co nie ma sensu?

> val s: Set[String] = List("Paris", "London").map(_.length) //will not compile 

jaki sposób kompilator zdecydować, jakie przedmioty są ukryte CanBuildFrom w zakresie do rozmowy?

+0

Oto post z całkiem ładnym wyjaśnienia http://blog.bruchez.name/2012/08/getting-to-know-canbuildfrom-without-phd.html –

+0

Dla przypomnienia, takie użytkowanie ma nazwa pojęciowa: polimorfizm typu powrotu. – lcn

Odpowiedz

29

Należy zauważyć, że drugi argument dla map jest niejawnym argumentem. Tammusi być ukryty w zakresie z odpowiednimi typami lub, inaczej, musisz musi przekazać taki argument.

W przykładzie That musi być Set[String], B musi być Int i Repr musi być List[String]. Dlatego do kompilacji potrzebny jest następujący ukryty obiekt w zakresie:

implicit object X: CanBuildFrom[List[String], Int, Set[String]] 

Nie ma czegoś takiego w zakresie. Ponadto, breakOut nie może go dostarczyć, ponieważ on sam potrzebuje domyślnego CanBuildFrom, którego pierwszym typem może być dowolna klasa (contra-variant potomny Nothing), ale poza tym ograniczone przez inne typy.

Spójrz na przykład na fabryce CanBuildFrom od obiektu towarzysz List:

implicit def canBuildFrom [A] : CanBuildFrom[List, A, List[A]] 

Ponieważ wiąże się drugie i trzecie parametry poprzez A, implicite w pytaniu nie będzie działać.

Skąd wiadomo, gdzie szukać, jeśli chodzi o takie implicite? Przede wszystkim Scala importuje kilka rzeczy do wszystkich zakresów.Teraz mogę przywołać następujące importu:

import scala.package._ // Package object 
import scala.Predef._ // Object 
// import scala.LowPriorityImplicits, class inherited by Predef 
import scala.runtime._ // Package 

Ponieważ jesteśmy zaniepokojeni implicits pamiętać, że podczas importowania rzeczy z paczek, tylko implicits możliwe są samotnymi. Podczas importowania rzeczy z obiektów (pojedynczych) z drugiej strony możesz mieć niejawne definicje, wartości i znaki singleton.

W tej chwili są CanBuildFrom implicity wewnątrz Predef i LowPriorityImplicits, które dotyczą ciągów. Pozwalają nam pisać "this is a string" map (_.toInt).

A więc, poza tymi automatycznymi importami i wyraźnymi importami, które można wprowadzić, gdzie jeszcze można znaleźć ukryte? Jedno miejsce: obiekty towarzyszące instancji, w której stosowana jest metoda.

Mówię obiekt towarzysza s, w liczbie mnogiej, ponieważ obiekty towarzyszące wszystkich cech i klas dziedziczonych przez klasę danej instancji mogą zawierać odpowiednie implikacje. Nie jestem pewien, czy sama instancja może zawierać niejawne. Szczerze mówiąc, nie mogę tego teraz odtworzyć, więc z pewnością popełniam tu jakiś błąd.

W każdym razie spójrz na obiekty towarzyszące.

+0

Daniel - skąd mam wiedzieć, jakie obiekty "niejawne" znajdują się w zasięgu w danym punkcie mojego kodu? O tym właśnie sprowadza się to pytanie. Gdzie jest określony? Również to pytanie nie ma nic wspólnego z 'breakOut'. –

+0

Oprócz niejawnych konwersji w obiekcie scala.Predef, musisz zdefiniować niejawny w tym samym lub otaczającym zakresie lub musisz jawnie zaimportować to niejawne. –

+0

"podczas importowania rzeczy z pakietów, jedynymi możliwymi implikacjami są single" Czy to nadal prawda z obiektami pakietu Scala 2.8? Mogą również zawierać implicite def i vals. –

0
object ArrayBuffer extends SeqFactory[ArrayBuffer] { 
    /** $genericCanBuildFromInfo */ 
    implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, ArrayBuffer[A]] = new GenericCanBuildFrom[A] 
    def newBuilder[A]: Builder[A, ArrayBuffer[A]] = new ArrayBuffer[A] 
}