Created
April 28, 2016 12:22
-
-
Save joakim-ribier/6cd94aed1074e906836a432f1d1b284d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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