2017-12-02 115 views
5

Zakładając Mam hierarchii typu jak poniżej:Scala seq - przyjmować tylko elementów tego samego podtypu

trait Color 
case class Red(r: String) extends Color 
case class Green(g: String) extends Color 

Czy jest możliwe aby utworzyć metodę, która akceptuje Seq[Color] który zawiera elementy zarówno wszystkich Red lub albo wszystkie Green, ale nie obie jednocześnie?

Na przykład w poniższym kodzie:

def process[T](colors: Seq[T]) = colors.size 

process(Seq(Red("a"), Green("g"))) 

co powinno [T] być tak, że powyższe nie wpisać sprawdzić?

Edit

Oryginalny problem jest następujący: Staram się opracować API JSON dla zagnieżdżonych zapytań. I mają pochodzić z następującym wzorem:

trait QueryUnit 
case class SimpleQuery(query: String, metadata: Metadata) 
case class ComplexQuery(Map[String, Seq[SimpleQuery]) 

case class API(
    query: Map[String, Seq[QueryUnit]] 
) 

Elementy mapa zostanie conjuctions (łączeniu), natomiast elementy Seq będzie alternatywy (ORS). Nie chcę mieszać Simple z ComplexQueries i planuję dopasować wzorzec w Seq [QueryUnit].

+1

brzmi jak problemu] [XY (https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) do mnie. Czy mógłbyś trochę rozwinąć problem? –

+1

Pewnie, wystarczająco dobrze :) Będę aktualizował więcej informacji o moim oryginalnym wydaniu. – spyk

+0

Zaktualizowany oryginalny problem – spyk

Odpowiedz

3

Spróbuj to (częściowo na podstawie this blog post from Miles Sabin):

// Your domain 
trait QueryUnit 
case class SimpleQuery(query: String, metadata: AnyRef) extends QueryUnit 
case class ComplexQuery(map: Map[String, Seq[SimpleQuery]]) extends QueryUnit 
// End of your domain 

// Something which has type parameter, so we can add QueryUnit, ... 
trait WrongArg[T] 

// Create ambiguous implicits for QueryUnit 
implicit def v0: WrongArg[QueryUnit] = ??? 
implicit def v2: WrongArg[QueryUnit] = ??? 

// And valid values for the concrete subclasses 
implicit val simpleQWrongArg: WrongArg[SimpleQuery] = new WrongArg[SimpleQuery] {} 
implicit val complexQWrongArg: WrongArg[ComplexQuery] = new WrongArg[ComplexQuery] {} 

case class API[QU <: QueryUnit](
       query: Map[String, Seq[QU]] 
    // Require an evidence that we are getting the correct type 
           )(implicit w: WrongArg[QU]) { 
} 

API/*[SimpleQuery]*/(Map("" -> Seq(SimpleQuery("", "")))) 

API/*[ComplexQuery]*/(Map("" -> Seq(ComplexQuery(Map.empty)))) 

// Fails to compile because of ambiguous implicits 
//API(Map("s" -> Seq(SimpleQuery("", "")), "c" -> Seq(ComplexQuery(Map.empty)))) 

To nie jest idealna (zły nazwy, nie @implicitNotFound adnotacji).

Podstawową ideą jest to, że tworzymy niejednoznaczne implikacje dla klasy bazowej, ale nie dla podklas. Z powodu domyślnych reguł rozdzielczości kompiluje się tylko dla podklas, ale nie dla klasy bazowej.

Oczyszczone się wersję:

@annotation.implicitNotFound("Base QueryUnit is not supported, you cannot mix SimpleQuery and ComplexQuery") 
trait Handler[T] extends (T => Unit) 

object API { 
    implicit def baseQueryUnitIsNotSupported0: Handler[QueryUnit] = ??? 

    implicit def baseQueryUnitIsNotSupported1: Handler[QueryUnit] = ??? 

    implicit val simpleQWrongArg: Handler[SimpleQuery] = new Handler[SimpleQuery] { 
    override def apply(s: SimpleQuery): Unit = {} 
    } 
    implicit val complexQWrongArg: Handler[ComplexQuery] = new Handler[ComplexQuery] { 
    override def apply(s: ComplexQuery): Unit = {} 
    } 
} 

case class API[QU <: QueryUnit](query: Map[String, Seq[QU]])(
    implicit handler: Handler[QU]) { 
    // Do something with handler for each input 
} 

// Usage 

import API._ 

import scala.annotation.implicitNotFound 
API/*[SimpleQuery]*/(Map("" -> Seq(SimpleQuery("", "")))) 

API/*[ComplexQuery]*/(Map("" -> Seq(ComplexQuery(Map.empty)))) 

// Error:(56, 71) Base QueryUnit is not supported, you cannot mix SimpleQuery and ComplexQuery 
//API(Map("s" -> Seq(SimpleQuery("", "")), "c" -> Seq(ComplexQuery(Map.empty)))) 

alternatywnie typu granicach i implicitly:

case class API[QU <: QueryUnit: Handler](query: Map[String, Seq[QU]]) { 
    def doSomething: Unit = for {(_, vs) <- query 
     v <- vs} { 
    val handler: Handler[QU] = implicitly[Handler[QU]] 
    handler(v) 
    } 
} 
+0

Prawdopodobnie jest coś na ten temat w stanie bezkształtnym, ale i tak nie jest to trudne. –

+0

Myślę, że to sprawia, że ​​API wcale nie jest trywialne, gdzie OP chce pójść na "proste rozwiązanie", nie sądzę, że to się kwalifikuje jako proste. –

+0

Zobacz moje komentarze do PO w pytaniu: * "a) chciałbym prostszego API" *.Nie musisz iść na poziom makro, aby komplikować rzeczy :) Jeśli OP znajdzie wzór pasujący do ADT dla podstawowego typu, nie sądzę, by to było kwalifikować jako prostsze podejście :) –