web-dev-qa-db-fra.com

Scala, tuple générique

J'ai une méthode générique qui peut accepter n'importe quel tuple de n'importe quelle taille, la seule contrainte est que le premier élément de ce tuple soit de type MyClass.

Quelque chose comme ça:

trait MyTrait[T <: (MyClass, _*)] {
  getMyClass(x: T): MyClass = x._1
}

J'ai essayé ça

trait MyTrait[T <: (MyClass, _) with (MyClass, _, _) with (MyClass, _, _) with ...] {
  getMyClass(x: T): MyClass = x._1
}

mais j'obtiens l'erreur unboud wildcard type

12
Gigitsu

C'est un peu dangereux, mais vous pouvez utiliser le type de structure dans ce cas:

trait MyTrait {
  def getMyClass(x: {def _1: MyClass}): MyClass = x._1
}
4
mkUltra

Si vous souhaitez effectuer cette opération sans réflexion standard ou d'exécution, Sans forme est votre meilleur choix. Vous pouvez utiliser la classe de type IsComposite pour appliquer des contraintes de niveau type au premier élément d'un tuple:

import shapeless.ops.Tuple.IsComposite

trait MustBeFirst

class MyClass[P <: Product](p: P)(implicit ev: IsComposite[P] { type H = MustBeFirst }) {
  def getMustBeFirst(x: P): MustBeFirst = ev.head(p)
}

Et alors:

scala> val good2 = (new MustBeFirst {}, "")
good2: (MustBeFirst, String) = ($anon$1@7294acee,"")

scala> val good3 = (new MustBeFirst {}, "", 123)
good3: (MustBeFirst, String, Int) = ($anon$1@6eff9288,"",123)

scala> val good4 = (new MustBeFirst {}, "", 'xyz, 123)
good4: (MustBeFirst, String, Symbol, Int) = ($anon$1@108cdf99,"",'xyz,123)

scala> val bad2 = ("abc", 123)
bad2: (String, Int) = (abc,123)

scala> new MyClass(good2)
res0: MyClass[(MustBeFirst, String)] = MyClass@5297aa76

scala> new MyClass(good3)
res1: MyClass[(MustBeFirst, String, Int)] = MyClass@3f501844

scala> new MyClass(good4)
res2: MyClass[(MustBeFirst, String, Symbol, Int)] = MyClass@24e15478

scala> new MyClass(bad2)
<console>:15: error: could not find implicit value for parameter ev: shapeless.ops.Tuple.IsComposite[(String, Int)]{type H = MustBeFirst}
       new MyClass(bad2)
       ^

Si vous devez utiliser un trait, vous pouvez insérer l'exigence ev (pour "preuve") dans la définition plutôt que dans le constructeur:

trait MyTrait[P <: Product] {
  implicit def ev: IsComposite[P] { type H = MustBeFirst }
}

Maintenant, toute classe instanciant MyTrait devra fournir la preuve que P est un tuple avec MustBeFirst comme premier élément.

11
Travis Brown

Scala ne peut pas utiliser un tuple générique de taille inconnue car les produits n'héritent pas eux-mêmes. Vous pouvez essayer d’utiliser Shapeless ou Products de play json lib.

2
Ivan Aristov

Vous devez hériter de votre trait de Product, grâce auquel vous pouvez avoir productIterator, productArity et, productElement pour gérer la valeur renvoyée. Voici un exemple

case class MyClass()

trait MyTrait[T <: Product] {
  def getMyClass(x: T): Option[MyClass] = 
                if(
                     x.productIterator.hasNext 
                                   && 
                     x.productIterator.next().isInstanceOf[MyClass]
                 ){
    Some(x.productIterator.next().asInstanceOf[MyClass])
  } else {
    None
  }
}

case class Test() extends MyTrait[Product]

Et vous pouvez invoquer comme ça

Test().getMyClass((MyClass(), 1,3,4,5))
//res1: Option[MyClass] = Some(MyClass())

Test().getMyClass((1,3,4,5))
//res2: Option[MyClass] = None

J'espère que cela vous aide.

0
Puneeth Reddy V