Skip to content

Instantly share code, notes, and snippets.

@joakim-ribier
Created April 28, 2016 12:22
Show Gist options
  • Select an option

  • Save joakim-ribier/6cd94aed1074e906836a432f1d1b284d to your computer and use it in GitHub Desktop.

Select an option

Save joakim-ribier/6cd94aed1074e906836a432f1d1b284d to your computer and use it in GitHub Desktop.
C'est un tips que je gardais pour un atelier scala avancé :), mais voilà un aperçu :
En gros quand on `.map` une collection, le compilateur scala cherche toujours un CanBuildFrom en implicite pour savoir comment reconstruire la collection derrière (On y fait généralement pas attention, mais il est bien dans la définition de la fonction `final def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That`). Une instance de CanBuildFrom sert à dire : comment passer de telle collection contenant tels éléments à tel type de collection.
Ca arrive régulièrement que la manière dont il la reconstruit n'est pas ce qu'on veut, typiquement dans ce cas :
```
val m = List(1, 2, 3)
.map { i => (i.toString -> i * i) } // De base il va faire une List[Tuple2[String, Int]]
.toMap // Mais ce qu'on veut en réalité c'est une Map[String, Int], on doit faire une transformation supplémentaire.
```
Le `.map` donne une List car c'est la transformation la plus naturelle (priorité des implicites), mais en fait le compilateur sait comment directement construire une map à partir de tuples. Il faudrait juste lui donner le bon CanBuildFrom en paramètre.
Le `scala.collection.breakOut` permet de changer de builder pris par défaut (sans avoir à fouiller le code source de scala pour trouver le bon CanBuildFrom), notamment en se basant sur l'inférence de type à partir du type de la variable dans laquelle on place la nouvelle collection (il devine pas magiquement dans quoi on veut transformer)
```
List(1, 2, 3).map(i => i.toString -> i*i)
res3: List[(String, Int)] = List((1,1), (2,4), (3,9))
List(1, 2, 3).map(i => i.toString -> i*i)(collection.breakOut) // no magic here
res1: scala.collection.immutable.IndexedSeq[(String, Int)]
val m: Map[String, Int] = List(1, 2, 3).map(i => i.toString -> i*i)(collection.breakOut)
m: Map[String,Int] = Map(1 -> 1, 2 -> 4, 3 -> 9)
scala> val m: Array[(String, Int)] = List(1, 2, 3).map(i => i.toString -> i*i)(collection.breakOut)
m: Array[(String, Int)] = Array((1,1), (2,4), (3,9))
```
Et l'inférence marche aussi à partir du type de retour d'une fonction :
```
def toto(): Map[String, Int] = {
List(1, 2, 3).map(i => i.toString -> i*i)(collection.breakOut) // ça semble magique mais en fait il cherche quelque chose pour que le contrat avec la fonction match.
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment