2016-03-31 3 views
1

Ich habe eine Klasse wie folgt definiert:Wie spezifiziert man einen Typparameter beim Aufruf einer Funktion, während der Compiler auf den anderen schließen kann?

implicit class TraversableLikeView[+A, +Repr, Raw](self: Raw)(implicit cast: Raw => TraversableLike[A,Repr]) { 

    def filterByType[B, That](implicit bf: CanBuildFrom[Repr, B, That]): That = { 
     val result = cast(self).flatMap{ 
     case tt: B => Some(tt) 
     case _ => None 
     }(bf) 
     result 
    } 
    } 

Wenn es auf TraversableLikeView("abc" :: "def" :: Nil) Aufruf:

Ich mag Typen Parameter B angegeben werden, und Typparameter That automatisch aus vordefinierten implicits abgeleitet werden. So nenne ich die Funktion wie folgt aus:

TraversableLikeView("abc" :: "def" :: Nil).filterByType[String, _].foreach{...} 

Allerdings gab der Compiler mir diesen Fehler:

Error:(38, 56) unbound wildcard type 
     ....filterByType[String, _].foreach{... 
          ^

Warum scala ist nicht in der Lage, diese Art Parameter ableiten? Was soll ich tun, um es zu beheben?

UPDATE: Das nächste, was ich bekommen kann, ist wie folgt aus:

implicit class TraversableLikeView[A, Repr, Raw <% TraversableLike[A, Repr]](self: Raw) { 

    def filterByType[B] = new FilterByType[B] 

    class FilterByType[B] { 

     def apply[That](implicit bf: CanBuildFrom[Repr, B, That]): That = { 
     val result = self.flatMap{ 
      case tt: B => Some(tt) 
      case _ => None 
     }(bf) 
     result 
     } 
    } 
    } 

    test("1") { 

    val res: Seq[String] = Seq("abc", "def").filterByType[String].apply 
    println(res) 

    val res2: Array[String] = Array("abc", "def").filterByType[String].apply 
    println(res2) 

    val res3: Set[String] = Set("abc", "def").filterByType[String].apply 
    println(res3) 
    } 

jedoch der Compiler auf der Suche nach den Beweisen (die nicht einmal erforderlich sein sollen) zum Scheitern verurteilt scheint:

Error:(38, 33) value filterByType is not a member of Array[com.tribbloids.spookystuff.pages.PageLike] 
     val unfetched = pageLikes.filterByType[Unfetched].head 
           ^

Wenn ich die Ansichtsbegrenzung abbringe funktioniert es perfekt (natürlich außer Array [String]), aber ich bin etwas überrascht zu sehen, dass es eine solche Umgehung braucht, um eine einfache Sache in scala zu implementieren.

Antwort

1

Sie können nicht nur einen der beiden Typparameter angeben und den anderen Typ ableiten. Entweder die Typen weglassen oder beide angeben.

Müssen Sie wirklich TraversableLike verwenden und Grenzen anzeigen? Wie wäre es nur das Magnetmuster wie folgt aus:

trait FilterMagnet[A, B] { 
    type Out 
    def apply(self: Traversable[A]): Out 
    } 
    object FilterMagnet { 
    implicit def forThat[A, B: ClassTag, That](implicit bf: CanBuildFrom[Traversable[A], B, That]) = new FilterMagnet[A, B] { 
     type Out = That 
     def apply(self: Traversable[A]): That = self.collect { case tt: B => tt } 
    } 
    } 

    implicit class TraversableLikeView[A, C[A0 <: A] <: Traversable[A0]](self: C[A]) { 
    def filterByType[B](implicit magnet: FilterMagnet[A, B]): magnet.Out = magnet(self) 
    } 

    val listResult = TraversableLikeView("abc" :: "def" :: 3 :: Nil).filterByType[String] 
    println(listResult) // prints List(abc, def) 

    val setResult = TraversableLikeView(Set("abc", 3, true, "def")).filterByType[String] 
    println(setResult) // prints Set(abc, def) 

Vergessen Sie nicht, einen ClassTag Kontext hinzufügen gebunden B filtern zu können, sonst geben Löschen Sie zur Laufzeit Informationen über die nicht halten lassen B Typ

+0

Ja ist es wichtig, gebunden zu verwenden Ansicht, sonst ist diese Funktion nicht auf zB funktionieren Array ("a", "b"). Außerdem ist dies keine perfekte Lösung, da die Kompilierungsarten von listResult & setResult immer noch Traversable sind [String] – tribbloid

1

Eine Lösung für dieses Problem ist der Typ-Parameter zwischen zwei Methoden aufzuteilen:

class FilterByType[B] { 
    def apply[That](implicit bf: CanBuildFrom[Repr, B, That]): That = { 
    val result = cast(self).flatMap{ 
     case tt: B => Some(tt) 
     case _ => None 
    }(bf) 
    result 
    } 
} 

def filterByType[B] = new FilterByType[B] 

TraversableLikeView("abc" :: "def" :: Nil).filterByType[String].apply.foreach{...} 
+0

Etwas bessere Lösung, aber ohne die Ansicht gebunden kann ich sie immer noch nicht auf Array [String] verwenden. Bitte erlauben Sie mir, die erste Klasse und Funktion in die implizite Ansicht zu verschieben, um Verwechslungen zu vermeiden – tribbloid

+0

Ich habe ein anderes Problem gefunden: Wenn Sie .filterByType [Int] verwenden, wird immer eine leere Liste zurückgegeben, unabhängig vom Inhalt. Nur filterByType [java.lang.Integer] funktioniert, das ist ein bekannter scala 2.10 Bug, aber bisher habe ich keine schnelle Umgehung gefunden – tribbloid

Verwandte Themen