20 votes

Comment faire pour que les conversions implicites fonctionnent dans les collections ?

Disons que j'ai une conversion implicite :

implicit def aToB(a: A):B={
...
}

Comment faire pour que cette conversion implicite fonctionne sur les éléments d'une liste ?

Si je l'ai fait :

val listOfA: List[A] ...

et j'ai une fonction qui prend une liste de B, est-il possible de laisser Scala convertir implicitement tous les éléments de A en B ?

Sans les conversions implicites, la conversion pourrait ressembler à ceci :

lisftOfA.map(a => new B(a.someValue, a.anotherValue))

Mais j'aimerais que cela se passe comme par magie... est-ce trop demander ?

18voto

Aaron Novstrup Points 10742

Voici quelques alternatives que vous pourriez envisager :

1. Utiliser une vue liée

S'il est possible de modifier la fonction qui prend une liste de B, ce serait la solution la plus simple. Modifiez-la pour qu'elle accepte une liste de choses qui peuvent être converties en B. C'est-à-dire,

def yourFn(l: List[B]) = ...

deviendrait

def yourFn[X <% B](l: List[X]) = ...

Ensuite, vous pouvez simplement appeler la fonction avec listOfA :

yourFn(listOfA)

2. Introduire une méthode de conversion

Cette solution est similaire à la première solution de Rogach, sauf que la conversion externe est non implicite :

def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }

Ensuite, sur le site d'appel de votre fonction, vous écrirez

yourFn(convert(listOfA))

Comme la deuxième solution de Rogach, cette solution est un peu plus sûre que l'introduction d'une conversion implicite.

3. Introduire une conversion implicite

Ceci est équivalent à la première solution de Rogach, mais la notation est un peu plus agréable (IMO).

implicit def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }

Si cette conversion est dans la portée de votre site d'appel, vous pouvez simplement appeler votre fonction avec le listOfA :

yourFn(listOfA)

Réflexions sur l'avenir

Il est intéressant d'examiner comment résoudre ce problème de manière générale. Que se passe-t-il si je veux définir ma méthode de conversion de façon à ce qu'elle puisse traiter n'importe quel type qui implémente la fonction map méthode ? c'est-à-dire,

def convert[B, A <% B, C[_]](c: C[A]): C[B] = c map { a => a: B }

Cela ne fonctionnera pas, bien sûr, puisque rien dans la signature n'exprime la contrainte que C doit mettre en œuvre map . Pour autant que je sache, l'expression de cette contrainte est assez complexe et ne peut pas être faite de manière à fournir un support prêt à l'emploi pour tout type qui implémente map . Voir Compréhensions de séquences Scala sûres pour les types .

3voto

Rogach Points 6717

La solution suivante est une solution générale, permettant la conversion implicite de listes (Liste[A] => Liste[B]) si la conversion implicite A=>B est disponible dans la portée :

scala> class A
defined class A

scala> class B
defined class B

scala> implicit def a2b(a:A) = new B
a2b: (a: A)B

scala> implicit def mapI[A,B](l: List[A])(implicit conv: A => B): List[B] = l.map(conv)
mapI: [A, B](l: List[A])(implicit conv: (A) => B)List[B]

scala> List(new A): List[B]
res0: List[B] = List(B@efa0bf4)

C'est ce dont vous avez besoin ?

De plus, puisque vous avez déjà cette conversion implicite, vous pouvez simplement écrire :

listOfA.map(a2b) // List[B]

Ce serait un peu plus verbeux, mais vous donne un contrôle un peu plus explicite de votre code.

2voto

Je pense que le mieux qui soit possible est

implicit def asToBs(as: List[A]): List[B] = as map aToB

1voto

Michael Ahlers Points 616

Dans un souci d'exhaustivité, vous pouvez envisager une approche plus générique employant CanBuildFrom .

Ce programme :

import scala.collection.generic.CanBuildFrom
import scala.language.{higherKinds, implicitConversions}

implicit def implyConvertedTraversable[A, B, C[X] <: Traversable[X]](as: C[A])(implicit conversion: A => B, cbf: CanBuildFrom[C[A], B, C[B]]): C[B] = {
  val builder = cbf(as)
  builder.sizeHint(as)
  builder ++= as.map(conversion)
  builder.result()
}

implicit def implyString(a: Int): String = a.toString

val intList = List(1, 2, 3)
val intStream = Stream(1, 2, 3)

val stringList: List[String] = intList
val stringStream: Stream[String] = intStream

Rendement :

intList: List[Int] = List(1, 2, 3)
intStream: scala.collection.immutable.Stream[Int] = Stream(1, ?)

stringList: List[String] = List(1, 2, 3)
stringStream: Stream[String] = Stream(1, ?)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X