<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
  <title>Blog d'Univalence</title>
  <subtitle>Tout les articles publiés par les talents d'Univalence.</subtitle>
  <link href="https://univalence.io"/>
  <link href="https://univalence.io/blog/atom.xml" rel="self"/>
  <updated>2025-09-08T17:24:18Z</updated>
  <author>
    <name>Univalence</name>
  </author>
  <id>urn:uuid:770fb220-7068-4f7e-94fc-cf66897f2ada</id>  <entry xml:lang="fr">
    <id>urn:notion:2e0d97fccfa44cdc82d1a28eecd23e6d</id>
    <title>Focus sur le Z-Ordering et le Liquid Clustering avec Delta Lake</title>
    <updated>2024-04-04T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/focus-sur-le-z-ordering-et-le-liquid-clustering-avec-delta-lake.html"/>
    <!--summary Explication sur le fonctionnement du Z-Ordering et Liquid Clustering avec Delta Lake-->    <author>
      <name>Bernarith Men</name>
      <uri>https://univalence.io/blog/auteurs/bernarith-men.html</uri>
    </author>        <category term="Delta Lake"></category>    <category term="Spark"></category>    <category term="SQL"></category>    <category term="performance"></category>    <content type="html">
&lt;!-- begin paragraph f1363268-3a2c-42c5-a4e4-99aa1883d872--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f1363268-3a2c-42c5-a4e4-99aa1883d872&quot;&gt;Dans mon apprentissage de &lt;a href=&quot;https://delta.io/&quot;&gt;Delta Lake&lt;/a&gt;, un format de stockage open-source qui permet notamment d’avoir les propriétés &lt;a href=&quot;https://fr.wikipedia.org/wiki/Propri%C3%A9t%C3%A9s_ACID&quot;&gt;ACID&lt;/a&gt; sur des systèmes de stockage qui de base ne les ont pas, entre autres, on m’a parlé de Z-Ordering et de Liquid Clustering pour améliorer les performances des requêtes. Mais que sont-ils ? À quoi peuvent-ils servir ? Nous vous expliquerons tout cela dans cet article. &lt;/p&gt;
&lt;!-- end paragraph f1363268-3a2c-42c5-a4e4-99aa1883d872--&gt;
&lt;!-- begin heading_1 859980f2-0765-4192-aee5-55784d0b3ae2--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-859980f2-0765-4192-aee5-55784d0b3ae2&quot;&gt;Z-Ordering&lt;/h2&gt;
&lt;!-- end heading_1 859980f2-0765-4192-aee5-55784d0b3ae2--&gt;
&lt;!-- begin heading_2 2c5309ba-1229-47d0-a84b-deda6033a440--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-2c5309ba-1229-47d0-a84b-deda6033a440&quot;&gt;Data-skipping&lt;/h3&gt;
&lt;!-- end heading_2 2c5309ba-1229-47d0-a84b-deda6033a440--&gt;
&lt;!-- begin paragraph 828fb09e-90e5-4b3e-a6c8-d3fdc43cd93e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-828fb09e-90e5-4b3e-a6c8-d3fdc43cd93e&quot;&gt;Un des aspects qui améliore les performances, est le &lt;b&gt;data-skipping &lt;/b&gt;(ou file-skipping), en effet la lecture d’un fichier pour le transférer dans la mémoire est coûteux, donc moins nous lisons de fichier, plus la requête sera performante. Pour cela Delta utilise les métadonnées de ses fichiers pour savoir si le fichier doit être lu ou non, on y trouve le &lt;b&gt;minimum &lt;/b&gt;et le &lt;b&gt;maximum &lt;/b&gt;de chaque colonne par fichier ce qui permet de le déterminer.&lt;/p&gt;
&lt;!-- end paragraph 828fb09e-90e5-4b3e-a6c8-d3fdc43cd93e--&gt;
&lt;!-- begin heading_2 fb722fa6-f08d-4e34-8c82-65bb06f5e47b--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-fb722fa6-f08d-4e34-8c82-65bb06f5e47b&quot;&gt;Compaction&lt;/h3&gt;
&lt;!-- end heading_2 fb722fa6-f08d-4e34-8c82-65bb06f5e47b--&gt;
&lt;!-- begin paragraph e5a274f9-9819-4597-8c36-9ca9d0f511a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5a274f9-9819-4597-8c36-9ca9d0f511a9&quot;&gt;Un autre aspect, est la réduction de nombre de fichier avec la &lt;b&gt;compaction&lt;/b&gt;, en compactant la table, c’est à dire que moins de fichier seront à lire.&lt;/p&gt;
&lt;!-- end paragraph e5a274f9-9819-4597-8c36-9ca9d0f511a9--&gt;
&lt;!-- begin heading_2 849b38eb-656b-484a-b740-5c9692f34c9c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-849b38eb-656b-484a-b740-5c9692f34c9c&quot;&gt;Illustration&lt;/h3&gt;
&lt;!-- end heading_2 849b38eb-656b-484a-b740-5c9692f34c9c--&gt;
&lt;!-- begin paragraph abf804eb-7f1a-4de5-8011-e21f0606a7b3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-abf804eb-7f1a-4de5-8011-e21f0606a7b3&quot;&gt;Le Z-Ordering utilise ces aspects d’amélioration de performance, en effet si nous utilisons le Z-Order selon une colonne &lt;b&gt;id &lt;/b&gt;par exemple, alors toutes les occurrences d’un même id vont être colocalisés au sein d’un fichier mais de façon ordonnée et les plages d’&lt;b&gt;id &lt;/b&gt;de chaque fichier&lt;b&gt; &lt;/b&gt;sera plus petite.&lt;/p&gt;
&lt;!-- end paragraph abf804eb-7f1a-4de5-8011-e21f0606a7b3--&gt;
&lt;!-- begin paragraph d0f4ed08-68d3-43ce-b7ab-6889ea50c9c6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d0f4ed08-68d3-43ce-b7ab-6889ea50c9c6&quot;&gt;Dans l’exemple simplifié ci-dessous, à droite nous avons notre donnée non ordonné repartie sous 4 partitions, on voit dans les métadonnées de chaque partition le minimum et le maximum de la colonne &lt;b&gt;id&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph d0f4ed08-68d3-43ce-b7ab-6889ea50c9c6--&gt;
&lt;!-- begin paragraph e05e8deb-879c-4ba9-a42d-d10e1a3382d5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e05e8deb-879c-4ba9-a42d-d10e1a3382d5&quot;&gt;Après l’application du Z-Order, à droite, nous n’avons plus que 2 partitions, et elles sont ordonnées par plage, la partition 1 contient les &lt;b&gt;id&lt;/b&gt;s entre 1 et 5 et la partition 2 contient ceux entre 6 et 10.&lt;/p&gt;
&lt;!-- end paragraph e05e8deb-879c-4ba9-a42d-d10e1a3382d5--&gt;
&lt;!-- begin paragraph 432dc85a-08ae-4d8b-ab67-53f3445fd477--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-432dc85a-08ae-4d8b-ab67-53f3445fd477&quot;&gt;Imaginions que nous voulions faire un &lt;code&gt;SELECT * FROM table where id = 5&lt;/code&gt; dans le premier cas nous aurions lu un total de &lt;span class=&quot;text-color-red&quot;&gt;&lt;b&gt;trois &lt;/b&gt;&lt;/span&gt;partitions contre &lt;span class=&quot;text-color-green&quot;&gt;&lt;b&gt;un &lt;/b&gt;&lt;/span&gt;après le Z-Order&lt;span class=&quot;text-color-green&quot;&gt;&lt;b&gt;. &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;!-- end paragraph 432dc85a-08ae-4d8b-ab67-53f3445fd477--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_2 4eac9542-9c83-4c88-8f97-85680b1b428a--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4eac9542-9c83-4c88-8f97-85680b1b428a&quot;&gt;OK, mais comment cela fonctionne à partir de 2 colonnes ?&lt;/h3&gt;
&lt;!-- end heading_2 4eac9542-9c83-4c88-8f97-85680b1b428a--&gt;
&lt;!-- begin paragraph 1a1736a4-36e5-4e57-b356-fc6ce3397d90--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1a1736a4-36e5-4e57-b356-fc6ce3397d90&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1a1736a4-36e5-4e57-b356-fc6ce3397d90--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/z-curve45.svg.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;extrait de la page Wikipedia &lt;a href=&quot;https://en.wikipedia.org/wiki/Z-order_curve&quot;&gt;https://en.wikipedia.org/wiki/Z-order_curve&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph be4f1a44-b1d1-4b7f-9e54-4a6b014c128c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be4f1a44-b1d1-4b7f-9e54-4a6b014c128c&quot;&gt;Imaginez que nous vous utiliser le Z order sur les colonnes x et y, l’algorithme va calculer les &lt;b&gt;coordonnées Z&lt;/b&gt; (ou z-values) de la façon suivante, il va d’abord changer chaque valeur de colonne en binaire, puis va interchanger la valeur binaire de &lt;b&gt;x&lt;/b&gt; et &lt;b&gt;y&lt;/b&gt; pour donner z, exemple :&lt;/p&gt;
&lt;!-- end paragraph be4f1a44-b1d1-4b7f-9e54-4a6b014c128c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Si l’on prend &lt;b&gt;x &lt;/b&gt;= 3 (&lt;span class=&quot;text-color-blue&quot;&gt;&lt;b&gt;011&lt;/b&gt;&lt;/span&gt;) et &lt;b&gt;y &lt;/b&gt;= 7 (&lt;span class=&quot;text-color-red&quot;&gt;&lt;b&gt;111&lt;/b&gt;&lt;/span&gt;) on obtiendra &lt;b&gt;z &lt;/b&gt;= 47 (&lt;span class=&quot;text-color-red&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/span&gt;&lt;span class=&quot;text-color-blue&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;&lt;span class=&quot;text-color-red&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/span&gt;&lt;span class=&quot;text-color-blue&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/span&gt;&lt;span class=&quot;text-color-red&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/span&gt;&lt;span class=&quot;text-color-blue&quot;&gt;&lt;b&gt;1)&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 06e00fb3-47a0-4a08-8e6a-fd2ca6ddeeda--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06e00fb3-47a0-4a08-8e6a-fd2ca6ddeeda&quot;&gt;Si nous parcourrons les valeurs de z dans l’ordre croissant, nous obtenons le schéma ci-dessus, qui étonnement forme aussi un Z (à plusieurs niveaux en plus) ! On appelle cela le &lt;a href=&quot;https://en.wikipedia.org/wiki/Z-order_curve&quot;&gt;Z-Order curve&lt;/a&gt; (ou &lt;a href=&quot;https://fr.wikipedia.org/wiki/Courbe_de_Lebesgue&quot;&gt;Courbe de Lebesgue&lt;/a&gt; en français).&lt;/p&gt;
&lt;!-- end paragraph 06e00fb3-47a0-4a08-8e6a-fd2ca6ddeeda--&gt;
&lt;!-- begin paragraph c839e51c-2524-48a6-98eb-c2659d7862fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c839e51c-2524-48a6-98eb-c2659d7862fb&quot;&gt;L’emploi de la commande du Z-Order va appliquer ce qu’on a vu pour une colonne à ces &lt;b&gt;coordonnées Z&lt;/b&gt; pour réorganiser notre table, avec des plages de coordonnées Z sous la même partition (sur l’illustration, on peut imaginer que chaque carré de pointillé devient une partition), donc c’est un peu comme transformer le problème à plusieurs dimensions en un problème à une dimension.&lt;/p&gt;
&lt;!-- end paragraph c839e51c-2524-48a6-98eb-c2659d7862fb--&gt;
&lt;!-- begin paragraph 4dc1fc8b-bf4e-4487-84c9-1c4edfef5af2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4dc1fc8b-bf4e-4487-84c9-1c4edfef5af2&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4dc1fc8b-bf4e-4487-84c9-1c4edfef5af2--&gt;
&lt;!-- begin heading_2 136134da-2d2f-40dd-b6a5-42a864842d4d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-136134da-2d2f-40dd-b6a5-42a864842d4d&quot;&gt;Comparaison&lt;/h3&gt;
&lt;!-- end heading_2 136134da-2d2f-40dd-b6a5-42a864842d4d--&gt;
&lt;!-- begin paragraph aebed981-fe1b-4306-b324-d9d525b8d4a3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aebed981-fe1b-4306-b324-d9d525b8d4a3&quot;&gt;Voici un exemple donné dans le &lt;a href=&quot;https://docs.google.com/document/d/1TYFxAUvhtYqQ6IHAZXjliVuitA5D1u793PMnzsH_3vs&quot;&gt;design sketch&lt;/a&gt; de la fonctionnalité.&lt;/p&gt;
&lt;!-- end paragraph aebed981-fe1b-4306-b324-d9d525b8d4a3--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 60f0cfe0-c9c5-407d-8fee-7eb6c3a72e13--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-60f0cfe0-c9c5-407d-8fee-7eb6c3a72e13&quot;&gt;&lt;br /&gt;
Ici, chaque fichier à 4 occurrences, à gauche nous avons le tri naturel, on ordonne d’abord par x puis par y, et à droite le Z-Order comme sur la figure précédente.&lt;/p&gt;
&lt;!-- end paragraph 60f0cfe0-c9c5-407d-8fee-7eb6c3a72e13--&gt;
&lt;!-- begin paragraph 6aa34b52-5cfe-4927-974b-95373f895d60--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6aa34b52-5cfe-4927-974b-95373f895d60&quot;&gt;Voici ce qui se passerait avec la requête  &lt;code&gt;SELECT * FROM points WHERE&lt;/code&gt;&lt;code&gt;&lt;b&gt; x = 2&lt;/b&gt;&lt;/code&gt;&lt;code&gt; OR&lt;/code&gt;&lt;code&gt;&lt;b&gt; y = 3&lt;/b&gt;&lt;/code&gt; &lt;/p&gt;
&lt;!-- end paragraph 6aa34b52-5cfe-4927-974b-95373f895d60--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;À gauche, nous scannons &lt;span class=&quot;text-color-red&quot;&gt;9 fichiers avec 21 occurrences qui ne nous intéressent pas&lt;/span&gt;.&lt;/li&gt;&lt;li&gt;À droite, nous scannons &lt;span class=&quot;text-color-green&quot;&gt;7 fichiers avec 13 occurences qui ne nous intéressent pas&lt;/span&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 51f64f36-d2a3-4a80-86ad-bbb24f3fa4c9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-51f64f36-d2a3-4a80-86ad-bbb24f3fa4c9&quot;&gt;C’est pourquoi avec cette courbe nous avons un data-skipping plus efficace pour un prédicat qui contient x et y.&lt;/p&gt;
&lt;!-- end paragraph 51f64f36-d2a3-4a80-86ad-bbb24f3fa4c9--&gt;
&lt;!-- begin heading_2 592c3f59-94b0-41d0-9ef9-6beaefe96dad--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-592c3f59-94b0-41d0-9ef9-6beaefe96dad&quot;&gt;Quand l’utiliser ?&lt;/h3&gt;
&lt;!-- end heading_2 592c3f59-94b0-41d0-9ef9-6beaefe96dad--&gt;
&lt;!-- begin paragraph 69aa66c9-36df-496e-83c3-00010a67f5fd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69aa66c9-36df-496e-83c3-00010a67f5fd&quot;&gt;Il faut faire le Z-Order si vos colonnes sont souvent utilisées dans vos requêtes et si elles ont une haute cardinalité (beaucoup de valeurs distinctes). &lt;/p&gt;
&lt;!-- end paragraph 69aa66c9-36df-496e-83c3-00010a67f5fd--&gt;
&lt;!-- begin paragraph 554ccc0f-aec8-40da-ab69-c977f0f0c55f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-554ccc0f-aec8-40da-ab69-c977f0f0c55f&quot;&gt;Le prérequis est que vos colonnes doivent avoir la collecte de statistiques activée (pour utiliser les métadonnées afin d’enclencher le data-skipping).&lt;/p&gt;
&lt;!-- end paragraph 554ccc0f-aec8-40da-ab69-c977f0f0c55f--&gt;
&lt;!-- begin paragraph 7fdcd3c0-064e-4d53-836a-dff6c74645f4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7fdcd3c0-064e-4d53-836a-dff6c74645f4&quot;&gt;Il ne faut pas non plus ajouter chaque colonne, en effet augmenter la dimension fera diminuer les performances si l’on utilise très peu les colonnes ajoutées dans nos prédicats.&lt;/p&gt;
&lt;!-- end paragraph 7fdcd3c0-064e-4d53-836a-dff6c74645f4--&gt;
&lt;!-- begin paragraph d9a4e0c0-3624-4b8f-ba32-865895ad1f20--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d9a4e0c0-3624-4b8f-ba32-865895ad1f20&quot;&gt;Le Z-Ordering est aussi compatible avec le Hive-Style-Partitioning (où les valeurs de la colonne de partition seront utilisés comme dossier pour séparer les fichiers), dans le sens où l’on peut réordonner les fichier au sein de ses dossiers, mais on ne pourra pas utiliser la colonne de partition pour le Z-Ordering.&lt;/p&gt;
&lt;!-- end paragraph d9a4e0c0-3624-4b8f-ba32-865895ad1f20--&gt;
&lt;!-- begin paragraph 47cf4f77-f6b9-4f57-9387-24b78c092251--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-47cf4f77-f6b9-4f57-9387-24b78c092251&quot;&gt;Par contre la commande doit être ré-exécuté assez régulièrement, quand nous avons de nouvelles données par exemple, l’opération est coûteuse, en effet on réécrit tous les fichiers ce qui peut en faire aussi un désavantage, il faut aussi se souvenir des colonnes utilisées pour le Z-Order. Aussi si l’opération d’optimisation échoue, il faut la recommencer.&lt;/p&gt;
&lt;!-- end paragraph 47cf4f77-f6b9-4f57-9387-24b78c092251--&gt;
&lt;!-- begin paragraph 0c5ea5b9-1fc3-4987-b6bd-55265069c266--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0c5ea5b9-1fc3-4987-b6bd-55265069c266&quot;&gt;&lt;b&gt;Mais justement nous allons voir qu’une autre fonctionnalité existe et arrive avec le Liquid Clustering qui remédie à ses limitations.&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 0c5ea5b9-1fc3-4987-b6bd-55265069c266--&gt;
&lt;!-- begin heading_2 674bdf65-4482-439c-84e9-492c4b4878d9--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-674bdf65-4482-439c-84e9-492c4b4878d9&quot;&gt;Pour aller plus loin&lt;/h3&gt;
&lt;!-- end heading_2 674bdf65-4482-439c-84e9-492c4b4878d9--&gt;
&lt;!-- begin paragraph f0368b41-9683-4227-9ebd-f5ced6fdc4ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f0368b41-9683-4227-9ebd-f5ced6fdc4ed&quot;&gt;Pour plus d’informations, n’hésitez pas à regarder la &lt;a href=&quot;https://docs.delta.io/2.0.2/optimizations-oss.html#z-ordering-multi-dimensional-clustering&quot;&gt;documentation&lt;/a&gt;, et l’&lt;a href=&quot;https://delta.io/blog/2023-06-03-delta-lake-z-order/&quot;&gt;article officiel&lt;/a&gt; de Delta Lake, ainsi que leur &lt;a href=&quot;https://github.com/delta-io/delta-examples/blob/master/notebooks/pyspark/delta-z-order.ipynb&quot;&gt;notebook&lt;/a&gt;. Par curiosité allez voir le &lt;a href=&quot;https://docs.google.com/document/d/1TYFxAUvhtYqQ6IHAZXjliVuitA5D1u793PMnzsH_3vs/edit&quot;&gt;sketch&lt;/a&gt; pour créer la fonctionnalité. Cette &lt;a href=&quot;https://youtu.be/YLVkITvF6KU?si=VeJIPILoTjQy6AP6&quot;&gt;vidéo&lt;/a&gt; explicative est pas mal aussi.&lt;/p&gt;
&lt;!-- end paragraph f0368b41-9683-4227-9ebd-f5ced6fdc4ed--&gt;
&lt;!-- begin paragraph c51904d7-7930-4a36-bde9-96d7f3ecf7ff--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c51904d7-7930-4a36-bde9-96d7f3ecf7ff&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c51904d7-7930-4a36-bde9-96d7f3ecf7ff--&gt;
&lt;!-- begin heading_1 716f3a69-a054-4fe1-b854-c919e400a2bd--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-716f3a69-a054-4fe1-b854-c919e400a2bd&quot;&gt;Liquid Clustering&lt;/h2&gt;
&lt;!-- end heading_1 716f3a69-a054-4fe1-b854-c919e400a2bd--&gt;
&lt;!-- begin paragraph 0e57b523-306d-4f33-b814-2bb472110d72--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e57b523-306d-4f33-b814-2bb472110d72&quot;&gt;L’annonce de Delta Lake version 3.0 en Juin 2023 s’est vu accompagné de la venu du Liquid Clustering, qui simplifierait la façon d’obtenir les meilleurs performances pour nos requêtes et son coût pour l’obtenir en même temps que la donnée grandit.&lt;/p&gt;
&lt;!-- end paragraph 0e57b523-306d-4f33-b814-2bb472110d72--&gt;
&lt;!-- begin paragraph b3331997-0675-4989-93d1-f68d7d9cadf9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3331997-0675-4989-93d1-f68d7d9cadf9&quot;&gt;Aujourd’hui avec &lt;a href=&quot;https://github.com/delta-io/delta/releases/tag/v3.1.0&quot;&gt;Delta Lake 3.1&lt;/a&gt;, la fonctionnalité est présente mais est toujours en expérimentation.&lt;/p&gt;
&lt;!-- end paragraph b3331997-0675-4989-93d1-f68d7d9cadf9--&gt;
&lt;!-- begin paragraph b8c699b7-c0c0-4460-9938-6e0ca3160c57--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b8c699b7-c0c0-4460-9938-6e0ca3160c57&quot;&gt;Toutes les illustrations proviennent de leur &lt;a href=&quot;https://docs.google.com/document/d/1FWR3odjOw4v4-hjFy_hVaNdxHVs4WuK1asfB6M6XEMw/edit#heading=h.skpz7c7ga1wl&quot;&gt;Design Doc&lt;/a&gt; .&lt;/p&gt;
&lt;!-- end paragraph b8c699b7-c0c0-4460-9938-6e0ca3160c57--&gt;
&lt;!-- begin paragraph a52cf027-ad7d-4942-ab38-9b125858d0a6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a52cf027-ad7d-4942-ab38-9b125858d0a6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a52cf027-ad7d-4942-ab38-9b125858d0a6--&gt;
&lt;!-- begin paragraph ed4dd386-d237-4a97-aa37-630beeffe96b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ed4dd386-d237-4a97-aa37-630beeffe96b&quot;&gt;Nous avions vu que le Hive-Style-Partitioning et le Z-Order pouvait cohabiter et être une bonne approche pour partitionner nos tables. Mais ils viennent avec leur limitation :&lt;/p&gt;
&lt;!-- end paragraph ed4dd386-d237-4a97-aa37-630beeffe96b--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le Hive-Style-Partitioning permet de cloisonner nos fichiers, mais si les colonnes de partitions ont une haute cardinalité cela va créer plusieurs petits fichiers que l’on ne peut combiner. Changer de stratégie de partitions peut être difficile, donc moins adaptable à la façon dont nous requêtons.&lt;/li&gt;&lt;li&gt;Pour le Z-Order, toutes les limitations vu plus tôt.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 808e68f3-83c0-4998-9790-661153434c49--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-808e68f3-83c0-4998-9790-661153434c49&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 808e68f3-83c0-4998-9790-661153434c49--&gt;
&lt;!-- begin paragraph cf2fe519-6e82-4776-9dd6-450ec0cd3bd0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf2fe519-6e82-4776-9dd6-450ec0cd3bd0&quot;&gt;L’idée avec le Liquid Clustering est de supprimer toutes ses limitations :&lt;/p&gt;
&lt;!-- end paragraph cf2fe519-6e82-4776-9dd6-450ec0cd3bd0--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;En mettant les colonnes de clustering directement au moment de la création de la table&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;elles seront persistées dans les métadonnées&lt;/li&gt;&lt;li&gt;on peut changer les colonnes de clustering pour s’adapter aux nouvelles stratégie de requêtage ou autre, sans pour autant tout recalculer.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Le clustering se fait de manière incrémentale, sans toucher à l’intégralité des données&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 c1b136d3-98f7-4c64-8af9-cd7b13c60457--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c1b136d3-98f7-4c64-8af9-cd7b13c60457&quot;&gt;Courbe de Hilbert&lt;/h3&gt;
&lt;!-- end heading_2 c1b136d3-98f7-4c64-8af9-cd7b13c60457--&gt;
&lt;!-- begin paragraph 1c5dde67-ea28-46bf-a900-728af01142ec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1c5dde67-ea28-46bf-a900-728af01142ec&quot;&gt;Avec le Z-Order, nous avons vu l’application de la &lt;a href=&quot;https://en.wikipedia.org/wiki/Z-order_curve&quot;&gt;Z-Order curve&lt;/a&gt; (&lt;a href=&quot;https://fr.wikipedia.org/wiki/Courbe_de_Lebesgue&quot;&gt;Courbe de Lebesgue&lt;/a&gt;), ici dans l’image ci-dessous provenant de leur &lt;a href=&quot;https://docs.google.com/document/d/1FWR3odjOw4v4-hjFy_hVaNdxHVs4WuK1asfB6M6XEMw/edit#heading=h.skpz7c7ga1wl&quot;&gt;Design Doc&lt;/a&gt;, ils veulent utiliser une autre courbe qui est la &lt;a href=&quot;https://fr.wikipedia.org/wiki/Courbe_de_Hilbert&quot;&gt;Courbe de Hilbert&lt;/a&gt; où la distance entre chaque point selon la courbe est de 1. &lt;/p&gt;
&lt;!-- end paragraph 1c5dde67-ea28-46bf-a900-728af01142ec--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph dccb888b-8c1b-4a84-a9d3-b715752fda6f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dccb888b-8c1b-4a84-a9d3-b715752fda6f&quot;&gt;De manière similaire au Z-Order, on peut les partitionner par 4 en suivant la courbe.&lt;/p&gt;
&lt;!-- end paragraph dccb888b-8c1b-4a84-a9d3-b715752fda6f--&gt;
&lt;!-- begin heading_3 6c57e02c-f840-498c-9b00-db4d52c167d8--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-6c57e02c-f840-498c-9b00-db4d52c167d8&quot;&gt;Comparaison&lt;/h4&gt;
&lt;!-- end heading_3 6c57e02c-f840-498c-9b00-db4d52c167d8--&gt;
&lt;!-- begin paragraph e97b7a33-de48-41bb-a552-5041de67d07e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e97b7a33-de48-41bb-a552-5041de67d07e&quot;&gt;Sur le schéma ci-dessus nous avons au final un même partionnement que pour le Z-Order, donc en quoi est-ce que la &lt;a href=&quot;https://fr.wikipedia.org/wiki/Courbe_de_Hilbert&quot;&gt;Courbe de Hilbert&lt;/a&gt; est mieux ? &lt;/p&gt;
&lt;!-- end paragraph e97b7a33-de48-41bb-a552-5041de67d07e--&gt;
&lt;!-- begin paragraph 61102171-c561-4379-95e0-149ceae1daf2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-61102171-c561-4379-95e0-149ceae1daf2&quot;&gt;Une bonne illustration est de voir ce qui se passe si nous partitionnons avec 6 éléments en suivant la courbe de chacun :&lt;/p&gt;
&lt;!-- end paragraph 61102171-c561-4379-95e0-149ceae1daf2--&gt;
&lt;!-- begin paragraph e846aece-7474-44c8-9f2e-d435b61d821a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e846aece-7474-44c8-9f2e-d435b61d821a&quot;&gt;&lt;b&gt;Avec Z-Order&lt;/b&gt; :&lt;/p&gt;
&lt;!-- end paragraph e846aece-7474-44c8-9f2e-d435b61d821a--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6e91b50d-553e-4ca0-9355-537f47537a22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e91b50d-553e-4ca0-9355-537f47537a22&quot;&gt;Le problème est bien visible ici, sur la partition entourée &lt;span class=&quot;text-color-red&quot;&gt;rouge&lt;/span&gt;, en effet avec Z-Order nous avons des “sauts” ce qui a pour impact d’avoir une trop grand plage de données, ici on aurait :&lt;/p&gt;
&lt;!-- end paragraph 6e91b50d-553e-4ca0-9355-537f47537a22--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;pour y : min = 3 et max = 5&lt;/li&gt;&lt;li&gt;mais pour &lt;span class=&quot;text-color-red&quot;&gt;&lt;b&gt;x : min = 0 et max = 7 !&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 236b4492-cfc1-4e11-a8a9-53ed80a18e18--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-236b4492-cfc1-4e11-a8a9-53ed80a18e18&quot;&gt;&lt;b&gt;Avec Courbe de Hilbert&lt;/b&gt; : &lt;/p&gt;
&lt;!-- end paragraph 236b4492-cfc1-4e11-a8a9-53ed80a18e18--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 4dc08ecb-dd36-4866-b7a0-19bc84965803--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4dc08ecb-dd36-4866-b7a0-19bc84965803&quot;&gt;Avec cette courbe, la distance entre toujours réduite, l’écart maximal est de 4.&lt;/p&gt;
&lt;!-- end paragraph 4dc08ecb-dd36-4866-b7a0-19bc84965803--&gt;
&lt;!-- begin paragraph 7ae3280c-61a7-4902-864d-00eb3a2ddd0a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7ae3280c-61a7-4902-864d-00eb3a2ddd0a&quot;&gt;Avec Z-Order, il y aurait un fichier qu’on lirait tout le temps si on cherche n’importe quel x, alors qu’avec l’autre courbe il n’existe pas de tel fichier. &lt;/p&gt;
&lt;!-- end paragraph 7ae3280c-61a7-4902-864d-00eb3a2ddd0a--&gt;
&lt;!-- begin heading_2 489c976b-6ff7-4bca-91c5-6a938562dbd4--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-489c976b-6ff7-4bca-91c5-6a938562dbd4&quot;&gt;Incremental Clustering&lt;/h3&gt;
&lt;!-- end heading_2 489c976b-6ff7-4bca-91c5-6a938562dbd4--&gt;
&lt;!-- begin paragraph 925bcb8f-24c6-4a1c-9bac-ce1e54fd8fea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-925bcb8f-24c6-4a1c-9bac-ce1e54fd8fea&quot;&gt;Le concept tourne autour de &lt;b&gt;ZCube : un groupement de fichier produit par le même job OPTIMIZE.&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 925bcb8f-24c6-4a1c-9bac-ce1e54fd8fea--&gt;
&lt;!-- begin paragraph b9e42e22-2ada-4710-b758-7907a77c7772--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b9e42e22-2ada-4710-b758-7907a77c7772&quot;&gt;Il y a une distinction à faire entre :&lt;/p&gt;
&lt;!-- end paragraph b9e42e22-2ada-4710-b758-7907a77c7772--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;les fichiers provenant de &lt;b&gt;ZCube&lt;/b&gt; “stables”, donc optimisé.&lt;/li&gt;&lt;li&gt;les nouveaux fichiers ou fichiers non optimisées qui appartienent à un &lt;b&gt;ZCube partiel.&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 6df83545-a81d-44f1-826a-4192835ba296--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6df83545-a81d-44f1-826a-4192835ba296&quot;&gt;Il y a deux paramètre de configuration pour définir ce qu’est un &lt;b&gt;ZCube &lt;/b&gt;et &lt;b&gt;ZCube partiel&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6df83545-a81d-44f1-826a-4192835ba296--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;MIN_CUBE_SIZE &lt;/b&gt;: Taille du ZCube pour laquelle les nouvelles données ne seront plus fusionnées avec celui-ci lors du OPTIMIZE incrémentiel. La valeur par défaut est 100 Go.&lt;/li&gt;&lt;li&gt;&lt;b&gt;TARGET_CUBE_SIZE : &lt;/b&gt;taille cible des ZCubes que nous allons créer. Il ne s’agit pas d’un maximum absolu ; nous continuerons à ajouter des fichiers à un ZCube jusqu&amp;#39;à ce que leur taille combinée dépasse cette valeur. Cette valeur doit être supérieure ou égale à MIN_CUBE_SIZE. La valeur par défaut est 150 Go.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph dd017614-acc6-4563-b1c7-2c245e322b0b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dd017614-acc6-4563-b1c7-2c245e322b0b&quot;&gt;Un &lt;b&gt;ZCube &lt;/b&gt;est &lt;b&gt;partiel &lt;/b&gt;si sa taille est plus petite que le &lt;b&gt;MIN_CUBE_SIZE&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph dd017614-acc6-4563-b1c7-2c245e322b0b--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 10571d5e-f16c-4e33-a501-4d1d44d3f11f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-10571d5e-f16c-4e33-a501-4d1d44d3f11f&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 10571d5e-f16c-4e33-a501-4d1d44d3f11f--&gt;
&lt;!-- begin paragraph 3d58b5c4-123b-41b4-a2b6-1731f094e3d9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d58b5c4-123b-41b4-a2b6-1731f094e3d9&quot;&gt;&lt;b&gt;Que se passe-t-il lors du déclenchement d’un OPTIMIZE ?&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 3d58b5c4-123b-41b4-a2b6-1731f094e3d9--&gt;
&lt;!-- begin paragraph a7c18eb7-4991-47a8-a6de-11921aabb98c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a7c18eb7-4991-47a8-a6de-11921aabb98c&quot;&gt;Un ensemble de fichiers candidats va être sélectionnés, ils proviennent soient de :&lt;/p&gt;
&lt;!-- end paragraph a7c18eb7-4991-47a8-a6de-11921aabb98c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;ZCubes partiels&lt;/li&gt;&lt;li&gt;ou de nouveaux fichiers qui ont été ajoutés après&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 56201a37-1c00-4368-85bd-4d8ebc91ac1d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-56201a37-1c00-4368-85bd-4d8ebc91ac1d&quot;&gt;Pour les rassembler en plusieurs ZCubes, où l’algorithme de la courbe va être appliqué. Chaque ZCube créé produit un commit, ce qui permet de sauvegarder le résultat partiel en cas de crash.&lt;/p&gt;
&lt;!-- end paragraph 56201a37-1c00-4368-85bd-4d8ebc91ac1d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2e0d97fc-cfa4-4cdc-82d1-a28eecd23e6d/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 1176a3a2-9627-4b5f-920a-779b0da1ee49--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1176a3a2-9627-4b5f-920a-779b0da1ee49&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1176a3a2-9627-4b5f-920a-779b0da1ee49--&gt;
&lt;!-- begin paragraph ba9f6616-cda3-412f-bd4e-258acf7a8b5d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba9f6616-cda3-412f-bd4e-258acf7a8b5d&quot;&gt;Une analogie intéressante, qui illustre un peu le comportement du Liquid Clustering est que cela ressemble à une défragmentation de disque dur, on va chercher à remplir les ZCube au maximum pour éviter les petits fichiers.&lt;/p&gt;
&lt;!-- end paragraph ba9f6616-cda3-412f-bd4e-258acf7a8b5d--&gt;
&lt;!-- begin heading_2 f7046125-0ec3-45e5-b8ab-6c280e5f45db--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f7046125-0ec3-45e5-b8ab-6c280e5f45db&quot;&gt;Quand l’utiliser ?&lt;/h3&gt;
&lt;!-- end heading_2 f7046125-0ec3-45e5-b8ab-6c280e5f45db--&gt;
&lt;!-- begin paragraph 1f054511-bb2f-4186-bd70-cc021cfcf3d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f054511-bb2f-4186-bd70-cc021cfcf3d8&quot;&gt;De la même manière que Z-Order, il faut le faire sur des colonnes souvent utilisées comme prédicat et qui ont une haute cardinalité.&lt;/p&gt;
&lt;!-- end paragraph 1f054511-bb2f-4186-bd70-cc021cfcf3d8--&gt;
&lt;!-- begin paragraph f8634fe5-0fdb-482c-975d-0f3083537500--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f8634fe5-0fdb-482c-975d-0f3083537500&quot;&gt;Le liquid clusturing offre surtout une flexibilité accrue par rapport au Z-Ordering, par rapport à la construction du clustering et sa maintenance, donc il conviendra mieux aux tables dont les requêtes sont voués à changer souvent, et aux tables qui grandissent vite.&lt;/p&gt;
&lt;!-- end paragraph f8634fe5-0fdb-482c-975d-0f3083537500--&gt;
&lt;!-- begin paragraph cf10ec89-ced7-4cee-be45-67c9b153d95e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf10ec89-ced7-4cee-be45-67c9b153d95e&quot;&gt;Si les colonnes de clusturing changent, alors les fichiers produits et déjà optimisés précédemment avec l’ancien set de colonnes ne seront pas touchés. Seul les nouvelles données seront clusterisés avec le nouveau set de colonnes. &lt;/p&gt;
&lt;!-- end paragraph cf10ec89-ced7-4cee-be45-67c9b153d95e--&gt;
&lt;!-- begin paragraph 63788e66-6cd0-453e-85f3-ba8f5d8e9f0b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-63788e66-6cd0-453e-85f3-ba8f5d8e9f0b&quot;&gt;Cela n’est pas compatible avec le Z-Order et le Hive-Style-partitioning, pour convertir ces tables il faudra utiliser les colonnes de partitions et Z-Order comme colonnes de Liquid Clusturing.&lt;/p&gt;
&lt;!-- end paragraph 63788e66-6cd0-453e-85f3-ba8f5d8e9f0b--&gt;
&lt;!-- begin paragraph e8abf85d-0023-4f0a-88b4-8e14309132ca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e8abf85d-0023-4f0a-88b4-8e14309132ca&quot;&gt;&lt;span class=&quot;text-color-red&quot;&gt;&lt;b&gt;Comme dit en début de cette section, le Liquid Clustering est encore en phase expérimentale, toutes les fonctionnalités ne sont pas encore supportés dans la version 3.1 de Delta Lake&lt;/b&gt;&lt;/span&gt;&lt;b&gt;.&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph e8abf85d-0023-4f0a-88b4-8e14309132ca--&gt;
&lt;!-- begin heading_2 e1fbe62e-367e-4bf4-992f-bbf17809f33a--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e1fbe62e-367e-4bf4-992f-bbf17809f33a&quot;&gt;Pour aller plus loin&lt;/h3&gt;
&lt;!-- end heading_2 e1fbe62e-367e-4bf4-992f-bbf17809f33a--&gt;
&lt;!-- begin paragraph b2cc515e-20f1-4c05-b2a3-e1fbdd83d53a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b2cc515e-20f1-4c05-b2a3-e1fbdd83d53a&quot;&gt;&lt;a href=&quot;https://docs.google.com/document/d/1FWR3odjOw4v4-hjFy_hVaNdxHVs4WuK1asfB6M6XEMw/edit#heading=h.skpz7c7ga1wl&quot;&gt;Design Doc&lt;/a&gt; de la fonctionnalité, &lt;a href=&quot;https://docs.delta.io/latest/delta-clustering.html&quot;&gt;documentation officielle&lt;/a&gt;. Un &lt;a href=&quot;https://www.youtube.com/watch?v=pUi0gjwPZnk&amp;t=255s&quot;&gt;extrait de la vidéo&lt;/a&gt; de la Keynote d’annonce des nouveautés de Delta Lake 3.0 au Data + AI Summit, qui illustre rapidement le Liquid Clustering.&lt;/p&gt;
&lt;!-- end paragraph b2cc515e-20f1-4c05-b2a3-e1fbdd83d53a--&gt;
&lt;!-- begin paragraph f82363d8-273f-462c-bac4-2d13769479ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f82363d8-273f-462c-bac4-2d13769479ed&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph f82363d8-273f-462c-bac4-2d13769479ed--&gt;
&lt;!-- begin heading_1 ca397aac-05b8-4e27-9422-3500fffe7996--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ca397aac-05b8-4e27-9422-3500fffe7996&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 ca397aac-05b8-4e27-9422-3500fffe7996--&gt;
&lt;!-- begin paragraph 4a3e35c6-e771-4da0-b625-6969a8189bfe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a3e35c6-e771-4da0-b625-6969a8189bfe&quot;&gt;J’espère que vous en savez maintenant plus sur ce qu’est le Z-Ordering et le Liquid Clustering, et comment ils peuvent améliorer les performances de vos tables, Il faudra surveiller de près ce qui fait autour de ce dernier.&lt;/p&gt;
&lt;!-- end paragraph 4a3e35c6-e771-4da0-b625-6969a8189bfe--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:7905a2cb810e45a5b527beeb0149f49a</id>
    <title>Créer ou modifier un tableau croisé dynamique à partir de ses données Spark avec Apache POI</title>
    <updated>2024-04-03T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/creer-ou-modifier-un-tableau-croise-dynamique-a-partir-de-ses-donnees-spark-avec-apache-poi.html"/>
    <!--summary Créer ou modifier un tableau croisé dynamique à partir de ses données Spark avec Apache POI-->    <author>
      <name>Bernarith Men</name>
      <uri>https://univalence.io/blog/auteurs/bernarith-men.html</uri>
    </author>        <category term="Scala"></category>    <category term="Spark"></category>    <category term="Java"></category>    <category term="Excel"></category>    <category term="Apache POI"></category>    <content type="html">
&lt;!-- begin paragraph e32505a2-b33c-4ed0-8a6a-3dca2bfefe92--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e32505a2-b33c-4ed0-8a6a-3dca2bfefe92&quot;&gt;Il peut vous arriver lors de votre carrière que l’on vous demande de créer un fichier Excel à partir de vos données, que soit pour du reporting ou autres. Pour créer des fichiers Excel en Scala, on va devoir utiliser l’interopérabilité avec Java et utiliser une bibliothèque Java Open Source bien connue pour manipuler les fichiers Microsoft, &lt;a href=&quot;https://poi.apache.org/&quot;&gt;Apache POI&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph e32505a2-b33c-4ed0-8a6a-3dca2bfefe92--&gt;
&lt;!-- begin heading_1 eb836cb3-bf30-4944-8e48-4735d804861a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-eb836cb3-bf30-4944-8e48-4735d804861a&quot;&gt;Configuration&lt;/h2&gt;
&lt;!-- end heading_1 eb836cb3-bf30-4944-8e48-4735d804861a--&gt;
&lt;!-- begin paragraph 9d1196c2-d6ae-4164-b633-436b6f15d9cd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9d1196c2-d6ae-4164-b633-436b6f15d9cd&quot;&gt;Dans notre build.sbt il faut importer les librairies Spark et POI, ici leur dernière version à date.&lt;/p&gt;
&lt;!-- end paragraph 9d1196c2-d6ae-4164-b633-436b6f15d9cd--&gt;
&lt;!-- begin code 0ff04ebc-9c72-43e8-95b3-abd8cab0deab--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0ff04ebc-9c72-43e8-95b3-abd8cab0deab&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies ++= Seq(
  &amp;quot;org.apache.poi&amp;quot; % &amp;quot;poi&amp;quot; % &amp;quot;5.2.5&amp;quot;,
  &amp;quot;org.apache.poi&amp;quot; % &amp;quot;poi-ooxml&amp;quot; % &amp;quot;5.2.5&amp;quot;,
  &amp;quot;org.apache.poi&amp;quot; % &amp;quot;poi-ooxml-lite&amp;quot; % &amp;quot;5.2.5&amp;quot;,
  &amp;quot;org.apache.spark&amp;quot; %% &amp;quot;spark-core&amp;quot; % &amp;quot;3.5.1&amp;quot;,
  &amp;quot;org.apache.spark&amp;quot; %% &amp;quot;spark-sql&amp;quot; % &amp;quot;3.5.1&amp;quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0ff04ebc-9c72-43e8-95b3-abd8cab0deab--&gt;
&lt;!-- begin paragraph e0eb8e45-17f9-4c51-98b1-426e0840f428--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e0eb8e45-17f9-4c51-98b1-426e0840f428&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e0eb8e45-17f9-4c51-98b1-426e0840f428--&gt;
&lt;!-- begin paragraph 903258bc-d7b6-4139-860b-0936a1af5aea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-903258bc-d7b6-4139-860b-0936a1af5aea&quot;&gt;Pour le jeu de données que l’on va utiliser en exemple, nous allons prendre ces données Open Data de Paris &lt;a href=&quot;https://opendata.paris.fr/explore/dataset/compte-de-resultat/information/&quot;&gt;https://opendata.paris.fr/explore/dataset/compte-de-resultat/information/&lt;/a&gt; qui sont les comptes de résultats de la ville, l’export parquet a été utilisé.&lt;/p&gt;
&lt;!-- end paragraph 903258bc-d7b6-4139-860b-0936a1af5aea--&gt;
&lt;!-- begin heading_1 86ace7cc-bc1f-4b1b-95ae-929fea2e4c64--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-86ace7cc-bc1f-4b1b-95ae-929fea2e4c64&quot;&gt;Comment créer un fichier Excel à partir de mes données Spark ?&lt;/h2&gt;
&lt;!-- end heading_1 86ace7cc-bc1f-4b1b-95ae-929fea2e4c64--&gt;
&lt;!-- begin paragraph c57d0fc5-cc7e-4c03-b2d6-82c03d0e3666--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c57d0fc5-cc7e-4c03-b2d6-82c03d0e3666&quot;&gt;Pour faire cela, il n’y a pas vraiment d’autres choix que d’abord “collecter” vos données, (après agrégation ou non), et de transformer vos lignes afin de créer chaque cellule de notre fichier Excel.&lt;/p&gt;
&lt;!-- end paragraph c57d0fc5-cc7e-4c03-b2d6-82c03d0e3666--&gt;
&lt;!-- begin paragraph 6a995c1a-eddd-43de-aeb2-f3dab3e6cb3a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a995c1a-eddd-43de-aeb2-f3dab3e6cb3a&quot;&gt;Pour la création de cellules, cela se fait de manière “classique” :&lt;/p&gt;
&lt;!-- end paragraph 6a995c1a-eddd-43de-aeb2-f3dab3e6cb3a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;D’abord en instanciant un objet &lt;code&gt;XSSFWorkbook&lt;/code&gt; pour travailler avec le type de fichier .xlsx.&lt;/li&gt;&lt;li&gt;Ensuite nous aurons besoin d’une feuille, pour cela il faudra créer un objet &lt;code&gt;XSSFSheet&lt;/code&gt;, à l’aide de la méthode &lt;code&gt;createSheet&lt;/code&gt; sur l’objet XSSFWorkbook.&lt;/li&gt;&lt;li&gt;Il vous faudra ensuite créer les lignes à partir de ce dernier avec &lt;code&gt;createRow&lt;/code&gt;, ce qui nous donne un objet &lt;code&gt;XSSFRow&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Puis créer les cellules avec &lt;code&gt;createCell&lt;/code&gt; sur les objets XSSRow, ce qui donnera un objet &lt;code&gt;XSSFCell&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Et enfin pour modifier le contenu de vos cellules, il faudra utiliser la méthode &lt;code&gt;setCellValue&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 77f6fec7-f87b-45f6-802f-e881334f5a58--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77f6fec7-f87b-45f6-802f-e881334f5a58&quot;&gt;Pour écrire notre fichier, on utilisera la méthode &lt;code&gt;write&lt;/code&gt; du XSSFWorkbook, qui prend en paramètre un &lt;code&gt;OutputStream&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 77f6fec7-f87b-45f6-802f-e881334f5a58--&gt;
&lt;!-- begin paragraph 7ba9adfa-9092-499a-a8ab-5bf77209b214--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7ba9adfa-9092-499a-a8ab-5bf77209b214&quot;&gt;Voici un exemple de code :&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph 7ba9adfa-9092-499a-a8ab-5bf77209b214--&gt;
&lt;!-- begin code 1fbcbf51-be8a-4042-8dcf-c28e645dcb2f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1fbcbf51-be8a-4042-8dcf-c28e645dcb2f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.poi.xssf.usermodel.{XSSFCell, XSSFRow, XSSFSheet, XSSFWorkbook}
import org.apache.spark.sql.{Row, SparkSession}
import java.io.{FileNotFoundException, FileOutputStream, IOException}

val spark = SparkSession.builder()
  .master(&amp;quot;local[*]&amp;quot;)
  .getOrCreate()
  
val compteDeResultatDf = spark.read.parquet(&amp;quot;compte-de-resultat.parquet&amp;quot;)

/*
compteDeResultatDf.printSchema()
root
 |-- exercice_comptable: string (nullable = true)
 |-- type_de_resultat: string (nullable = true)
 |-- produit_charge: string (nullable = true)
 |-- poste: string (nullable = true)
 |-- detail_postes: string (nullable = true)
 |-- montant_en_eur: double (nullable = true)
 */
 
//certains montants sont à null, on leur donne le montant 0 par défaut.
//et mise à négatif du montant quand son type est charges.
val dfCleaned = compteDeResultatDf
  .na.fill(0, Seq(&amp;quot;montant_en_eur&amp;quot;))
  .withColumn(&amp;quot;montant_en_eur&amp;quot;,
    when($&amp;quot;produit_charge&amp;quot; === &amp;quot;CHARGES&amp;quot;, -$&amp;quot;montant_en_eur&amp;quot;) //mise à négatif du montant quand son type est charges
      .otherwise($&amp;quot;montant_en_eur&amp;quot;)
  )
  
val dataCollected = dfCleaned
        .collect()

val wb: XSSFWorkbook = new XSSFWorkbook() //création du workbook
val sheet: XSSFSheet = wb.createSheet(&amp;quot;ma feuille&amp;quot;) //création d&amp;#39;une feuille dans le workbook

//Création d&amp;#39;un style de cellule pour les montants, ici pour obtenir 2 chiffres après la virgule dans Excel
val amountStyle= wb.createCellStyle()
  amountStyle.setDataFormat(wb.createDataFormat().getFormat(&amp;quot;0.00&amp;quot;))
  
//notre première ligne sera le header de nos données
val header = dfCleaned.columns
val headerRow = sheet.createRow(0)
for {
  i &amp;lt;- header.indices
  cell = headerRow.createCell(i)
} yield cell.setCellValue(header(i))

for {
  i &amp;lt;- dataCollected.indices
  poiRow: XSSFRow = sheet.createRow(i) //pour chaque ligne de nos données, on créé une ligne Excel, en sautant la première ligne dans l&amp;#39;Excel
  sparkRow: Row = dataCollected(i) //on prend aussi la Row en cours
  j &amp;lt;- 0 until sparkRow.size
  cell: XSSFCell = poiRow.createCell(j) //pour chaque colonne de nos données, on créé une cellule Excel
  value: Any = sparkRow(j) //on prend la valeur de la colonne en cours
} yield value match {
  //Pattern matching pour éventuellement convertir certaines données,
  //en effet setCellValue n&amp;#39;accepte que certains types en entrée.
  //Ici que Double et String car le schéma de notre DataFrame ne contient que ces derniers pour l&amp;#39;exemple.
  //On peut y ajouter du Style/Formattage selon le type auquel on match,
  //ici le format qui nous permet d&amp;#39;avoir 2 chiffres après la virgule.
  case d: Double =&amp;gt; {
    cell.setCellStyle(amountStyle)
    cell.setCellValue(d)
  }
  case s: String =&amp;gt; cell.setCellValue(s)
}

//pour régler la largeur de nos colonnes automatiquement, attention plus vous avez de données, plus cela est coûteux
header.indices.foreach(sheet.autoSizeColumn)

//écriture de notre fichier
val os = new FileOutputStream(&amp;quot;workbook.xlsx&amp;quot;)
try {
  wb.write(os)
} catch {
  case fnf: FileNotFoundException =&amp;gt; fnf.printStackTrace()
  case ioe: IOException =&amp;gt; ioe.printStackTrace()
} finally {
  os.close()
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1fbcbf51-be8a-4042-8dcf-c28e645dcb2f--&gt;
&lt;!-- begin paragraph 7a3cad61-8b17-4d08-b175-48976a2e0bd5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7a3cad61-8b17-4d08-b175-48976a2e0bd5&quot;&gt;Voici le résultat après ouverture du fichier sous Excel :&lt;/p&gt;
&lt;!-- end paragraph 7a3cad61-8b17-4d08-b175-48976a2e0bd5--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/7905a2cb-810e-45a5-b527-beeb0149f49a/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 e66f4f34-10ca-4c48-9ba9-48327b25b134--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e66f4f34-10ca-4c48-9ba9-48327b25b134&quot;&gt;Comment faire pour créer un tableau croisé dynamique avec Apache POI ?&lt;/h2&gt;
&lt;!-- end heading_1 e66f4f34-10ca-4c48-9ba9-48327b25b134--&gt;
&lt;!-- begin paragraph 0c166d08-8961-4de2-8be9-aa2f24b19589--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0c166d08-8961-4de2-8be9-aa2f24b19589&quot;&gt;Maintenant que nous avons vu comment comment créer un fichier Excel à l’aide de nos données, nous allons voir comment créer un tableau croisé dynamique.&lt;/p&gt;
&lt;!-- end paragraph 0c166d08-8961-4de2-8be9-aa2f24b19589--&gt;
&lt;!-- begin paragraph fe5c47c5-286b-4a52-b100-922f1906bf9e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fe5c47c5-286b-4a52-b100-922f1906bf9e&quot;&gt;Une fois notre &lt;code&gt;XSSFWorkbook&lt;/code&gt; instancié et rempli de données, il suffira d’utiliser la méthode &lt;code&gt;.createPivotTable&lt;/code&gt; sur un objet &lt;code&gt;XSSFSheet&lt;/code&gt;. La méthode prend en paramètre :&lt;/p&gt;
&lt;!-- end paragraph fe5c47c5-286b-4a52-b100-922f1906bf9e--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;le tableau source qui sera utilisé pour le tableau croisé dynamique, ci-dessous on utilisera un objet &lt;code&gt;AreaReference&lt;/code&gt; pour indiquer la plage du tableau&lt;/li&gt;&lt;li&gt;la cellule qui recevra le TCD, on utilise un objet &lt;code&gt;CellReference&lt;/code&gt; pour cela, la cellule est dans la feuille avec laquelle on a invoqué la méthode&lt;/li&gt;&lt;li&gt;en option, un object &lt;code&gt;XSSFSheet&lt;/code&gt;, si la source est dans une autre feuille, cela sera le cas de notre exemple&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 142d75ab-0028-42ae-a6aa-ee3b3172726f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-142d75ab-0028-42ae-a6aa-ee3b3172726f&quot;&gt;On reprend le code précédent juste avant l’écriture du fichier:&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph 142d75ab-0028-42ae-a6aa-ee3b3172726f--&gt;
&lt;!-- begin code ce2421af-a73b-4a57-8897-b53196941a99--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ce2421af-a73b-4a57-8897-b53196941a99&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.poi.ss.usermodel.DataConsolidateFunction
import org.apache.poi.ss.util.{AreaReference, CellReference}
import org.apache.poi.xssf.usermodel.{XSSFCell, XSSFPivotTable, XSSFRow, XSSFSheet, XSSFWorkbook}

/*
code précédent de création
*/

//on créé une nouvelle feuille qui va contenir notre TCD
val tcdSheet: XSSFSheet = wb.createSheet(&amp;quot;TCD&amp;quot;)
val pivotTable: XSSFPivotTable = tcdSheet.createPivotTable(
  new AreaReference(&amp;quot;A1:F288&amp;quot;, null), //les coordonnées du tableau source, null car la méthode prend par défaut un deuxième paramètre qui est la version du spreadsheet
  new CellReference(&amp;quot;A1&amp;quot;), //celulle qui recevra le TCD
  sheet) //on reference la feuille source optionnellement, par défaut la feuille source est celle utilisée pour invoquer la méthode createPivotTable 
      
 
//Nous allons utiliser comme étiquette de lignes :
pivotTable.addRowLabel(0) //exercice_comptable -&amp;gt; colonne 0 de notre tableau source
pivotTable.addRowLabel(1) //type_de_resultat
pivotTable.addRowLabel(2) //produit_charge
//Nous ajoutons une colonne qui sommera les montants
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 5)

//écriture de notre fichier
val os = new FileOutputStream(&amp;quot;workbook.xlsx&amp;quot;)
try {
  wb.write(os)
} catch {
  case fnf: FileNotFoundException =&amp;gt; fnf.printStackTrace()
  case ioe: IOException =&amp;gt; ioe.printStackTrace()
} finally {
  os.close()
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ce2421af-a73b-4a57-8897-b53196941a99--&gt;
&lt;!-- begin paragraph 9e3d26f5-46ea-4065-8902-27cb1cdf5640--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e3d26f5-46ea-4065-8902-27cb1cdf5640&quot;&gt;On retrouve notre TCD dans la feuille “TCD” :&lt;/p&gt;
&lt;!-- end paragraph 9e3d26f5-46ea-4065-8902-27cb1cdf5640--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/7905a2cb-810e-45a5-b527-beeb0149f49a/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 193c7a11-f29a-4c22-a2ee-6e11d7434ab1--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-193c7a11-f29a-4c22-a2ee-6e11d7434ab1&quot;&gt;Comment faire pour modifier un fichier Excel déjà existant avec mes données ?&lt;/h2&gt;
&lt;!-- end heading_1 193c7a11-f29a-4c22-a2ee-6e11d7434ab1--&gt;
&lt;!-- begin paragraph 48bbb337-5f50-46ff-a1ad-2c706703ebda--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-48bbb337-5f50-46ff-a1ad-2c706703ebda&quot;&gt;Et si nous souhaitons, mettre à jour nos données ? Imaginons que c’est une correction de montants à appliquer. Nous allons créer la case class CompteDeResultat et créer un petit Dataset de correction.&lt;/p&gt;
&lt;!-- end paragraph 48bbb337-5f50-46ff-a1ad-2c706703ebda--&gt;
&lt;!-- begin code 8c57f801-b22c-4ac3-b406-3ac78fc63d69--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8c57f801-b22c-4ac3-b406-3ac78fc63d69&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class CompteDeResultat(
                             exercice_comptable: String,
                             type_de_resultat: String,
                             produit_charge: String,
                             poste: String,
                             details_postes: String,
                             montant_en_eur: Double
                           )&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8c57f801-b22c-4ac3-b406-3ac78fc63d69--&gt;
&lt;!-- begin code f81564e0-f259-44fd-ab7b-ec47f8f3395d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f81564e0-f259-44fd-ab7b-ec47f8f3395d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.poi.ss.usermodel.{Cell, Row, Sheet, Workbook, WorkbookFactory}
import org.apache.spark.sql.{Dataset, SparkSession}

import java.io.{FileInputStream, FileNotFoundException, FileOutputStream, IOException}
import scala.jdk.CollectionConverters._

//création &amp;quot;rapide&amp;quot; d&amp;#39;un dataset de correction de montants, cela aurait pu venir d&amp;#39;une table de correction
val spark = SparkSession.builder()
  .master(&amp;quot;local[*]&amp;quot;)
  .getOrCreate()
  
import spark.implicits._

val correctionDS: Dataset[CompteDeResultat] = List(
  CompteDeResultat(&amp;quot;2016&amp;quot;,&amp;quot;EXPLOITATION&amp;quot;,&amp;quot;PRODUITS&amp;quot;,&amp;quot;PRODUITS COURANTS NON FINANCIERS&amp;quot;,&amp;quot;Production stockée&amp;quot;, 99999999999.0),
  CompteDeResultat(&amp;quot;2016&amp;quot;,&amp;quot;FINANCIER&amp;quot;,&amp;quot;PRODUITS&amp;quot;,&amp;quot;PRODUITS COURANTS FINANCIERS&amp;quot;,&amp;quot;Valeurs mob et créances de l&amp;#39;actifimmo&amp;quot;, 55555555555.0)
).toDS
/*
correctionDS.printSchema()
root
 |-- exercice_comptable: string (nullable = true)
 |-- type_de_resultat: string (nullable = true)
 |-- produit_charge: string (nullable = true)
 |-- poste: string (nullable = true)
 |-- detail_postes: string (nullable = true)
 |-- montant_en_eur: double (nullable = false)
 */&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f81564e0-f259-44fd-ab7b-ec47f8f3395d--&gt;
&lt;!-- begin paragraph 1412c99c-be58-473e-a423-af97399fdba6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1412c99c-be58-473e-a423-af97399fdba6&quot;&gt;Utilisons le fichier que nous avons précédemment créé, l’idée est que nous connaissons déjà son schéma, sa structure ne changera pas, donc nous savons comment le lire, c’est à dire que le code que l’on va définir sera assez spécifique à notre fichier Excel, ici on sait que c’est le montant que l’on veut modifier.&lt;/p&gt;
&lt;!-- end paragraph 1412c99c-be58-473e-a423-af97399fdba6--&gt;
&lt;!-- begin paragraph 4acb362c-29a9-4264-a519-a761ac6836b3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4acb362c-29a9-4264-a519-a761ac6836b3&quot;&gt;On va créer deux Map :&lt;/p&gt;
&lt;!-- end paragraph 4acb362c-29a9-4264-a519-a761ac6836b3--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;À partir du fichier Excel, une qui associe toutes les colonnes (hors colonnes à modifier) aux coordonnées des cellules à modifier&lt;/li&gt;&lt;li&gt;À partir de notre dataFrame, une Map qui aura la même clé et qui associe la nouvelle valeur&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph ea34ba5b-bcbe-4edd-8e1e-37bcbbae50c6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ea34ba5b-bcbe-4edd-8e1e-37bcbbae50c6&quot;&gt;Pour créer ces Maps, nous définirons ces méthodes :&lt;/p&gt;
&lt;!-- end paragraph ea34ba5b-bcbe-4edd-8e1e-37bcbbae50c6--&gt;
&lt;!-- begin code de343848-0ec3-4eff-8271-9709263a9823--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-de343848-0ec3-4eff-8271-9709263a9823&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def getMapFromWorkbook(wb: Workbook, sheetName: String, columnToModify: String): Map[List[String], (Int, Int)] = {
  val sheet: Sheet = wb.getSheet(sheetName)
  //on récupère du header, l&amp;#39;index des colonnes à exclure
  val headerRow = sheet.getRow(0)
  val columnIndexToExcludeFromKey = headerRow.cellIterator().asScala.toList
    .find(cell =&amp;gt; cell.getStringCellValue == columnToModify)
    .map(_.getColumnIndex)
    .getOrElse(-1)
  //création de la Map qui associe chaque clé aux coordonnées des cellules à modifier
  (for {
    row: Row &amp;lt;- sheet.rowIterator().asScala.toList.tail
    cells: List[Cell] = row.cellIterator().asScala.toList.filterNot(_.getColumnIndex == columnIndexToExcludeFromKey)
  } yield cells.map(_.getStringCellValue) -&amp;gt; (row.getRowNum, columnIndexToExcludeFromKey)
    ).toMap
}
/*extrait de la Map après execution
HashMap(
List(2021, EXPLOITATION, CHARGES, CHARGES D&amp;#39;INTERVENTION, Dont autres organismes publics) -&amp;gt; (212,5),
List(2019, FINANCIER, CHARGES, CHARGES FINANCIERES, Charges nettes sur cessions de valeurs mobilières de placement) -&amp;gt; (44,5),
...)
*/

def getMapFromDataset(ds: Dataset[CompteDeResultat]): Map[List[String], Double] = {
  ds.collect
    .map(row =&amp;gt; List(
      row.exercice_comptable,
      row.type_de_resultat,
      row.produit_charge,
      row.poste,
      row.details_postes
    ) -&amp;gt; row.montant_en_eur
    ).toMap
}
/*extrait de la Map
Map(List(2016, EXPLOITATION, PRODUITS, PRODUITS COURANTS NON FINANCIERS, Production stockée) -&amp;gt; 9.99999999E8,
List(2016, FINANCIER, PRODUITS, PRODUITS COURANTS FINANCIERS, Valeurs mob et créances de l&amp;#39;actifimmo) -&amp;gt; 5.55555555E8)
*/&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code de343848-0ec3-4eff-8271-9709263a9823--&gt;
&lt;!-- begin paragraph fab43e0b-93ba-472b-89dd-249640d01245--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fab43e0b-93ba-472b-89dd-249640d01245&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fab43e0b-93ba-472b-89dd-249640d01245--&gt;
&lt;!-- begin paragraph 0e7e8e63-5f2e-4e50-99ad-b54fd4cf1d95--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e7e8e63-5f2e-4e50-99ad-b54fd4cf1d95&quot;&gt;Pour lire un Excel avec POI, on utilise un &lt;code&gt;FileInputStream&lt;/code&gt; et POI propose un &lt;code&gt;WorkbookFactory&lt;/code&gt; qui nous permet d’obtenir un &lt;code&gt;Workbook&lt;/code&gt; à partir de l’input stream.&lt;/p&gt;
&lt;!-- end paragraph 0e7e8e63-5f2e-4e50-99ad-b54fd4cf1d95--&gt;
&lt;!-- begin paragraph 94414326-4813-4146-8418-8780b43efe4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-94414326-4813-4146-8418-8780b43efe4d&quot;&gt;On fait ensuite correspondre les deux Maps pour appliquer les modifications, enfin on écrit le workbook dans un autre fichier Excel new_workbook.xlsx.&lt;/p&gt;
&lt;!-- end paragraph 94414326-4813-4146-8418-8780b43efe4d--&gt;
&lt;!-- begin code dedd0e66-c52e-4b9a-ae63-f197b499106a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dedd0e66-c52e-4b9a-ae63-f197b499106a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val inp = new FileInputStream(&amp;quot;workbook.xlsx&amp;quot;) //lecture de notre Excel
try {
  val wb: Workbook = WorkbookFactory.create(inp) //création d&amp;#39;un objet workbook à partir de la bibliothèque
  val mapWB = getMapFromWorkbook(wb, &amp;quot;ma feuille&amp;quot;, &amp;quot;montant_en_eur&amp;quot;)
  val mapDS = getMapFromDataset(correctionDS)
  val sheet = wb.getSheet(&amp;quot;ma feuille&amp;quot;)
  //modifications des cellules à partir des deux Maps
  for {
    modifications: (List[String], Double) &amp;lt;- mapDS
    coordinates: (Int, Int) = mapWB(modifications._1)
    row = sheet.getRow(coordinates._1)
    cell = row.getCell(coordinates._2)
  } yield {
        cell.setCellValue(modifications._2)
    }
  //écriture de notre fichier sous le nom new_workbook.xlsx
  val os = new FileOutputStream(&amp;quot;new_workbook.xlsx&amp;quot;)
  try {
    wb.write(os)
  } catch {
    case fnf: FileNotFoundException =&amp;gt; fnf.printStackTrace()
    case ioe: IOException =&amp;gt; ioe.printStackTrace()
  } finally {
    os.close()
  }
} finally if (inp != null) inp.close()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dedd0e66-c52e-4b9a-ae63-f197b499106a--&gt;
&lt;!-- begin paragraph 07bcc255-dbe2-46f0-9f0e-2d77a578f7c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-07bcc255-dbe2-46f0-9f0e-2d77a578f7c3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 07bcc255-dbe2-46f0-9f0e-2d77a578f7c3--&gt;
&lt;!-- begin paragraph 046a49c2-86a2-44a0-afe7-4d398dbbf8b1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-046a49c2-86a2-44a0-afe7-4d398dbbf8b1&quot;&gt;Sur le nouveau fichier nous voyons bien les deux lignes modifiées (en surbrillance sur la capture) :&lt;/p&gt;
&lt;!-- end paragraph 046a49c2-86a2-44a0-afe7-4d398dbbf8b1--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/7905a2cb-810e-45a5-b527-beeb0149f49a/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 32cd7bcb-b80d-4168-bc0a-2a48cd4c9ec5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-32cd7bcb-b80d-4168-bc0a-2a48cd4c9ec5&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 32cd7bcb-b80d-4168-bc0a-2a48cd4c9ec5--&gt;
&lt;!-- begin paragraph 4f7eb575-dc46-4596-8124-8278e6dca75c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4f7eb575-dc46-4596-8124-8278e6dca75c&quot;&gt;Sur le TCD, on a bien les valeurs à jour aussi :&lt;/p&gt;
&lt;!-- end paragraph 4f7eb575-dc46-4596-8124-8278e6dca75c--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/7905a2cb-810e-45a5-b527-beeb0149f49a/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 d5f23ff2-8620-4297-af74-4e524425f92d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d5f23ff2-8620-4297-af74-4e524425f92d&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 d5f23ff2-8620-4297-af74-4e524425f92d--&gt;
&lt;!-- begin paragraph bd958c52-49fd-41b7-bf12-be6f45a4ae86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd958c52-49fd-41b7-bf12-be6f45a4ae86&quot;&gt;Nous avons vu comment créer un modifier un fichier Excel à partir de nos données Spark avec Apache POI, avec un focus sur son module XSSF permettant de créer des .xlsx, et avec un exemple sur comment créer un tableau croisé dynamique, je vous invite à regarder sa documentation officielle &lt;a href=&quot;https://poi.apache.org/components/spreadsheet/how-to.html&quot;&gt;https://poi.apache.org/components/spreadsheet/how-to.html&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph bd958c52-49fd-41b7-bf12-be6f45a4ae86--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:761a510f380e4f63a491bd5b87ccebb8</id>
    <title>Retour sur ScalaIO 2024</title>
    <updated>2024-03-17T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/retour-sur-scalaio-2024.html"/>
    <!--summary Retour sur Scala.IO 2024-->    <author>
      <name>Bernarith Men</name>
      <uri>https://univalence.io/blog/auteurs/bernarith-men.html</uri>
    </author>        <category term="ScalaIO"></category>    <category term="Conférence"></category>    <category term="FP"></category>    <category term="Scala"></category>    <category term="Community"></category>    <content type="html">
&lt;!-- begin paragraph 44336225-783d-4f0b-a602-d05f9707ee47--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-44336225-783d-4f0b-a602-d05f9707ee47&quot;&gt;Le précédent &lt;a href=&quot;https://scala.io/&quot;&gt;ScalaIO&lt;/a&gt; s’était déroulé en Novembre 2022 à Paris, l’édition 2023 manquant à l’appel, c’était avec grand plaisir que nous ayons pu assister à la récente édition qui a pu se faire en ce début d’année, les 15 et 16 Février 2024 à Nantes dans &lt;a href=&quot;https://icilundi.fr/lieux/le-palace/&quot;&gt;Le Palace&lt;/a&gt;, un espace de coworking et d’évènements. Pour rappel ScalaIO est une conférence autour du langage Scala (le langage, des librairies, …), mais pas seulement, on peut y trouver des sujets autour de la programmation fonctionnelle (dans d’autres langages ou non), de data engineering, de communauté, et aussi, ce qui fait beaucoup parler en ce moment, d’intelligence artificielle.&lt;/p&gt;
&lt;!-- end paragraph 44336225-783d-4f0b-a602-d05f9707ee47--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/761a510f-380e-4f63-a491-bd5b87ccebb8/untitled.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 2c832e01-ef79-4a8d-8042-e2a0ee83de45--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2c832e01-ef79-4a8d-8042-e2a0ee83de45&quot;&gt;Les talks&lt;/h2&gt;
&lt;!-- end heading_1 2c832e01-ef79-4a8d-8042-e2a0ee83de45--&gt;
&lt;!-- begin paragraph c8ae0fb9-030b-4616-9ede-cb3064ac875c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c8ae0fb9-030b-4616-9ede-cb3064ac875c&quot;&gt;A savoir que cette édition, nous retrouvons beaucoup de talks multilingues, beaucoup d’anglais, et certaines en français, et il y en avait pour tous les niveaux, de sujets plus ou moins avancés. Voici un retours sur quelques unes de ces présentations.&lt;/p&gt;
&lt;!-- end paragraph c8ae0fb9-030b-4616-9ede-cb3064ac875c--&gt;
&lt;!-- begin heading_2 cfcfe03c-7590-4dc2-bb80-8b272e116200--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-cfcfe03c-7590-4dc2-bb80-8b272e116200&quot;&gt;Keynote d’ouverture - &lt;a href=&quot;https://youtu.be/r4c7xuVB9xA?si=R3LaeT7BK2R7sJHC&quot;&gt;&lt;b&gt;Une autre introduction aux GADTs &lt;/b&gt;&lt;/a&gt;&lt;a href=&quot;https://youtu.be/r4c7xuVB9xA?si=R3LaeT7BK2R7sJHC&quot;&gt;de &lt;/a&gt;&lt;a href=&quot;https://youtu.be/r4c7xuVB9xA?si=R3LaeT7BK2R7sJHC&quot;&gt;&lt;b&gt;Xavier Van de Woestyne&lt;/b&gt;&lt;/a&gt;&lt;b&gt; :&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 cfcfe03c-7590-4dc2-bb80-8b272e116200--&gt;
&lt;!-- begin paragraph b366a176-e3aa-4ff7-95fc-07ace45b365a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b366a176-e3aa-4ff7-95fc-07ace45b365a&quot;&gt;Le speaker fait essentiellement du OCaml et du Kotlin cela peut être surprenant pour ceux qui seraient venus assister pour la première fois à ScalaIO, en effet ce n’était pas un sujet purement Scala mais plutôt un sujet d’algèbre/programmation fonctionnelle.&lt;/p&gt;
&lt;!-- end paragraph b366a176-e3aa-4ff7-95fc-07ace45b365a--&gt;
&lt;!-- begin paragraph e385c90c-8d30-4c65-8c0c-3687ce0209fe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e385c90c-8d30-4c65-8c0c-3687ce0209fe&quot;&gt;Xavier nous introduit aux &lt;b&gt;G&lt;/b&gt;eneralized &lt;b&gt;A&lt;/b&gt;lgebraic &lt;b&gt;D&lt;/b&gt;ata &lt;b&gt;T&lt;/b&gt;ypes, il nous réexplique d’abord rapidement ce que sont les Product Types et Sum Types, et qu’ils ont tous un cas minimal qui permet de décrire tous les autres Types Product et Sum :&lt;/p&gt;
&lt;!-- end paragraph e385c90c-8d30-4c65-8c0c-3687ce0209fe--&gt;
&lt;!-- begin code ad8b05f4-3d52-4307-b21d-b4df159266c8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ad8b05f4-3d52-4307-b21d-b4df159266c8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//code repris de la présentation
//cas minimal de Product Type
case class Prod[+A, +B](fst: A, snd: B)

//permet d&amp;#39;écrire ce produit à 4 éléments
val quad = Prod(1, Prod(2, Prod(3, 4)))

//cas minimal de Sum type, qui en fait un Either
enum Sum[+A, +B]:
	case Left(x: A)
	case Right(x: B)

//on peut écrire ce type Triple à partir de notre Sum de la manière suivante
type Triple = Sum[Int, Sum[Double, String]]
val a : Triple = Sum.Left(1)
val b : Triple = Sum.Right(Sum.Left(1.0))
val c : Triple = Sum.Right(Sum.Rigth(&amp;quot;1&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ad8b05f4-3d52-4307-b21d-b4df159266c8--&gt;
&lt;!-- begin paragraph 6948e158-73f6-4b98-82f6-bfc31adc2dd3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6948e158-73f6-4b98-82f6-bfc31adc2dd3&quot;&gt;Puis Xavier nous donne cette définition :&lt;/p&gt;
&lt;!-- end paragraph 6948e158-73f6-4b98-82f6-bfc31adc2dd3--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;&lt;i&gt;“A Generalized Algebraic Data Type&lt;/i&gt;&lt;i&gt;&lt;b&gt; is a sum type that allows its contructors to be non-surjective&lt;/b&gt;&lt;/i&gt;&lt;i&gt; on one or more of its type parameters and &lt;/i&gt;&lt;i&gt;&lt;b&gt;introduces local type-equality constraints in pattern-matching branches&lt;/b&gt;&lt;/i&gt;&lt;i&gt;, making the expression of existential types trivial”&lt;/i&gt;&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph 608e870d-ae49-4cd3-bdca-a335a97bd814--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-608e870d-ae49-4cd3-bdca-a335a97bd814&quot;&gt;Wow, mais que cela veut-il dire ?&lt;/p&gt;
&lt;!-- end paragraph 608e870d-ae49-4cd3-bdca-a335a97bd814--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;i&gt;&lt;b&gt;a sum type that allows its contructors to be non-surjective &lt;/b&gt;&lt;/i&gt;&lt;i&gt;on one or more of its type parameters&lt;/i&gt; : &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 4618c458-8f0e-4c0e-8d13-846fb8077978--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4618c458-8f0e-4c0e-8d13-846fb8077978&quot;&gt;Rendre les constructeurs de son Sum Type non-surjectifs sur un ou plus de ses paramètres de types, c’est en quelque sorte limiter ce que peut être le A dans les constructeurs du GADT :&lt;/p&gt;
&lt;!-- end paragraph 4618c458-8f0e-4c0e-8d13-846fb8077978--&gt;
&lt;!-- begin code abb21111-bd21-427b-a011-a13b6a40fba1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-abb21111-bd21-427b-a011-a13b6a40fba1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//code repris de la présentation
enum AST[A]:
	case I(x: Int)                                   extends AST[Int]
	case B(x: Boolean)                               extends AST[Boolean]
	case Add(l: AST[Int], r: AST[Int])               extends AST[Int]
	case Mul(l: AST[Int], r: AST[Int])               extends AST[Int]
	case Equal(l: AST[A], r: AST[A])                 extends AST[Boolean]
	case Cond(c: AST[Boolean], t: AST[A], f: AST[A]) extends AST[A]

def eval[A](ast: AST[A]): A =
	import AST.*
	ast match
		case I(x)            =&amp;gt; x
		case B(x)            =&amp;gt; x
		case Add(l, r)       =&amp;gt; eval(l) + eval(r)
		case Mul(l, r)       =&amp;gt; eval(l) * eval(r)
		case Equal(l, r)     =&amp;gt; eval(l) == eval(r)
		case Cond(c, t, f)   =&amp;gt; if(eval(c)) then eval(t) else eval(f)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code abb21111-bd21-427b-a011-a13b6a40fba1--&gt;
&lt;!-- begin paragraph 103f7b2b-53a8-4ca5-af65-82c0f7aa7ae7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-103f7b2b-53a8-4ca5-af65-82c0f7aa7ae7&quot;&gt;Xavier nous explique que cet exemple fonctionne en Scala 2, et qu’on pourrait penser que cela suffit à démontrer que l’on gère les GADT, sauf qu’il manque la suite de sa définition. Cet exemple ne suffit donc pas.&lt;/p&gt;
&lt;!-- end paragraph 103f7b2b-53a8-4ca5-af65-82c0f7aa7ae7--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;i&gt;&lt;b&gt;introduces local type-equality constraints in pattern-matching branches &lt;/b&gt;&lt;/i&gt;:&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph d5fcb568-0073-4d5a-894d-8d59e2832e4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d5fcb568-0073-4d5a-894d-8d59e2832e4d&quot;&gt;L’idée pour comprendre cette partie est alors de trouver le “GADT minimal” pour prouver la définition du dessus, de manière similaire à ce qui a été décrit pour les Product Type et Sum Type.&lt;/p&gt;
&lt;!-- end paragraph d5fcb568-0073-4d5a-894d-8d59e2832e4d--&gt;
&lt;!-- begin code 0bb0f0ab-0fbd-4d29-a3a9-fbeaa4cf6548--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0bb0f0ab-0fbd-4d29-a3a9-fbeaa4cf6548&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//code repris de la présentation
//definit l&amp;#39;égalité entre A et B
enum Eq[A, B]:
	case Refl[A]() extends Eq[A, A]
//Le seul constructeur possible est celui qui étend Eq[A, A], donc que A = B

type Z = Int
val a : Eq[Int, String] = Eq.Refl() //ne compile pas
val b : Eq[Int, Int] = Eq.Refl()
val c : Eq[Int, Z] = Eq.Refl()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0bb0f0ab-0fbd-4d29-a3a9-fbeaa4cf6548--&gt;
&lt;!-- begin paragraph 3259f39e-db42-4fa6-af59-3f90a189f617--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3259f39e-db42-4fa6-af59-3f90a189f617&quot;&gt;L’idée est que si l’on peut instancier un Refl, c’est que A et B sont égaux localement.&lt;/p&gt;
&lt;!-- end paragraph 3259f39e-db42-4fa6-af59-3f90a189f617--&gt;
&lt;!-- begin paragraph c0baf1da-ac7e-4854-aec9-ab158bc2501d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c0baf1da-ac7e-4854-aec9-ab158bc2501d&quot;&gt;Pour garantir l’égalité, on a des principes de substitution à respecter :&lt;/p&gt;
&lt;!-- end paragraph c0baf1da-ac7e-4854-aec9-ab158bc2501d--&gt;
&lt;!-- begin code 3c057fdf-0c3a-4b2b-85f5-ee38a5ebd8d1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3c057fdf-0c3a-4b2b-85f5-ee38a5ebd8d1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//code repris de la présentation

//Symmetry
def symmetry[A, B](witness: Eq[A, B]): Eq[B, A] =
	witness match
		case Eq.Refl() =&amp;gt; Eq.Refl()

//Transitivity
def transitivity[A, B, C](witnessA: Eq[A, B], witnessB: Eq[B, C]): Eq[A, C] =
	(witnessA, witnessB) match
		case (Eq.Refl(), Eq.Refl()) =&amp;gt; Eq.Refl()

//Free Cast
def cast[A, B](witness: Eq[A, B], value: A): B =
	witness match
		case Eq.Refl() =&amp;gt; value

//Injectivity
def injectivity[T[_], A, B](witness: Eq[A, B]): Eq[T[A], T[B]] =
	witness match
		case Eq.Refl() =&amp;gt; Eq.Refl()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3c057fdf-0c3a-4b2b-85f5-ee38a5ebd8d1--&gt;
&lt;!-- begin paragraph 0e9dc346-150a-43e0-88dc-de2848c77f43--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e9dc346-150a-43e0-88dc-de2848c77f43&quot;&gt;C’est le cas de l’injectivité qui prouve que le cas des égalités locales est correctement implémenté dans le langage, et cela n’était pas possible en Scala 2 en n’utilisant que Eq, cela fonctionne seulement depuis Scala 3. Le support de ce cas est la preuve qu’on a des GADT.&lt;/p&gt;
&lt;!-- end paragraph 0e9dc346-150a-43e0-88dc-de2848c77f43--&gt;
&lt;!-- begin paragraph 6d6e35d9-e49a-45e7-9fca-84eba6ac66ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d6e35d9-e49a-45e7-9fca-84eba6ac66ee&quot;&gt;Il nous présentera ensuite des exemples de types existentiels avec les GADT et un cas pour printer des URLs.&lt;/p&gt;
&lt;!-- end paragraph 6d6e35d9-e49a-45e7-9fca-84eba6ac66ee--&gt;
&lt;!-- begin paragraph 1b7bf4c6-3d06-4dc4-9997-84621d30f502--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1b7bf4c6-3d06-4dc4-9997-84621d30f502&quot;&gt;Présenté avec humour et enthousiasme avec quelques gentilles piques à Scala (typeclasses), cette Keynote nous met bien dans le bain, en effet nombreux talks de cette édition reprendront, de près ou de loin, de notions vues dans celle-ci.&lt;/p&gt;
&lt;!-- end paragraph 1b7bf4c6-3d06-4dc4-9997-84621d30f502--&gt;
&lt;!-- begin paragraph fa9fae73-87b0-431f-9afd-970663c7efe8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa9fae73-87b0-431f-9afd-970663c7efe8&quot;&gt;Pour en savoir plus, retrouvez le replay de cette keynote &lt;a href=&quot;https://youtu.be/r4c7xuVB9xA?si=R3LaeT7BK2R7sJHC&quot;&gt;ici&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph fa9fae73-87b0-431f-9afd-970663c7efe8--&gt;
&lt;!-- begin heading_2 a8aec0ca-35a6-479a-8f40-d378d67f5ef7--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-a8aec0ca-35a6-479a-8f40-d378d67f5ef7&quot;&gt;&lt;b&gt;Songwriting in Scala - &lt;/b&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Mr6TtLRU5LM&quot;&gt;&lt;b&gt;Creating a DSL for writing Music with ADT&amp;#39;s de Paul Matthews, la présentation Rock !&lt;/b&gt;&lt;/a&gt;&lt;b&gt; :&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 a8aec0ca-35a6-479a-8f40-d378d67f5ef7--&gt;
&lt;!-- begin paragraph c0d9d257-1a99-4608-be6b-6b1e79f5edbd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c0d9d257-1a99-4608-be6b-6b1e79f5edbd&quot;&gt;Pour rester dans le thème des ADT, nous avons eu une présentation très originale de Paul Matthews, dont le parcours est très atypique, en effet c’est une rockstar ! Bassiste du groupe &lt;a href=&quot;https://en.wikipedia.org/wiki/I_Am_Giant&quot;&gt;I Am Giant&lt;/a&gt;, auteur-compositeur et ingénieur du son, l’industrie changeante etc. il a du trouver un nouveau but, et c’est en obtenant son diplôme en informatique qu’il est devenu un ingénieur Scala.&lt;/p&gt;
&lt;!-- end paragraph c0d9d257-1a99-4608-be6b-6b1e79f5edbd--&gt;
&lt;!-- begin paragraph 809b76ed-01f0-4c7a-ba90-57c97dc5198e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-809b76ed-01f0-4c7a-ba90-57c97dc5198e&quot;&gt;C’est tout du moins naturel donc que Paul nous fasse une présentation sur le thème de la musique!&lt;br /&gt;
L’idée de la présentation est de partir d’ADT qui représente une portion de musique, et d’interpréter un morceau écrit avec ce dernier à l’aide d’un module Scala.js/Tone.js.&lt;/p&gt;
&lt;!-- end paragraph 809b76ed-01f0-4c7a-ba90-57c97dc5198e--&gt;
&lt;!-- begin paragraph ce5774f3-cda4-4137-a7e6-e059ee42eca6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce5774f3-cda4-4137-a7e6-e059ee42eca6&quot;&gt;C’est en apprenant le Scala, qu’il a d’abord écrit cet ADT&lt;/p&gt;
&lt;!-- end paragraph ce5774f3-cda4-4137-a7e6-e059ee42eca6--&gt;
&lt;!-- begin code cf4cd6db-8cb1-4992-b5f5-516b96ce357c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-cf4cd6db-8cb1-4992-b5f5-516b96ce357c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//code repris de la présentation
sealed trait MusicEvent
final case class Note(noteName: NoteName, duration: Duration) extends MusicEvent
final case class Melody(left: MusicEvent, right: MusicEvent)  extends MusicEvent
final case class Harmony(notes: Vector[Note])                 extends MusicEvent
final case class Rest(duration: Duration)                     extends MusicEvent&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code cf4cd6db-8cb1-4992-b5f5-516b96ce357c--&gt;
&lt;!-- begin paragraph 27be0778-1c45-4249-b331-303eade1a37a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27be0778-1c45-4249-b331-303eade1a37a&quot;&gt;Puis de fil en aiguille, il nous montre comment il améliore ses différents Product Type, il se rend compte qu’il peut mieux définir certaines classes, en rajouter, ou en y ajoutant plus de paramètres, etc., avec Note par exemple :&lt;/p&gt;
&lt;!-- end paragraph 27be0778-1c45-4249-b331-303eade1a37a--&gt;
&lt;!-- begin code 4541321a-b24a-40be-925c-b309bf7d7a1c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4541321a-b24a-40be-925c-b309bf7d7a1c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//code repris de la présentation
final case class Note(
	pitch: NoteName,
	accidental: Accidental = Natural,
	duration: Duration = Quarter(),
	velocity: Int,
	octave: Int = 3
) extends MusicEvent&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4541321a-b24a-40be-925c-b309bf7d7a1c--&gt;
&lt;!-- begin paragraph 938c53e8-820e-4881-9a08-469b0d02ef81--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-938c53e8-820e-4881-9a08-469b0d02ef81&quot;&gt;À propos de la velocity, il nous explique qu’il avait besoin de “cloisonner” un peu les différents niveau pour modéliser une &lt;a href=&quot;https://fr.wikipedia.org/wiki/Musical_Instrument_Digital_Interface#Notes_de_musique&quot;&gt;MIdi Velocity&lt;/a&gt;, en effet un Int peut être négatif ou très grand, mais que dans les plages standards il n’est possible que d’être entre 0 et 127, de plus Tone.js lui ne prend que des valeurs entre 0 et 1. La solution qu’il a trouvé est de créer un autre ADT Velocity:&lt;/p&gt;
&lt;!-- end paragraph 938c53e8-820e-4881-9a08-469b0d02ef81--&gt;
&lt;!-- begin code fabec54e-ae32-4a9e-b050-b7c97f49d9f3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fabec54e-ae32-4a9e-b050-b7c97f49d9f3&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//code repris de la présentation
enum Velocity(val midiVelocity: Int) {
	def getNormalisedVelocity: Double = {(1.0/127) * this.midiVelocity}
	}
case TheSilentTreatment   extends Velocity(0)
case Softest              extends Velocity(10)
case Sof                  extends Velocity(50)
case Medium               extends Velocity(80)
case Assertively          extends Velocity(100)
case Loud                 extends Velocity(115)
case OnFull               extends Velocity(127)
case Napalm               extends Velocity(13335)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fabec54e-ae32-4a9e-b050-b7c97f49d9f3--&gt;
&lt;!-- begin paragraph 61b87306-7397-4c6f-8263-88901dc352f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-61b87306-7397-4c6f-8263-88901dc352f0&quot;&gt;Il nous détaillera tout au long du talk chaque définition de classes qu’il a conçues pour représenter un élément musical.&lt;/p&gt;
&lt;!-- end paragraph 61b87306-7397-4c6f-8263-88901dc352f0--&gt;
&lt;!-- begin paragraph 96de7c1f-bca4-44b4-8bdb-8b8acf3229f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-96de7c1f-bca4-44b4-8bdb-8b8acf3229f0&quot;&gt;C’était vraiment une bonne présentation pour comprendre la construction ADT d’une manière beaucoup moins abstraite et concrète, avec le bonus d’entendre la musique codée en live et le tout avec beaucoup de passion.&lt;/p&gt;
&lt;!-- end paragraph 96de7c1f-bca4-44b4-8bdb-8b8acf3229f0--&gt;
&lt;!-- begin paragraph 5c051698-a830-471b-bee2-adf420508268--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5c051698-a830-471b-bee2-adf420508268&quot;&gt;Je vous invite à voir la présentation &lt;a href=&quot;https://www.youtube.com/watch?v=Mr6TtLRU5LM&amp;t=&quot;&gt;ici&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 5c051698-a830-471b-bee2-adf420508268--&gt;
&lt;!-- begin heading_2 c011c80a-65c6-4bfe-98d4-6291086ba6c5--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c011c80a-65c6-4bfe-98d4-6291086ba6c5&quot;&gt;&lt;a href=&quot;https://youtu.be/wmuVcCSsk-4?si=XSoB43y3cCbUDLb2&quot;&gt;&lt;b&gt;Scala 3 Compiler Academy Journey&lt;/b&gt;&lt;/a&gt;&lt;b&gt; de Anatolii Kmetiuk&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 c011c80a-65c6-4bfe-98d4-6291086ba6c5--&gt;
&lt;!-- begin paragraph 6fc1e493-4d9d-411c-b373-f8a386e4e5c2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6fc1e493-4d9d-411c-b373-f8a386e4e5c2&quot;&gt;À ScalaIO on a aussi des talks communautaires, comme celui d’Anatolii Kmetiuk qui travaille au sein du &lt;a href=&quot;https://scala.epfl.ch/&quot;&gt;Scala Center&lt;/a&gt; à l’&lt;a href=&quot;https://www.epfl.ch/fr/&quot;&gt;EPFL&lt;/a&gt;. Il nous fera un retour d’expérience sur la création du Scala 3 Compiler Academy.&lt;/p&gt;
&lt;!-- end paragraph 6fc1e493-4d9d-411c-b373-f8a386e4e5c2--&gt;
&lt;!-- begin paragraph 555e538c-2943-469c-a482-33340680df12--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-555e538c-2943-469c-a482-33340680df12&quot;&gt;Il nous dira que le plus important dans la création d’un logiciel ce sont les personnes, ce sont eux qui le créent, le maintiennent et qui le gardent à jour, c’est pourquoi ce projet d’académie est né avec l’aspect communautaire mis en avant.&lt;/p&gt;
&lt;!-- end paragraph 555e538c-2943-469c-a482-33340680df12--&gt;
&lt;!-- begin paragraph ad0261a0-aae4-4027-9919-0ae30bc6bbe6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ad0261a0-aae4-4027-9919-0ae30bc6bbe6&quot;&gt;Ce talk peut s’adresser et s’appliquer à n’importe quel projet Open Source qui souhaite développer son aspect communautaire pour in fine agrandir le nombre de mainteneurs.&lt;/p&gt;
&lt;!-- end paragraph ad0261a0-aae4-4027-9919-0ae30bc6bbe6--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Qu’est - ce que Scala 3 Compiler Academy ?&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 2a59a645-2925-4a60-922e-f3aea79f5e55--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2a59a645-2925-4a60-922e-f3aea79f5e55&quot;&gt;Il se compose de sessions de Pair programming en ligne avec les équipes principales de Scala 3, principalement sur les sujets de compilateur Scala 3 et notamment ceux visant à corriger le compilateur. Toute personne intéressée par ces sujets peut rejoindre l’équipe, à raison d’environ une session par mois.&lt;/p&gt;
&lt;!-- end paragraph 2a59a645-2925-4a60-922e-f3aea79f5e55--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Le parcours&lt;/b&gt;
&lt;!-- begin paragraph 2d8dfaca-4aa0-4d49-ba25-d4bdcac0c1c9--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-2d8dfaca-4aa0-4d49-ba25-d4bdcac0c1c9&quot;&gt;L’idée date depuis 2021, puis en 2022 était la phase d’expérimentation pour arriver à une stabilité en 2023.&lt;/p&gt;
&lt;!-- end paragraph 2d8dfaca-4aa0-4d49-ba25-d4bdcac0c1c9--&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;&lt;b&gt;2021 &lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-2&quot;&gt;&lt;li&gt;Contexte : COVID ⇒ travail en remote, rester en contact devient plus difficile, Scala 3.0.0 venait de sortir, donc beaucoup de charge sur l’équipe de maintenance (surtout que l’équipe qui s’en occupe est une équipe de recherche dont le but premier n’est pas le support).&lt;/li&gt;&lt;li&gt;Cela a d’abord commencé avec des sessions de 2 heures avec surtout du transfert de connaissance avec l’équipe principale le tout en &lt;b&gt;interne&lt;/b&gt;, pour être sûr que ce format fonctionne. Le tout en ligne dû au contexte sanitaire de l’époque, ce qui était nouveau pour eux.&lt;/li&gt;&lt;li&gt;À chaque session, ils collectaient des retours (ce qui marchait bien, et ce qui ne marchait pas bien) jusqu’à ne plus avoir de retours négatifs.&lt;/li&gt;&lt;li&gt;Fun fact : ils cherchaient des outils de conférence qui utilisent le moins de ressources CPU, en effet comme ces ressources étaient utilisés pour la conférence cela empêchait de compiler.&lt;/li&gt;&lt;li&gt;Focus sur le contenu des sessions : le temps, le découpage ⇒ la conclusion est le plus simple le mieux, aussi bien pour l’organisateur que pour les participants afin qu’ils comprennent tout de suite le déroulé.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f677d4ff-4ff1-489d-a0e3-e49943706fdc--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-f677d4ff-4ff1-489d-a0e3-e49943706fdc&quot;&gt;&lt;b&gt;Ouverture à l’extérieur&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph f677d4ff-4ff1-489d-a0e3-e49943706fdc--&gt;&lt;ul class=&quot;block-bulleted_list_item level-2&quot;&gt;&lt;li&gt;Communications via Twitter, puis après ajout de Mastodonte, Discord, Slack.&lt;/li&gt;&lt;li&gt;Il fallait s’adapter aux horaires mondiaux cette fois ci pour les sessions, les retours étaient toujours collectés.&lt;/li&gt;&lt;li&gt;La nouveauté c’est le “onboarding call” à la différence d’en interne, où il faut prendre le temps d’expliquer au volontaire externe ce qu’il faut faire et comment il peut participer et aussi s’il a besoin de guidage.&lt;/li&gt;&lt;li&gt;Un autre challenge qui est apparu, c’est l’équilibrage des équipes, certains sont plus experts dans un domaine du compilateur plutôt qu’un autre. Ils font cela via un formulaire Google Form où chacun choisit l’issue sur lequel il veut travailler, et aussi une option sur une autre issue si jamais ils ne peuvent pas être dispatché dans tel équipe.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;2022&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-2&quot;&gt;&lt;li&gt;L’idée de cette année était de monter en volume, aussi bien en nombre de fix à résoudre qu’en nombre de participants.&lt;/li&gt;&lt;li&gt;Forcément un des problèmes était le temps des mentors.&lt;/li&gt;&lt;li&gt;Une chaîne &lt;a href=&quot;https://www.youtube.com/@scala3compileracademy&quot;&gt;YouTube&lt;/a&gt; a été ouverte aussi pour permettre aux personnes d’apprendre.&lt;/li&gt;&lt;li&gt;Un template a été créé avec une checklist pour chaque organisation de spree, pour faciliter la création de ceux-ci et libérer de la charge à l’organisateur.&lt;br /&gt;
Ils ont automatisé tout cela via des outils NoCode.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;2023 et aujourd’hui&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-2&quot;&gt;&lt;li&gt;Il y a une stabilité de participants, plus de 80 participants uniques ont participé au projet depuis sa naissance en 2021.&lt;/li&gt;&lt;li&gt;Aujourd’hui c’est un format de 3 semaines pour chaque spree&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f62dbd29-0b6a-4adb-8923-dea6d7de6393--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f62dbd29-0b6a-4adb-8923-dea6d7de6393&quot;&gt;Cela était très intéressant de voir comment s’organise le développement autour d’un projet Open Source comme le compilateur Scala, on ne se rend pas forcément compte des difficultés qui peuvent subvenir pour le bon fonctionnement d’un projet comme celui-ci.&lt;/p&gt;
&lt;!-- end paragraph f62dbd29-0b6a-4adb-8923-dea6d7de6393--&gt;
&lt;!-- begin paragraph 972c7ba9-361a-479b-8b5a-1f02e87c4d48--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-972c7ba9-361a-479b-8b5a-1f02e87c4d48&quot;&gt;Plus de détails dans la vidéo du talk &lt;a href=&quot;https://youtu.be/wmuVcCSsk-4?si=XSoB43y3cCbUDLb2&quot;&gt;ici&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 972c7ba9-361a-479b-8b5a-1f02e87c4d48--&gt;
&lt;!-- begin heading_2 fdeb4b31-a721-49b4-970d-dd9b87388030--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-fdeb4b31-a721-49b4-970d-dd9b87388030&quot;&gt;Et plein d’autres présentations !&lt;/h3&gt;
&lt;!-- end heading_2 fdeb4b31-a721-49b4-970d-dd9b87388030--&gt;
&lt;!-- begin paragraph 87d87571-496b-4e0b-b670-b77252dc7d6e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-87d87571-496b-4e0b-b670-b77252dc7d6e&quot;&gt;Bien sûr, ce n’était qu’un bref aperçu de quelques présentations de ce ScalaIO 2024, vous pouvez retrouver tous les talks sur la &lt;a href=&quot;https://youtube.com/@scalaio?si=DBtSyavVT5K_X5xXX5xX&quot;&gt;chaîne YouTube&lt;/a&gt; de ScalaIO.&lt;/p&gt;
&lt;!-- end paragraph 87d87571-496b-4e0b-b670-b77252dc7d6e--&gt;
&lt;!-- begin heading_1 34729794-5600-413f-aef8-29e99ec2fa60--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-34729794-5600-413f-aef8-29e99ec2fa60&quot;&gt;Le reste, comment c’était ?&lt;/h2&gt;
&lt;!-- end heading_1 34729794-5600-413f-aef8-29e99ec2fa60--&gt;
&lt;!-- begin paragraph 38d5688a-58b6-4835-bd5a-d3d7bb7d1b9f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38d5688a-58b6-4835-bd5a-d3d7bb7d1b9f&quot;&gt;Pour cette édition, une seule salle de conférence, et un endroit pour le partage et/ou la pause. De taille plus modeste pour cette itération, la salle était quand même bien remplie surtout que parallèlement se tenait une grève nationale de transport affectant beaucoup la venue de participants, cela était très plaisant de voir toujours autant de monde intéressé par cette conférence, et de manière générale le Scala.&lt;/p&gt;
&lt;!-- end paragraph 38d5688a-58b6-4835-bd5a-d3d7bb7d1b9f--&gt;
&lt;!-- begin paragraph 75ce2920-bdae-45c5-95a3-d018d84a1afc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75ce2920-bdae-45c5-95a3-d018d84a1afc&quot;&gt;D’autres aspects qui peuvent vous être intéressants :&lt;/p&gt;
&lt;!-- end paragraph 75ce2920-bdae-45c5-95a3-d018d84a1afc--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Enchaînement des présentations très fluide, pas de retard, et je n’ai pas vu de problèmes techniques.&lt;/li&gt;&lt;li&gt;Le lieu était vraiment très bien, bien placé dans le centre-ville de Nantes, des logements se trouvaient à proximité, donc pas besoin de transports pour y arriver.&lt;/li&gt;&lt;li&gt;Les repas étaient vraiment au top pour les midis ainsi que le traiteur pour la Community Party du premier soir qui se déroulait dans les même locaux.&lt;/li&gt;&lt;li&gt;Ambiance générale toujours plaisante, tout le monde est accessible et prêt à échanger.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/761a510f-380e-4f63-a491-bd5b87ccebb8/untitled.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph f13c1fe8-b10d-4efd-ba11-e4fe935fe8f8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f13c1fe8-b10d-4efd-ba11-e4fe935fe8f8&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph f13c1fe8-b10d-4efd-ba11-e4fe935fe8f8--&gt;
&lt;!-- begin paragraph 2f1e533c-87e3-467b-bbe6-8b94535f45aa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2f1e533c-87e3-467b-bbe6-8b94535f45aa&quot;&gt;Je remercie beaucoup les organisateurs pour cette édition du ScalaIO, toujours de qualité, et on attend la prochaine session avec impatience !&lt;/p&gt;
&lt;!-- end paragraph 2f1e533c-87e3-467b-bbe6-8b94535f45aa--&gt;
&lt;!-- begin paragraph a942d75d-88fc-4a26-a6a0-d9163a735044--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a942d75d-88fc-4a26-a6a0-d9163a735044&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a942d75d-88fc-4a26-a6a0-d9163a735044--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:7231a950b27845f2839814a291109ce8</id>
    <title>Double jointures dans Kafka Streams</title>
    <updated>2024-01-30T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/double-jointures-dans-kafka-streams.html"/>
    <!--summary Comment gérer des flux réactifs dans Kafka Streams-->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>        <category term="Scala"></category>    <category term="Kafka"></category>    <category term="Kafka Streams"></category>    <content type="html">
&lt;!-- begin heading_1 e15718d0-2423-42df-8d2e-bb0a250ad941--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e15718d0-2423-42df-8d2e-bb0a250ad941&quot;&gt;Intention&lt;/h2&gt;
&lt;!-- end heading_1 e15718d0-2423-42df-8d2e-bb0a250ad941--&gt;
&lt;!-- begin paragraph 1f8c40e8-98f3-4a40-8c03-b46ac663761b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f8c40e8-98f3-4a40-8c03-b46ac663761b&quot;&gt;Imaginez que vous disposez de deux flux : l&amp;#39;un représente le stock des produits d’un magasin, et l&amp;#39;autre concerne les commandes des clients. Vous souhaitez combiner ces deux flux afin de créer une projection précise du niveau de stock pour chaque produit. Les scénarios sont les suivants :&lt;/p&gt;
&lt;!-- end paragraph 1f8c40e8-98f3-4a40-8c03-b46ac663761b--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Lorsqu&amp;#39;une commande client est passée, elle doit réduire le niveau de stock correspondant. Par conséquent, il est nécessaire que l&amp;#39;arrivée d&amp;#39;une commande déclenche un calcul et une mise à jour de la projection.&lt;/li&gt;&lt;li&gt;Lorsque le stock des produits dans le magasin est actualisé, il doit également mettre à jour le &lt;b&gt;dernier&lt;/b&gt; stock de base de chaque produit, déclenchant ainsi un nouveau calcul et une nouvelle projection.&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph 71ac3014-bb5f-4eb8-a95e-d53bb04661d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71ac3014-bb5f-4eb8-a95e-d53bb04661d7&quot;&gt;En d’autre termes, nous avons besoin de faire une &lt;b&gt;double jointure &lt;/b&gt;entre le flux des stocks et le flux des commandes et tout cela en &lt;b&gt;temps réel&lt;/b&gt;. Comment faire cela dans Kafka Streams ?&lt;/p&gt;
&lt;!-- end paragraph 71ac3014-bb5f-4eb8-a95e-d53bb04661d7--&gt;
&lt;!-- begin heading_1 027754a4-472a-448d-99a3-cbcf10bdbe70--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-027754a4-472a-448d-99a3-cbcf10bdbe70&quot;&gt;Jointure KTable-KTable&lt;/h2&gt;
&lt;!-- end heading_1 027754a4-472a-448d-99a3-cbcf10bdbe70--&gt;
&lt;!-- begin paragraph 9414fbf3-789b-4905-ac90-a12625338537--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9414fbf3-789b-4905-ac90-a12625338537&quot;&gt;Kafka Streams est un outil pour traiter des données en temps réel. Pour effectuer une jointure en temps réel entre deux flux de données (KStream), on peut faire :&lt;/p&gt;
&lt;!-- end paragraph 9414fbf3-789b-4905-ac90-a12625338537--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Jointure KStream-KStream :&lt;/b&gt; On conserve les deux flux de données KStream et on utilise une fenêtre (windowing) pour associer tous les éléments correspondants dans une période de temps spécifique.
&lt;!-- begin paragraph 7988adb4-3e7b-47d0-9c48-339e8fa89152--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-7988adb4-3e7b-47d0-9c48-339e8fa89152&quot;&gt;Dans ce cas, on a bien le flux de commandes qui se joint avec celui des stocks et vice-versa, mais seulement pour ceux arrivant &lt;b&gt;dans la même fenêtre&lt;/b&gt;. Il faudrait une fenêtre de temps énorme pour récupérer tout les changements, ce qui fait que l’on ne serait plus en temps réel.&lt;/p&gt;
&lt;!-- end paragraph 7988adb4-3e7b-47d0-9c48-339e8fa89152--&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Jointure KStream-KTable :&lt;/b&gt; On transforme l&amp;#39;un des flux en une KTable pour garder les changements d&amp;#39;état, puis on associe chaque élément du flux KStream avec chaque élément de la table KTable.
&lt;!-- begin paragraph b54eec14-1f43-49e9-9176-3b466abb4506--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-b54eec14-1f43-49e9-9176-3b466abb4506&quot;&gt;Dans ce cas, les éléments du flux de KStream (stock ou commande) viendrait se joindre avec la KTable, ce qui produit bien une projection. Mais les éléments de la KTable ne se joindrait pas avec ceux de la KStream, ce qui ne produit pas une projection.&lt;/p&gt;
&lt;!-- end paragraph b54eec14-1f43-49e9-9176-3b466abb4506--&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Jointure KTable-KTable&lt;/b&gt; : On transforme les deux flux en deux Ktable, ce qui nous permet de garder les changement d’état des deux sources, et nous permet de joindre chaque élément comme en SQL.&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph 86e7aca3-d617-4506-a8cd-6e9474b4d800--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86e7aca3-d617-4506-a8cd-6e9474b4d800&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 86e7aca3-d617-4506-a8cd-6e9474b4d800--&gt;
&lt;!-- begin paragraph 4a12298b-9833-4a09-9ccd-4c02ae2459e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a12298b-9833-4a09-9ccd-4c02ae2459e1&quot;&gt;Au vu des descriptions, cela ressemble plus à une jointure KTable-KTable qu’il faudrait faire ! Faisons donc cela.&lt;/p&gt;
&lt;!-- end paragraph 4a12298b-9833-4a09-9ccd-4c02ae2459e1--&gt;
&lt;!-- begin paragraph eeeed61a-5978-4acb-8acc-4b15c478fd55--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eeeed61a-5978-4acb-8acc-4b15c478fd55&quot;&gt;Voici le modèle de données des stocks et des commandes (orders ici) :&lt;/p&gt;
&lt;!-- end paragraph eeeed61a-5978-4acb-8acc-4b15c478fd55--&gt;
&lt;!-- begin code a7569918-25f9-4940-bed3-6cf705c409a7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a7569918-25f9-4940-bed3-6cf705c409a7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt; case class Stock(
    store: String,
    product: String,
    checkedAt: Instant,
    quantity: Double
)

case class Order(
    orderId: String,
    store: String,
    createdAt: Instant,
    items: List[OrderItem],
    deliverAt: Instant,
    status: String
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a7569918-25f9-4940-bed3-6cf705c409a7--&gt;
&lt;!-- begin paragraph 93b07eae-c18c-48a0-8ebe-b1978c05dff3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-93b07eae-c18c-48a0-8ebe-b1978c05dff3&quot;&gt;Et voici un exemple de construction de la topologie dans Kafka Streams :&lt;/p&gt;
&lt;!-- end paragraph 93b07eae-c18c-48a0-8ebe-b1978c05dff3--&gt;
&lt;!-- begin code 81bfb6cd-4db4-4b34-bfa6-257019304017--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-81bfb6cd-4db4-4b34-bfa6-257019304017&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;/* Create the stock KStream from the stock topic.
 */
val stocks: KStream[Key, Stock] =
  builder.stream(StockTopic)(
    Consumed.`with`(Key.keySerde, Stock.valueSerde)
  )

/* Create a KStream for orders the same way.
 */
val orders: KStream[Key, Order] =
  builder.stream(OrderTopic)(
    Consumed.`with`(Key.keySerde, Order.valueSerde)
  )

val orderTable: KTable[Key, Order] =
  orders
    .groupByKey(Grouped.`with`(Key.keySerde, Order.valueSerde))
    .reduce((order1, order2) =&amp;gt; mergeOrders(order1, order2))(
      Materialized.as(&amp;quot;order-table&amp;quot;)(Key.keySerde, Order.valueSerde)
    )

val stockTable: KTable[Key, Stock] =
  stocks
    .groupByKey(Grouped.`with`(Key.keySerde, Stock.valueSerde))
    .reduce((stock1, stock2) =&amp;gt; moreRecentOf(stock1, stock2))(
      Materialized.as(&amp;quot;stock-table&amp;quot;)(Key.keySerde, Stock.valueSerde)
    )

val projections: KStream[Key, Projection] =
  stockTable
    .leftJoin(orderTable)((stock, order) =&amp;gt;
      project(Option(stock), Option(order))
    )
    .toStream
    .flatMapValues(_.toList)


projections.to(ProjectionTopic)(
  Produced.`with`(Key.keySerde, Projection.valueSerde)
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 81bfb6cd-4db4-4b34-bfa6-257019304017--&gt;
&lt;!-- begin paragraph 5137225c-0e99-4353-8c85-bcb4cba57ee8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5137225c-0e99-4353-8c85-bcb4cba57ee8&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5137225c-0e99-4353-8c85-bcb4cba57ee8--&gt;
&lt;!-- begin paragraph d304aaee-710a-4689-b6e0-8234e4d23961--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d304aaee-710a-4689-b6e0-8234e4d23961&quot;&gt;Vous trouverez le code complet ici :&lt;/p&gt;
&lt;!-- end paragraph d304aaee-710a-4689-b6e0-8234e4d23961--&gt;
&lt;!-- begin paragraph 23191a4b-2878-4ef7-8e4a-11a09933cf54--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-23191a4b-2878-4ef7-8e4a-11a09933cf54&quot;&gt;&lt;a href=&quot;https://github.com/univalence/double_jointures_ks/blob/main/src/main/scala/io/univalence/double_jointures_ks/KTableJoinMain.scala&quot;&gt;https://github.com/univalence/double_jointures_ks/blob/main/src/main/scala/io/univalence/double_jointures_ks/KTableJoinMain.scala&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 23191a4b-2878-4ef7-8e4a-11a09933cf54--&gt;
&lt;!-- begin heading_1 b7e84446-0f34-486c-beda-7bfd527aa89f--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-b7e84446-0f34-486c-beda-7bfd527aa89f&quot;&gt;Pouvons nous faire mieux ?&lt;/h2&gt;
&lt;!-- end heading_1 b7e84446-0f34-486c-beda-7bfd527aa89f--&gt;
&lt;!-- begin paragraph 016dd94c-63f9-4f90-a25e-2ffe232f79d9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-016dd94c-63f9-4f90-a25e-2ffe232f79d9&quot;&gt;Une autre manière de faire cette jointure est de passer directement par les &lt;b&gt;states stores&lt;/b&gt;. Cette méthode consiste à utiliser l’API de bas niveau de Kafka Streams afin de faire une &lt;b&gt;double jointure partielle&lt;/b&gt; à l’aide des méthodes &lt;code&gt;.get&lt;/code&gt; et &lt;code&gt;.put&lt;/code&gt; des state stores. Cela nous permet de ne pas devoir transformer les données de KStream en KTable, et donc de faire &lt;b&gt;une jointure KStream sans fenêtrage &lt;/b&gt;qui répond à nos attentes.&lt;/p&gt;
&lt;!-- end paragraph 016dd94c-63f9-4f90-a25e-2ffe232f79d9--&gt;
&lt;!-- begin paragraph 4bd9a6a8-2d02-4c5b-9fce-defb6594aa78--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4bd9a6a8-2d02-4c5b-9fce-defb6594aa78&quot;&gt;Le code se trouve ici avec l’utilisation de la case class &lt;code&gt;ExtraStreamBuilder&lt;/code&gt; : &lt;/p&gt;
&lt;!-- end paragraph 4bd9a6a8-2d02-4c5b-9fce-defb6594aa78--&gt;
&lt;!-- begin paragraph 0939578f-7f6d-4567-a180-09a9acb84ecb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0939578f-7f6d-4567-a180-09a9acb84ecb&quot;&gt;&lt;a href=&quot;https://github.com/univalence/double_jointures_ks/blob/main/src/main/scala/io/univalence/double_jointures_ks/DoublePartialJoinMain.scala&quot;&gt;https://github.com/univalence/double_jointures_ks/blob/main/src/main/scala/io/univalence/double_jointures_ks/DoublePartialJoinMain.scala&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 0939578f-7f6d-4567-a180-09a9acb84ecb--&gt;
&lt;!-- begin paragraph 56af5ceb-3d28-4474-86c7-a3d91b3f4295--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-56af5ceb-3d28-4474-86c7-a3d91b3f4295&quot;&gt;En faisant une visualisation de la topologie pour chaque code, nous voyons bien la simplification (à gauche jointure KTable-KTable, à droite double jointure partielle)&lt;/p&gt;
&lt;!-- end paragraph 56af5ceb-3d28-4474-86c7-a3d91b3f4295--&gt;
&lt;!-- begin paragraph c1f638f6-8930-4c26-a2f5-3b559bd010ca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c1f638f6-8930-4c26-a2f5-3b559bd010ca&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c1f638f6-8930-4c26-a2f5-3b559bd010ca--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/topologie_ktable_join.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/topologie_double_partial_join.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph 3cd6bdb5-347f-4b0b-b976-654493e426f8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3cd6bdb5-347f-4b0b-b976-654493e426f8&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 3cd6bdb5-347f-4b0b-b976-654493e426f8--&gt;
&lt;!-- begin heading_1 094a83ca-7709-4f46-b724-61a8f1f42900--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-094a83ca-7709-4f46-b724-61a8f1f42900&quot;&gt;Encore mieux ??&lt;/h2&gt;
&lt;!-- end heading_1 094a83ca-7709-4f46-b724-61a8f1f42900--&gt;
&lt;!-- begin paragraph 70cbaf8c-1dbb-49fb-9b8c-202fa7827cf1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-70cbaf8c-1dbb-49fb-9b8c-202fa7827cf1&quot;&gt;En allant un peu plus loin, pour optimiser cette double jointure on peut aussi… supprimer la notion de jointure des deux flux. Non ce n’est pas un clickbait ! Cette méthode consiste à prendre les deux flux et à les fusionner dans un seul schéma dès le début. Cela semble bizarre dit comme ça, mais voyons un exemple pour mieux comprendre.&lt;/p&gt;
&lt;!-- end paragraph 70cbaf8c-1dbb-49fb-9b8c-202fa7827cf1--&gt;
&lt;!-- begin paragraph 3d72502a-2bd9-4961-919c-6cb115af2533--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d72502a-2bd9-4961-919c-6cb115af2533&quot;&gt;Pour rappel nous avons les deux flux de stocks et de commandes :&lt;/p&gt;
&lt;!-- end paragraph 3d72502a-2bd9-4961-919c-6cb115af2533--&gt;
&lt;!-- begin code 6d367ccb-a104-416f-853d-86bb6cbb5d23--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6d367ccb-a104-416f-853d-86bb6cbb5d23&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Stock(
    product: String,
    checkedAt: Instant,
    quantity: Double
)

case class Order(
    product: String,
    quantity: Double
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6d367ccb-a104-416f-853d-86bb6cbb5d23--&gt;
&lt;!-- begin paragraph a2b4be57-79f6-47f1-ba03-a11c42ed99c7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a2b4be57-79f6-47f1-ba03-a11c42ed99c7&quot;&gt;Nous fusionnons ces deux schémas en un seul en utilisant les mettant en Option tous les deux :&lt;/p&gt;
&lt;!-- end paragraph a2b4be57-79f6-47f1-ba03-a11c42ed99c7--&gt;
&lt;!-- begin code 9ea55d19-3264-411b-b2b8-e7b4e0566340--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9ea55d19-3264-411b-b2b8-e7b4e0566340&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class StockAndOrder(
	  stock: Option[Stock],
	  order: Option[Order]
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9ea55d19-3264-411b-b2b8-e7b4e0566340--&gt;
&lt;!-- begin paragraph 225a3c42-79b3-4736-8ce1-340a1c696d8b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-225a3c42-79b3-4736-8ce1-340a1c696d8b&quot;&gt;Et voilà ! Nous avons un ADT qui représente toutes nos sources (ici il n’y en a que deux mais il pourrait y en avoir d’autres).&lt;/p&gt;
&lt;!-- end paragraph 225a3c42-79b3-4736-8ce1-340a1c696d8b--&gt;
&lt;!-- begin paragraph 8aa82d05-5b58-4a49-a321-46ff78f8390b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8aa82d05-5b58-4a49-a321-46ff78f8390b&quot;&gt;Nous transformons ensuite le stream des commandes avec le schéma StockAndOrder :&lt;/p&gt;
&lt;!-- end paragraph 8aa82d05-5b58-4a49-a321-46ff78f8390b--&gt;
&lt;!-- begin code 0ab6f61c-16e5-4572-8a23-5f812d9b6fc9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0ab6f61c-16e5-4572-8a23-5f812d9b6fc9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val orders: KStream[Key, Order] =
  builder.stream(OrderTopic)(
    Consumed.`with`(Key.keySerde, Order.valueSerde)
  )

val finalOrders = orders.mapValues(_.toStockAndOrder)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0ab6f61c-16e5-4572-8a23-5f812d9b6fc9--&gt;
&lt;!-- begin paragraph f5c0a04b-e491-4808-bf81-294b1079125e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f5c0a04b-e491-4808-bf81-294b1079125e&quot;&gt;Et pareil pour le stream des stocks :&lt;/p&gt;
&lt;!-- end paragraph f5c0a04b-e491-4808-bf81-294b1079125e--&gt;
&lt;!-- begin code bc9ca66c-aeed-4844-972d-5a7a90f8f000--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bc9ca66c-aeed-4844-972d-5a7a90f8f000&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val stocks: KStream[Key, Stock] =
  builder.stream(StockTopic)(
    Consumed.`with`(Key.keySerde, Stock.valueSerde)
  )

val finalStocks = stocks.mapValues(_.toStockAndOrder)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bc9ca66c-aeed-4844-972d-5a7a90f8f000--&gt;
&lt;!-- begin paragraph ae572b00-7c65-422e-82e3-784b2236d279--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ae572b00-7c65-422e-82e3-784b2236d279&quot;&gt;Nous fusionnons les deux streams avec un merge (car ils ont maintenant le même schéma)&lt;/p&gt;
&lt;!-- end paragraph ae572b00-7c65-422e-82e3-784b2236d279--&gt;
&lt;!-- begin code a5610b5e-250b-4fef-afc9-50057858327c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a5610b5e-250b-4fef-afc9-50057858327c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val mergedStream = finalOrders.merge(finalStocks)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a5610b5e-250b-4fef-afc9-50057858327c--&gt;
&lt;!-- begin paragraph 7f070660-543c-444a-9f1a-b7197498474a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7f070660-543c-444a-9f1a-b7197498474a&quot;&gt;Puis nous aggrégeons le tout dans une KTable. Dans cette étape, nous faisons un groupByKey qui va permettre d’aggréger deux StockAndOrder. Selon qu’on ait l’information de stock ou l’information d’order dedans qui est définie, on peut avec un match décider quelle information va être apportée dans la KTable (&lt;code&gt;current.copy&lt;/code&gt;). Enfin en sortie, nous pouvons transformer StockAndOrder en Projection avec un &lt;code&gt;flatMapValues&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 7f070660-543c-444a-9f1a-b7197498474a--&gt;
&lt;!-- begin code 3223a6a6-d7fb-4081-8153-a0e415b60aec--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3223a6a6-d7fb-4081-8153-a0e415b60aec&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val projections =
  mergedStream
    .groupByKey(Grouped.`with`(Key.keySerde, StockAndOrder.valueSerde))
    .aggregate(StockAndOrder.empty){
      (_, newValue, current) =&amp;gt;
        (newValue, current) match {
          case (_, StockAndOrder.empty) =&amp;gt; newValue
          case (StockAndOrder(None, _), _) =&amp;gt; current.copy(order = newValue.order)
          case (StockAndOrder(_, None), _) =&amp;gt; current.copy(stock = newValue.stock)
        }
    }(
      Materialized.as(&amp;quot;stock-and-order-agg&amp;quot;)(Key.keySerde, StockAndOrder.valueSerde)
    )
    .toStream
    .flatMapValues { v =&amp;gt;
      project(v.stock, v.order) match {
        case Some(v) =&amp;gt; Seq(v)
        case None =&amp;gt; Seq.empty
      }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3223a6a6-d7fb-4081-8153-a0e415b60aec--&gt;
&lt;!-- begin paragraph 54e3b014-8595-4846-82c5-070eb658276e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-54e3b014-8595-4846-82c5-070eb658276e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 54e3b014-8595-4846-82c5-070eb658276e--&gt;
&lt;!-- begin paragraph 2826def2-a8f7-4da0-a43c-9869ebeb06c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2826def2-a8f7-4da0-a43c-9869ebeb06c3&quot;&gt;Le principal avantage avec cette méthode, c’est que nous évitons du shuffle puisque nous supprimons les jointures. Nous avons également une seule source en entrée avant l’aggrégation, nous pouvons donc remplacer le début de cette topologie plusieurs micro-services sources qui viendraient alimenter un unique topic. &lt;/p&gt;
&lt;!-- end paragraph 2826def2-a8f7-4da0-a43c-9869ebeb06c3--&gt;
&lt;!-- begin paragraph 7b1e7b46-a1d7-4411-99ba-905377519380--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7b1e7b46-a1d7-4411-99ba-905377519380&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7b1e7b46-a1d7-4411-99ba-905377519380--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/7231a950-b278-45f2-8398-14a291109ce8/topologie_adt_join.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 3dca7609-0e1f-4dd6-a829-1cd8f3587fc4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3dca7609-0e1f-4dd6-a829-1cd8f3587fc4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 3dca7609-0e1f-4dd6-a829-1cd8f3587fc4--&gt;
&lt;!-- begin paragraph cecbc973-6dde-45f5-9195-70a758eeacf3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cecbc973-6dde-45f5-9195-70a758eeacf3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph cecbc973-6dde-45f5-9195-70a758eeacf3--&gt;
&lt;!-- begin paragraph 177fbe3f-683c-46f2-aacd-cf2a96612369--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-177fbe3f-683c-46f2-aacd-cf2a96612369&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 177fbe3f-683c-46f2-aacd-cf2a96612369--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:07797286bb9441e9b72f67e347ab2186</id>
    <title>Scala côté frontend grâce à Laminar</title>
    <updated>2023-02-22T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/scala-cote-frontend-grace-a-laminar.html"/>
    <!--summary Scala se diversifie ! Aujourd’hui, nous allons voir comment l’utiliser côté Frontend avec Laminar.-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Scala"></category>    <category term="ScalaJS"></category>    <category term="Laminar"></category>    <content type="html">
&lt;!-- begin paragraph a1c425cf-ebab-45bc-955e-0217d473863b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a1c425cf-ebab-45bc-955e-0217d473863b&quot;&gt;Le langage Scala est généralement utilisé dans deux cas : la mise en place de serveurs backend (microservices ou monolithes) et la création de pipeline de données (Spark, Kafka Streams ou autre). Pour beaucoup, ses capacités s’arrêtent ici. &lt;/p&gt;
&lt;!-- end paragraph a1c425cf-ebab-45bc-955e-0217d473863b--&gt;
&lt;!-- begin paragraph 47ba483a-5426-49b4-83ca-aacaaae151ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-47ba483a-5426-49b4-83ca-aacaaae151ea&quot;&gt;Pourtant, ce n’est pas le cas. Ces dernières années, nous avons vu émerger dans la communauté deux game changers dans des domaines complétement différents :&lt;/p&gt;
&lt;!-- end paragraph 47ba483a-5426-49b4-83ca-aacaaae151ea--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/scala-native/scala-native&quot;&gt;Scala Native&lt;/a&gt; qui permet de développer des applications embarquées&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.scala-js.org/&quot;&gt;ScalaJS&lt;/a&gt; qui permet de développer des applications frontend&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 2772f684-8cbd-4862-9b79-77f10855f4a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2772f684-8cbd-4862-9b79-77f10855f4a0&quot;&gt;Je n’ai pas encore eu le temps de creuser Scala Native, alors je vous propose aujourd’hui que l&amp;#39;on aborde ScalaJS ensemble afin de vous montrer comment ce dernier peut vous être utile.&lt;/p&gt;
&lt;!-- end paragraph 2772f684-8cbd-4862-9b79-77f10855f4a0--&gt;
&lt;!-- begin heading_2 f4da0e04-1bef-4c7b-942d-d38474af3566--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f4da0e04-1bef-4c7b-942d-d38474af3566&quot;&gt;Pourquoi ScalaJS ?&lt;/h3&gt;
&lt;!-- end heading_2 f4da0e04-1bef-4c7b-942d-d38474af3566--&gt;
&lt;!-- begin paragraph 41aebe83-586b-4ae7-ba0c-2d54f46df5e2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-41aebe83-586b-4ae7-ba0c-2d54f46df5e2&quot;&gt;ScalaJS offre un avantage considérable comparé aux solutions existantes, son interopérabilité avec les autres librairies Scala. En effet, c’est selon moi l’élément à prendre en compte si l’on considère utiliser ScalaJS en production. Utiliser le même langage côté Frontend et Backend permet de ne pas se répéter et de rester DRY. En effet, généralement lorsque nous utilisons ScalaJS, nous avons trois projets dans notre repository:&lt;/p&gt;
&lt;!-- end paragraph 41aebe83-586b-4ae7-ba0c-2d54f46df5e2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;L’application Frontend&lt;/li&gt;&lt;li&gt;L’application Backend&lt;/li&gt;&lt;li&gt;La librairie partagée&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph a327f6d2-9053-4f75-9cbb-0b27aa536af8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a327f6d2-9053-4f75-9cbb-0b27aa536af8&quot;&gt;Les deux applications sont assez communes, ce qui nous intéresse ici, c&amp;#39;est la librairie partagée. Cette dernière a comme particularité d’abriter du code Scala qui va être utilisé par l’application backend en Scala et l’application frontend en ScalaJS. Généralement, nous entreposons dans cette librairie les choses suivantes :&lt;/p&gt;
&lt;!-- end paragraph a327f6d2-9053-4f75-9cbb-0b27aa536af8--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Notre domaine, à savoir les case class qui décrivent l’aspect métier qu’essaye de résoudre notre application. Généralement, ce sont des case class partagées entre le front et le back via une API REST.&lt;/li&gt;&lt;li&gt;Nos encoders et decoders, qui nous permettent de partager ce domaine entre nos deux applications.&lt;/li&gt;&lt;li&gt;Nos descriptions de routes, ces dernières ne sont pas obligatoires, mais je vous encourage fortement à les implémenter en utilisant &lt;a href=&quot;https://tapir.softwaremill.com/en/latest/&quot;&gt;Tapir&lt;/a&gt;, par exemple. Cela va permettre de réutiliser ces descriptions pour créer notre serveur côté Backend et notre client côté Frontend rapidement et facilement.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 31b5bd89-328e-48c7-b150-875efe9cf8e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-31b5bd89-328e-48c7-b150-875efe9cf8e1&quot;&gt;Comme je l’ai mentionné auparavant, rajouter cette librairie permet de ne pas se répéter dans les points mentionnés précédemment. Ce n’est pas le seul avantage, cette librairie permet de garder une version unifiée de notre système. Ainsi, si vous changez votre domaine en rajoutant un champ, par exemple, ou alors si vous rajoutez un &lt;i&gt;query parameter&lt;/i&gt; dans l’une de vos routes, c’est automatiquement répercuté côté back et front en même temps. &lt;/p&gt;
&lt;!-- end paragraph 31b5bd89-328e-48c7-b150-875efe9cf8e1--&gt;
&lt;!-- begin paragraph 570675d5-197e-432a-8f8e-f0f0b155ea1c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-570675d5-197e-432a-8f8e-f0f0b155ea1c&quot;&gt;On évite les erreurs humaines, on rend notre boucle de développement plus rapide et on utilise le même langage pour toute notre équipe. Voici où réside la puissance de ScalaJS. La question à se poser ensuite est la suivante : est-ce que l’écosystème ScalaJS est assez riche pour pouvoir mettre en place une application frontend ?&lt;/p&gt;
&lt;!-- end paragraph 570675d5-197e-432a-8f8e-f0f0b155ea1c--&gt;
&lt;!-- begin heading_1 a0873659-91e4-4205-ab7d-d9944c9f9461--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a0873659-91e4-4205-ab7d-d9944c9f9461&quot;&gt;L’écosystème de ScalaJS&lt;/h2&gt;
&lt;!-- end heading_1 a0873659-91e4-4205-ab7d-d9944c9f9461--&gt;
&lt;!-- begin paragraph 45b013d9-a033-4f26-96e1-d0f869fdd8cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-45b013d9-a033-4f26-96e1-d0f869fdd8cc&quot;&gt;Je vous ai parlé de la librairie partagée comme étant la solution idéale pour réunir le frontend et le backend, trop longtemps séparés. Il y a juste un seul problème que je n’ai pas mentionné auparavant. Pour que cela fonctionne, il faut que les librairies utilisées soient compatibles avec Scala et ScalaJS.&lt;/p&gt;
&lt;!-- end paragraph 45b013d9-a033-4f26-96e1-d0f869fdd8cc--&gt;
&lt;!-- begin paragraph 977f9b8b-90a5-474c-a9c4-9bf35b95623f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-977f9b8b-90a5-474c-a9c4-9bf35b95623f&quot;&gt;ScalaJS génère du code JavaScript. Il vous sera impossible d’utiliser une librairie Java dans le projet partagé du moment où ce même morceau de code doit être utilisé côté frontend et backend.  Ainsi, si vous utilisez &lt;a href=&quot;https://github.com/google/gson&quot;&gt;Gson&lt;/a&gt; pour la serialization / deserialization de vos JSON, cela n’est pas forcément adapté à la situation. Vous pouvez tout de même utiliser une grande partie du JDK puisque ce dernier a été &lt;a href=&quot;https://github.com/scala-js/scala-js/tree/main/javalib/src/main/scala/java&quot;&gt;partiellement réécrit en ScalaJS&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 977f9b8b-90a5-474c-a9c4-9bf35b95623f--&gt;
&lt;!-- begin paragraph 3204af21-434c-4e90-9eda-bbdeb354ed20--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3204af21-434c-4e90-9eda-bbdeb354ed20&quot;&gt;Fort heureusement pour nous, dans le cas des librairies Scala, pléthore de librairies diverses et variées supportent Scala ET ScalaJS, pour ne pas les citer : &lt;a href=&quot;https://zio.dev/&quot;&gt;ZIO&lt;/a&gt;, &lt;a href=&quot;https://tapir.softwaremill.com/en/latest/&quot;&gt;Tapir&lt;/a&gt;, &lt;a href=&quot;https://github.com/circe/circe&quot;&gt;Circe&lt;/a&gt;, par exemple. Pour une liste un peu plus exhaustive, je vous conseille ce lien : &lt;a href=&quot;https://www.scala-js.org/libraries/libs.html&quot;&gt;https://www.scala-js.org/libraries/libs.html&lt;/a&gt;. &lt;/p&gt;
&lt;!-- end paragraph 3204af21-434c-4e90-9eda-bbdeb354ed20--&gt;
&lt;!-- begin paragraph d5515f68-0520-4f5d-98de-85b1996e91b9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d5515f68-0520-4f5d-98de-85b1996e91b9&quot;&gt;Pour ce qui est du projet Frontend fait en ScalaJS, ce dernier supporte aussi tout l’écosystème JavaScript. Pour cela, il faut utiliser les façades. Pour ceux qui sont familiers avec TypeScript, le principe est le même. Comme JavaScript est typé dynamiquement, cette étape consiste à rajouter des types aux différentes fonctions de ladite librairie. Vous pouvez &lt;a href=&quot;https://www.scala-js.org/doc/interoperability/facade-types.html&quot;&gt;créer vos propres faces simplement&lt;/a&gt;, &lt;a href=&quot;https://www.scala-js.org/libraries/facades.html&quot;&gt;utiliser ceux déjà écrits par la communauté&lt;/a&gt; ou alors &lt;a href=&quot;https://scalablytyped.org/docs/readme&quot;&gt;utiliser directement les types defintions typescript&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph d5515f68-0520-4f5d-98de-85b1996e91b9--&gt;
&lt;!-- begin paragraph a6ef4d17-b2a0-455b-be2d-cfd8e7fa9caa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a6ef4d17-b2a0-455b-be2d-cfd8e7fa9caa&quot;&gt;Pour saupoudrer le tout, la communauté a aussi créé des librairies exclusivement destinées à ScalaJS. C’est le cas de &lt;a href=&quot;https://laminar.dev/&quot;&gt;Laminar&lt;/a&gt;, une librairie frontend fait pour Scala que je trouve prometteur.&lt;/p&gt;
&lt;!-- end paragraph a6ef4d17-b2a0-455b-be2d-cfd8e7fa9caa--&gt;
&lt;!-- begin heading_1 fb2040fd-46cc-46a2-8af0-f712638bb586--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-fb2040fd-46cc-46a2-8af0-f712638bb586&quot;&gt;Créer votre site internet avec Laminar&lt;/h2&gt;
&lt;!-- end heading_1 fb2040fd-46cc-46a2-8af0-f712638bb586--&gt;
&lt;!-- begin paragraph 69245dda-69d6-4735-bc3d-a5c70f4e0825--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69245dda-69d6-4735-bc3d-a5c70f4e0825&quot;&gt;Laminar est une librairie pour créer des applications Web, au même titre que React ou encore Angular. Outre le fait que cette librairie a été faite pour ScalaJS, je la trouve particulièrement plaisante à utiliser. Elle se base sur un principe peu commun en programmation, le reactive programming. &lt;/p&gt;
&lt;!-- end paragraph 69245dda-69d6-4735-bc3d-a5c70f4e0825--&gt;
&lt;!-- begin paragraph 186d0099-d762-404e-b3dd-9f07de5ff048--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-186d0099-d762-404e-b3dd-9f07de5ff048&quot;&gt;Sa syntaxe est assez naturelle et ne devrait pas trop dépayser les personnes développant des applications frontend. Voici un exemple :&lt;/p&gt;
&lt;!-- end paragraph 186d0099-d762-404e-b3dd-9f07de5ff048--&gt;
&lt;!-- begin code 7b32f750-a05c-468e-bda1-a20b2a29dc74--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7b32f750-a05c-468e-bda1-a20b2a29dc74&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import com.raquo.laminar.api.L.{*, given}
import org.scalajs.dom

object Main {
	val app = div(
		h1(&amp;quot;Hello World&amp;quot;, className := &amp;quot;title&amp;quot;)
		className := &amp;quot;body&amp;quot;
	)

	renderOnDomContentLoaded(dom.document.querySelector(&amp;quot;#app&amp;quot;), app)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7b32f750-a05c-468e-bda1-a20b2a29dc74--&gt;
&lt;!-- begin paragraph 15adf2bc-26de-4224-a75e-9248c52082ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-15adf2bc-26de-4224-a75e-9248c52082ad&quot;&gt;Là où l’application se démarque, c’est sur son côté réactif. En effet, Laminar se base sur une autre librairie écrite par le même auteur (raquo) nommé &lt;a href=&quot;https://github.com/raquo/Airstream&quot;&gt;Airstream&lt;/a&gt;. Le principe est le suivant : l’état de votre application est contenu dans des streams. Nos différents éléments Html peuvent alors mettre à jour ces streams ou alors récupérer l’état de ces streams pour automatiquement s&amp;#39;actualiser.&lt;/p&gt;
&lt;!-- end paragraph 15adf2bc-26de-4224-a75e-9248c52082ad--&gt;
&lt;!-- begin heading_2 0922caf2-2d91-43b5-b4c3-49bbaa5575c5--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-0922caf2-2d91-43b5-b4c3-49bbaa5575c5&quot;&gt;Implémentation d’un générateur de nombre aléatoire&lt;/h3&gt;
&lt;!-- end heading_2 0922caf2-2d91-43b5-b4c3-49bbaa5575c5--&gt;
&lt;!-- begin paragraph 55340d37-7054-46b3-989b-00d1d7e42a2b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-55340d37-7054-46b3-989b-00d1d7e42a2b&quot;&gt;Imaginons que nous voulons créer une application qui tire un nombre aléatoire et que nous voulons l’afficher. L’état de l’application est ce nombre aléatoire. Comme nous voulons à la fois le mettre à jour et récupérer son état, nous devons créer un &lt;a href=&quot;https://github.com/raquo/Airstream#var&quot;&gt;Var&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 55340d37-7054-46b3-989b-00d1d7e42a2b--&gt;
&lt;!-- begin code fb5b6830-a91e-4896-a1e5-8336957e4d4e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fb5b6830-a91e-4896-a1e5-8336957e4d4e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val state: Var[Int] = Var(0)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fb5b6830-a91e-4896-a1e5-8336957e4d4e--&gt;
&lt;!-- begin paragraph 082b29ba-5857-4f8c-89b4-9a544d28189e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-082b29ba-5857-4f8c-89b4-9a544d28189e&quot;&gt;Nous allons maintenant créer le composant qui va s’abonner à cette valeur :&lt;/p&gt;
&lt;!-- end paragraph 082b29ba-5857-4f8c-89b4-9a544d28189e--&gt;
&lt;!-- begin code 17e99fbe-f296-47aa-a8b1-1da347d6ec34--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-17e99fbe-f296-47aa-a8b1-1da347d6ec34&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val display: Div = div(
	child &amp;lt;-- state.signal.map(span(_)),
  className := &amp;quot;display&amp;quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 17e99fbe-f296-47aa-a8b1-1da347d6ec34--&gt;
&lt;!-- begin paragraph 6c9caf08-c577-4ac3-b7b4-3b754915866d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6c9caf08-c577-4ac3-b7b4-3b754915866d&quot;&gt;Ce dernier est un div qui va contenir un span contenant la valeur du Var à l’intérieur. À tout moment, si la valeur du Var change, alors le span sera de nouveau généré. L’opérateur &lt;code&gt;←-&lt;/code&gt; permet ici de consommer le signal.&lt;/p&gt;
&lt;!-- end paragraph 6c9caf08-c577-4ac3-b7b4-3b754915866d--&gt;
&lt;!-- begin paragraph 2ddbee88-da5c-4342-901b-4447146dda77--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2ddbee88-da5c-4342-901b-4447146dda77&quot;&gt;Ce nombre ne risque pas de se générer tout seul ! Alors, il faut aussi un composant pour mettre à jour cette valeur :&lt;/p&gt;
&lt;!-- end paragraph 2ddbee88-da5c-4342-901b-4447146dda77--&gt;
&lt;!-- begin code c40593ba-0e11-41e8-bf0b-5291ceddb978--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c40593ba-0e11-41e8-bf0b-5291ceddb978&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val generator: Button = button(
  &amp;quot;Generate a new number&amp;quot;,
  onClick.map { _ =&amp;gt; Random.nextInt() } --&amp;gt; state,
  className := &amp;quot;generator&amp;quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c40593ba-0e11-41e8-bf0b-5291ceddb978--&gt;
&lt;!-- begin paragraph da2638b7-8987-4d89-8800-43b8bf2ad4dd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-da2638b7-8987-4d89-8800-43b8bf2ad4dd&quot;&gt;Ce qui nous intéresse ici, c&amp;#39;est ce qui se passe lorsqu’on clique sur ce bouton. &lt;code&gt;onClick&lt;/code&gt; renvoie l’événement de la souris, ce dernier ne nous intéresse pas, donc on map cette valeur pour, à la place, générer un nombre aléatoire. Tout comme &lt;code&gt;&amp;lt;--&lt;/code&gt; permet de consommer une valeur, &lt;code&gt;—-&amp;gt;&lt;/code&gt; permet de produire une valeur, en l’occurrence, le nombre aléatoire.&lt;/p&gt;
&lt;!-- end paragraph da2638b7-8987-4d89-8800-43b8bf2ad4dd--&gt;
&lt;!-- begin paragraph 760665eb-4c84-499e-90a3-7c61663e6472--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-760665eb-4c84-499e-90a3-7c61663e6472&quot;&gt;Et c’est tout ! Les deux composants communiquent à présent ensemble. L’un actualise l’état de l’application. L’autre le consomme et se met à jour lorsque l’état change. Il faut un certain temps d’adaptation pour manipuler les streams avec aisance, mais croyez-moi, cela en vaut la chandelle.&lt;/p&gt;
&lt;!-- end paragraph 760665eb-4c84-499e-90a3-7c61663e6472--&gt;
&lt;!-- begin paragraph 664e77c6-5090-4176-9a6c-902b3958f466--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-664e77c6-5090-4176-9a6c-902b3958f466&quot;&gt;J’ai créé spécialement pour cet article un repository qui implémente le code ci-dessus : &lt;a href=&quot;https://github.com/univalence/laminar-article&quot;&gt;https://github.com/univalence/laminar-article&lt;/a&gt;. Ce dernier peut aussi vous être utile pour mettre en place une codebase ScalaJS comportant notamment le préprocesseur SCSS et Vite. Cette codebase est en très grande partie inspirée du &lt;a href=&quot;https://www.youtube.com/watch?v=hWUAVrNj65c&quot;&gt;talk de Sebastien Doeraene&lt;/a&gt; que nous avons notamment eu la chance d’écouter lors du &lt;a href=&quot;http://scala.io/&quot;&gt;Scala.IO&lt;/a&gt; 2022.&lt;/p&gt;
&lt;!-- end paragraph 664e77c6-5090-4176-9a6c-902b3958f466--&gt;
&lt;!-- begin heading_2 8dfdf4ad-22e3-4798-af1c-88ba23f8090f--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8dfdf4ad-22e3-4798-af1c-88ba23f8090f&quot;&gt;Les tradeoffs&lt;/h3&gt;
&lt;!-- end heading_2 8dfdf4ad-22e3-4798-af1c-88ba23f8090f--&gt;
&lt;!-- begin paragraph 16db4cc3-7ab4-4e20-9e3f-82ae2bfd4e17--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-16db4cc3-7ab4-4e20-9e3f-82ae2bfd4e17&quot;&gt;Cet avant-gout devrait déjà vous avoir partiellement convaincu. J’ai personnellement été bluffé de voir à quel point le développement frontend devenait plaisant et intuitif avec Laminar. Je n’ai bien sûr qu’effleuré la surface et je vous conseille d’aller creuser dans &lt;a href=&quot;https://laminar.dev/documentation&quot;&gt;la documentation&lt;/a&gt; si cette dernière vous intéresse.&lt;/p&gt;
&lt;!-- end paragraph 16db4cc3-7ab4-4e20-9e3f-82ae2bfd4e17--&gt;
&lt;!-- begin paragraph b185a32c-1163-4f8e-ba66-a1d4057ee97e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b185a32c-1163-4f8e-ba66-a1d4057ee97e&quot;&gt;Maintenant, afin que vous optiez pour la solution qui convient le plus à votre projet, il est de mon devoir de vous faire part des points négatifs d’un tel environnement de développement. Nous avons longuement vu pourquoi opter pour ScalaJS. Voici certaines choses qui pourraient vous manquer si jamais vous sautiez le pas.&lt;/p&gt;
&lt;!-- end paragraph b185a32c-1163-4f8e-ba66-a1d4057ee97e--&gt;
&lt;!-- begin paragraph 7bf59bee-d539-4d4e-a0eb-79b5457752e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7bf59bee-d539-4d4e-a0eb-79b5457752e4&quot;&gt;Le point le plus important selon moi, c’est le tooling. Ce dernier n’est pas très optimisé pour ScalaJS. Il suffit de voir &lt;a href=&quot;https://github.com/univalence/laminar-article&quot;&gt;le repository&lt;/a&gt; de l’exemple ci-dessus pour se rendre compte qu’il faut tout de même pas mal de code custom pour rattacher les pièces ensemble. De plus, certains outils très intéressant n’existent pas pour ScalaJS, ce qui me manque personnellement, c&amp;#39;est le support de &lt;a href=&quot;https://github.com/storybookjs/storybook&quot;&gt;Storybook&lt;/a&gt;. Cet outil est devenu un incontournable du développement frontend et permet véritablement à une équipe d’avoir plus de visibilité sur ses composants et de créer un &lt;i&gt;design system&lt;/i&gt; déclinable et efficace. &lt;/p&gt;
&lt;!-- end paragraph 7bf59bee-d539-4d4e-a0eb-79b5457752e4--&gt;
&lt;!-- begin paragraph a24b9f67-ba3f-412d-b6c1-2e38c204bd10--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a24b9f67-ba3f-412d-b6c1-2e38c204bd10&quot;&gt;L’écosystème étant encore très jeune, il manque de librairies et de frameworks. Comme dit au-dessus, ScalaJS est compatible avec toutes les libraires Javascript à condition d’avoir une façade. Cependant, comme utiliser une librairie en Java dans un projet Scala n’est pas agréable, il en va de même pour une librairie Javascript dans un projet ScalaJS. Cette fonctionnalité permet de dépanner le développeur en lui assurant, par exemple, de toujours trouver chaussure à son pied. Cependant, on ne lésinerait pas sur plus de librairies directement faites en ScalaJS, tel que Laminar, adoptant ainsi les conventions implicites du langage, tel que la composition, l’omniprésence des fonctions anonymes ou encore l’intégration avec le &lt;i&gt;standard librairie&lt;/i&gt; de Scala.  &lt;/p&gt;
&lt;!-- end paragraph a24b9f67-ba3f-412d-b6c1-2e38c204bd10--&gt;
&lt;!-- begin paragraph 50cf006b-49d8-4c29-89dd-624956d9671b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50cf006b-49d8-4c29-89dd-624956d9671b&quot;&gt;Ces tradeoffs ne sont pas une fatalité en soi. Ils ne sont que la conséquence d’un écosystème encore assez jeune et ambitieux. Néanmoins, la stabilité reste une métrique très importante à prendre en compte lorsqu’on choisit une technologie pour un projet professionnel et je devais souligner ces points.&lt;/p&gt;
&lt;!-- end paragraph 50cf006b-49d8-4c29-89dd-624956d9671b--&gt;
&lt;!-- begin heading_1 c6fef7fa-6f4b-481e-a34c-ca93e4bd31be--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c6fef7fa-6f4b-481e-a34c-ca93e4bd31be&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 c6fef7fa-6f4b-481e-a34c-ca93e4bd31be--&gt;
&lt;!-- begin paragraph 2ec0531a-d612-4292-a5a1-64187428a6db--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2ec0531a-d612-4292-a5a1-64187428a6db&quot;&gt;Depuis que j’ai vu la présentation sur ScalaJS et Laminar de Sébastien Doeraene au &lt;a href=&quot;http://scala.io/&quot;&gt;Scala.IO&lt;/a&gt; 2022, j’ai été conquis par la solution. Elle réunit dans une seule et même stack notre code frontend et backend, elle permet d’avoir un code typé, dry et safe. Selon moi, ScalaJS a réussi son coup, à savoir permettre à des développeurs Scala de coder des applications frontend sans que cela soit un supplice.&lt;/p&gt;
&lt;!-- end paragraph 2ec0531a-d612-4292-a5a1-64187428a6db--&gt;
&lt;!-- begin paragraph eb139828-3278-4a24-b02f-b51d45505cb0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eb139828-3278-4a24-b02f-b51d45505cb0&quot;&gt;La technologie reste assez jeune et vous ne retrouverez pas tous les outils présents dans l’écosystème Javascript. Mais c’est largement suffisant pour construire des projets d’ampleurs sans trop de soucis. Essayer c’est l’adopter !&lt;/p&gt;
&lt;!-- end paragraph eb139828-3278-4a24-b02f-b51d45505cb0--&gt;
&lt;!-- begin paragraph c8b17791-dba7-4e2b-b022-525fb1c84078--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c8b17791-dba7-4e2b-b022-525fb1c84078&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c8b17791-dba7-4e2b-b022-525fb1c84078--&gt;
&lt;!-- begin paragraph af296a5c-3c38-472e-815d-b71372904cbc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-af296a5c-3c38-472e-815d-b71372904cbc&quot;&gt; &lt;/p&gt;
&lt;!-- end paragraph af296a5c-3c38-472e-815d-b71372904cbc--&gt;
&lt;!-- begin paragraph 44d183e5-662d-41f8-a9dd-8c3d3cad7d27--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-44d183e5-662d-41f8-a9dd-8c3d3cad7d27&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 44d183e5-662d-41f8-a9dd-8c3d3cad7d27--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:a9256e9e16db453f9a89f0b4dd0090db</id>
    <title>Comment la génération de code de zio-spark fonctionne</title>
    <updated>2023-01-18T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/comment-la-generation-de-code-de-zio-spark-fonctionne.html"/>
    <!--summary zio-spark est une librairie assez atypique puisqu’elle se base énormément sur la génération de code. Cet article vous explique comment !-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Spark"></category>    <category term="ZIO"></category>    <category term="Metaprogrammation"></category>    <category term="zio-spark"></category>    <content type="html">
&lt;!-- begin heading_1 75052e72-acd7-41c9-bb4a-c9fb1cfca7bb--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-75052e72-acd7-41c9-bb4a-c9fb1cfca7bb&quot;&gt;Introduction&lt;/h2&gt;
&lt;!-- end heading_1 75052e72-acd7-41c9-bb4a-c9fb1cfca7bb--&gt;
&lt;!-- begin paragraph 10ba72b6-1724-4c09-a5b6-6311f1e12855--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-10ba72b6-1724-4c09-a5b6-6311f1e12855&quot;&gt;Intégrer une librairie existante dans ZIO est souvent assez direct. Pour simplifier, dans les (très) grandes lignes, il suffit de wrapper toutes les fonctions qui causent des effets dans ZIO. Ces fonctions auront ainsi pleins de nouveaux superpouvoirs tels que la parallélisation, la gestion des erreurs intégrée, le scheduling et j’en passe.&lt;/p&gt;
&lt;!-- end paragraph 10ba72b6-1724-4c09-a5b6-6311f1e12855--&gt;
&lt;!-- begin paragraph 63ceb308-d2af-4311-a691-35568ab54964--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-63ceb308-d2af-4311-a691-35568ab54964&quot;&gt;Avec ceci en tête, nous avons donc décidé de faire zio-spark. Nous utilisions déjà ZIO et Spark dans nos pipelines et le but était ainsi d’avoir une meilleure syntaxe. Nous sommes aujourd’hui fiers de cette librairie, mais ceci ne s&amp;#39;est pas passé sans son lot de difficultés.&lt;/p&gt;
&lt;!-- end paragraph 63ceb308-d2af-4311-a691-35568ab54964--&gt;
&lt;!-- begin paragraph 43bcf331-3507-4355-8bb2-699e3935ac34--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-43bcf331-3507-4355-8bb2-699e3935ac34&quot;&gt;Je vous propose de ce fait de retracer son histoire afin de voir les différentes problématiques qui se sont posées et comment nous les avons traités.&lt;/p&gt;
&lt;!-- end paragraph 43bcf331-3507-4355-8bb2-699e3935ac34--&gt;
&lt;!-- begin heading_1 ca793f79-59bf-4455-ae69-678b5268817a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ca793f79-59bf-4455-ae69-678b5268817a&quot;&gt;La méthode naïve&lt;/h2&gt;
&lt;!-- end heading_1 ca793f79-59bf-4455-ae69-678b5268817a--&gt;
&lt;!-- begin paragraph 738ab030-6339-4f6a-84c2-f3b8a0154248--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-738ab030-6339-4f6a-84c2-f3b8a0154248&quot;&gt;Nous voulions une API qui se rapproche le plus possible de celle de Spark. Le but étant que l’utilisateur puisse aisément transformer du code Spark and du code zio-spark.&lt;/p&gt;
&lt;!-- end paragraph 738ab030-6339-4f6a-84c2-f3b8a0154248--&gt;
&lt;!-- begin paragraph 07e263bb-1bb1-40f9-b480-bb1457bb0912--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-07e263bb-1bb1-40f9-b480-bb1457bb0912&quot;&gt;Ainsi si nous prenons le code suivant :&lt;/p&gt;
&lt;!-- end paragraph 07e263bb-1bb1-40f9-b480-bb1457bb0912--&gt;
&lt;!-- begin code 71919481-2117-4f96-ba63-7a282438f1fe--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-71919481-2117-4f96-ba63-7a282438f1fe&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.spark.sql._

val df: DataFrame = ???
val res: Int = df.filter(&amp;quot;&amp;quot;).count&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 71919481-2117-4f96-ba63-7a282438f1fe--&gt;
&lt;!-- begin paragraph fcf0d80d-63c0-48b0-8574-6d69c7285feb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fcf0d80d-63c0-48b0-8574-6d69c7285feb&quot;&gt;Le but est d’avoir une API similaire pour zio-spark :&lt;/p&gt;
&lt;!-- end paragraph fcf0d80d-63c0-48b0-8574-6d69c7285feb--&gt;
&lt;!-- begin code ca2296f1-f460-4a5e-93e1-ca3f7466eb7f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ca2296f1-f460-4a5e-93e1-ca3f7466eb7f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import zio.spark.sql._

val df: DataFrame = ???
val res: Task[Int] = df.filter(&amp;quot;&amp;quot;).count&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ca2296f1-f460-4a5e-93e1-ca3f7466eb7f--&gt;
&lt;!-- begin paragraph 4be68388-8a50-402a-9c9b-0741c83ab509--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4be68388-8a50-402a-9c9b-0741c83ab509&quot;&gt;Pour ce faire, nous avons créé notre propre objet &lt;code&gt;Dataset[T]&lt;/code&gt; de la manière suivante, sachant qu’un &lt;code&gt;DataFrame&lt;/code&gt; n’est qu’un &lt;code&gt;Dataset[Row]&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 4be68388-8a50-402a-9c9b-0741c83ab509--&gt;
&lt;!-- begin code bfa4c3ad-44f5-47ab-b00a-13094a097603--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bfa4c3ad-44f5-47ab-b00a-13094a097603&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;package zio.spark.sql

import org.apache.spark.sql.{Dataset =&amp;gt; UnderlyingDataset}
import zio._

case class Dataset[T](underlying: UnderlyingDataset[T]) {
	def filter(conditionExpr: String): Dataset[T] = 
		Dataset(underlying.filter(conditionExpr))

	def count(implicit trace: Trace): Task[Long] = 
		ZIO.attempt(underlying.count)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bfa4c3ad-44f5-47ab-b00a-13094a097603--&gt;
&lt;!-- begin paragraph c011f78f-a476-4a84-9e7a-04babd06e2c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c011f78f-a476-4a84-9e7a-04babd06e2c1&quot;&gt;Cela fonctionne très bien et répondait à notre problématique puisque l’utilisateur pouvait à partir de maintenant manipuler des &lt;code&gt;DataFrame&lt;/code&gt; dans lesquels toutes actions étaient transformées en effets grâce à ZIO nous permettant d’ores et déjà de scheduler nos jobs par exemple.&lt;/p&gt;
&lt;!-- end paragraph c011f78f-a476-4a84-9e7a-04babd06e2c1--&gt;
&lt;!-- begin paragraph ec55fc51-75fc-4f5b-9411-3cfc417d869d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec55fc51-75fc-4f5b-9411-3cfc417d869d&quot;&gt;Toutes actions ? Pas vraiment… Dans l’état actuel des choses, notre nouveau &lt;code&gt;Dataset&lt;/code&gt; n’en a qu’une seule, la fonction &lt;code&gt;count&lt;/code&gt;. Le reste, que ce soit les transformations ou les actions, il va falloir les implémenter nous même et autant vous dire que l’API de Spark ne rigole pas. Pour être précis, il y a, pour la version Spark 3.3.1 en Scala 2.13, 216 fonctions dans la class &lt;code&gt;Dataset&lt;/code&gt; (On ne prend pas en compte ses déclinaisons telles que le &lt;code&gt;RelationalGroupedDataset&lt;/code&gt; par exemple).&lt;/p&gt;
&lt;!-- end paragraph ec55fc51-75fc-4f5b-9411-3cfc417d869d--&gt;
&lt;!-- begin paragraph 0967c388-b33b-4860-9da0-1e489d8ef0b1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0967c388-b33b-4860-9da0-1e489d8ef0b1&quot;&gt;Nous sommes vite arrivés à la conclusion qu’il fallait qu’on réfléchisse autrement.&lt;/p&gt;
&lt;!-- end paragraph 0967c388-b33b-4860-9da0-1e489d8ef0b1--&gt;
&lt;!-- begin heading_1 6ca36f9b-17be-4781-928c-9c8ee51e6ca1--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6ca36f9b-17be-4781-928c-9c8ee51e6ca1&quot;&gt;La génération de code&lt;/h2&gt;
&lt;!-- end heading_1 6ca36f9b-17be-4781-928c-9c8ee51e6ca1--&gt;
&lt;!-- begin paragraph 41fe829e-cc71-4a01-9844-d872b21f9af5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-41fe829e-cc71-4a01-9844-d872b21f9af5&quot;&gt;Nous sommes partis d’un postulat simple : un &lt;code&gt;Dataset&lt;/code&gt; possède majoritairement deux types d’opérations, les actions et les transformations. &lt;/p&gt;
&lt;!-- end paragraph 41fe829e-cc71-4a01-9844-d872b21f9af5--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Les transformations sont pures (ou presque) puisqu’elles servent juste à mettre à jour le &lt;code&gt;LogicalPlan&lt;/code&gt; qui n’est rien d’autre qu’un ADT représentant un programme Spark.&lt;/li&gt;&lt;li&gt;Les actions sont impures parce qu’elles appliquent le &lt;code&gt;LogicalPlan&lt;/code&gt; et doivent donc être “wrappées” dans ZIO.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 67019727-4089-4201-81e9-4d6bde2751d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67019727-4089-4201-81e9-4d6bde2751d3&quot;&gt;C’est une façon de penser assez binaire et même un robot pourrait réécrire notre version d’un &lt;code&gt;Dataset&lt;/code&gt;. S’il pourrait, pourquoi ne le ferait-il pas ?&lt;/p&gt;
&lt;!-- end paragraph 67019727-4089-4201-81e9-4d6bde2751d3--&gt;
&lt;!-- begin paragraph 25015cfe-0aee-4a25-94d6-ef1fe05eba2c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-25015cfe-0aee-4a25-94d6-ef1fe05eba2c&quot;&gt;Il s’avère que pendant cette période, je suis tombé sur &lt;a href=&quot;https://vigoo.github.io/posts/2020-09-23-zioaws-code-generation.html&quot;&gt;un article&lt;/a&gt; de Vigoo, un développeur Scala qui travaillait actuellement pour Ziverge, la boite en charge de ZIO. Ce dernier expliquait comment il avait fait pour générer &lt;a href=&quot;https://github.com/zio/zio-aws&quot;&gt;zio-aws&lt;/a&gt;, une librairie qui créé un client Scala pour tous les services AWS basés sur ZIO. Cela nous a servi de base pour zio-spark !&lt;/p&gt;
&lt;!-- end paragraph 25015cfe-0aee-4a25-94d6-ef1fe05eba2c--&gt;
&lt;!-- begin heading_2 062c53e8-3ce6-4a6e-862d-fa182cda71f5--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-062c53e8-3ce6-4a6e-862d-fa182cda71f5&quot;&gt;Le plugin SBT&lt;/h3&gt;
&lt;!-- end heading_2 062c53e8-3ce6-4a6e-862d-fa182cda71f5--&gt;
&lt;!-- begin paragraph ce558df2-dcca-4b29-aae7-41b407d8f065--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce558df2-dcca-4b29-aae7-41b407d8f065&quot;&gt;Nous n’avons pas réinventé la roue pour générer nos fichiers &lt;code&gt;.scala&lt;/code&gt;. En effet, nous avons suivi à la lettre l’article de blog de Vigoo à ce niveau-là. Nous avons créé un plugin SBT local nommé &lt;a href=&quot;https://github.com/univalence/zio-spark/tree/master/zio-spark-codegen&quot;&gt;zio-spark-codegen&lt;/a&gt; chargé de générer ces fichiers. &lt;/p&gt;
&lt;!-- end paragraph ce558df2-dcca-4b29-aae7-41b407d8f065--&gt;
&lt;!-- begin paragraph fa6fa7fb-a439-46a3-b675-018e08d39865--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa6fa7fb-a439-46a3-b675-018e08d39865&quot;&gt;Pour les générer, nous devions nous baser sur le code source Spark. Notre programme devait ainsi, à l’instar de zio-aws qui se basait sur des &lt;a href=&quot;https://github.com/aws/aws-sdk-java-v2/tree/master/services&quot;&gt;jsons&lt;/a&gt;, créer un programme capable de lire du code Scala pour en générer à son tour.&lt;/p&gt;
&lt;!-- end paragraph fa6fa7fb-a439-46a3-b675-018e08d39865--&gt;
&lt;!-- begin heading_2 f54375c3-2d38-4122-afae-b93a1aa08cd0--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f54375c3-2d38-4122-afae-b93a1aa08cd0&quot;&gt;Scalameta à la rescousse&lt;/h3&gt;
&lt;!-- end heading_2 f54375c3-2d38-4122-afae-b93a1aa08cd0--&gt;
&lt;!-- begin paragraph c33aa056-453e-487c-889a-d360f644adcc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c33aa056-453e-487c-889a-d360f644adcc&quot;&gt;Nous avons tout d’abord testé &lt;a href=&quot;https://www.scala-lang.org/api/2.12.4/scala-reflect/scala/reflect/api/Universe.html&quot;&gt;la réflexion&lt;/a&gt; pour lire le code Spark. Malheureusement, ceci s&amp;#39;est très vite avéré inefficace. Le Type Erasure nous empêchait de récupérer des informations primordiales sur les différents types, rendant la tâche très difficile voir impossible. Certaines choses nous semblaient aussi impossible avec cette technique. On ne voyait pas, par exemple, comment on aurait pu récupérer les docstrings des différentes fonctions sans une étape manuelle.&lt;/p&gt;
&lt;!-- end paragraph c33aa056-453e-487c-889a-d360f644adcc--&gt;
&lt;!-- begin paragraph 0945dad0-d549-4584-b04c-9b60f42f16e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0945dad0-d549-4584-b04c-9b60f42f16e4&quot;&gt;Après avoir essayé et raté, nous nous sommes donc tournés vers &lt;a href=&quot;https://scalameta.org/&quot;&gt;Scalameta&lt;/a&gt;. Scalameta n’utilise pas du tout la réflexion, cette librairie crée depuis un ADT représentant un programme depuis du code Scala (un String). Il permet de valider qu’un programme est correct, que deux programmes soient synthétiquement équivalents (même si la forme n’est pas la même), de créer des règles de syntaxe (scalafmt et scalafix se basent sur scalameta) et dans notre cas d’interpréter l’ADT pour regénérer du code Scala depuis ce dernier.&lt;/p&gt;
&lt;!-- end paragraph 0945dad0-d549-4584-b04c-9b60f42f16e4--&gt;
&lt;!-- begin paragraph 4cc80b66-625b-49cb-878b-2480cbe1ab30--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4cc80b66-625b-49cb-878b-2480cbe1ab30&quot;&gt;Jusqu’à présent, voici comment le plugin fonctionne dans les grandes lignes :&lt;/p&gt;
&lt;!-- end paragraph 4cc80b66-625b-49cb-878b-2480cbe1ab30--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;On récupère le classpath&lt;/li&gt;&lt;li&gt;On récupère le code source &lt;code&gt;Dataset.scala&lt;/code&gt;&lt;/li&gt;&lt;li&gt;On récupère son ADT grâce à Scalameta&lt;/li&gt;&lt;li&gt;On traverse cet ADT pour générer notre &lt;code&gt;Dataset.scala&lt;/code&gt; sous la forme d’un simple String&lt;/li&gt;&lt;li&gt;On écrit ce String dans l’endroit de notre choix&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph aedfdecb-087d-4187-ab1e-0cc206d4cae0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aedfdecb-087d-4187-ab1e-0cc206d4cae0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph aedfdecb-087d-4187-ab1e-0cc206d4cae0--&gt;
&lt;!-- begin heading_2 840c44e7-d26c-4114-9966-570bc20b1ab5--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-840c44e7-d26c-4114-9966-570bc20b1ab5&quot;&gt;Du code custom dans du code généré&lt;/h3&gt;
&lt;!-- end heading_2 840c44e7-d26c-4114-9966-570bc20b1ab5--&gt;
&lt;!-- begin paragraph 5a46b9f8-dd72-4110-a387-b42051acde23--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a46b9f8-dd72-4110-a387-b42051acde23&quot;&gt;Félicitations, nous avons notre &lt;code&gt;Dataset&lt;/code&gt; généré de A à Z en suivant la règle binaire érigé au-dessus (on wrap toutes les actions dans des effets et on retranscrit comme tels les transformations). Malheureusement pour nous, le monde n’est pas si manichéen. Ce modèle automatique est devenu assez restrictif lorsque nous avons eu besoin de customiser certaines fonctions de &lt;code&gt;Dataset&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 5a46b9f8-dd72-4110-a387-b42051acde23--&gt;
&lt;!-- begin paragraph ebdc4bc4-b4a3-4cd5-9ad4-df33eb18c8ec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ebdc4bc4-b4a3-4cd5-9ad4-df33eb18c8ec&quot;&gt;En effet, au fur et à mesure que nous avancions, nous avons eu besoin d’avoir, au sein de nos &lt;code&gt;Dataset&lt;/code&gt;, du code custom. Nous avions besoin plus précisément de cette feature pour deux cas particulier.&lt;/p&gt;
&lt;!-- end paragraph ebdc4bc4-b4a3-4cd5-9ad4-df33eb18c8ec--&gt;
&lt;!-- begin heading_3 fb8488d7-7373-4e20-8e1c-95f02c0340c2--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-fb8488d7-7373-4e20-8e1c-95f02c0340c2&quot;&gt;Créer nos propres fonctions&lt;/h4&gt;
&lt;!-- end heading_3 fb8488d7-7373-4e20-8e1c-95f02c0340c2--&gt;
&lt;!-- begin paragraph b3180389-8a3d-4d59-9911-a0d715604886--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3180389-8a3d-4d59-9911-a0d715604886&quot;&gt;Créer nos fonctions se comprend assez facilement, nous voulions notamment rajouter une fonction &lt;code&gt;headOption&lt;/code&gt; afin de profiter du type system de Scala. &lt;/p&gt;
&lt;!-- end paragraph b3180389-8a3d-4d59-9911-a0d715604886--&gt;
&lt;!-- begin heading_3 f3023500-23ed-44c3-8e58-a251fa7fa30b--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-f3023500-23ed-44c3-8e58-a251fa7fa30b&quot;&gt;Éditer manuellement des fonctions existantes&lt;/h4&gt;
&lt;!-- end heading_3 f3023500-23ed-44c3-8e58-a251fa7fa30b--&gt;
&lt;!-- begin paragraph e31cf9d7-53f6-4bad-b918-44ba460f4ea7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e31cf9d7-53f6-4bad-b918-44ba460f4ea7&quot;&gt;Mais pourquoi voudrions-nous éditer manuellement les fonctions de Spark ? Car ZIO ne se résume pas qu’à wrapper les effets dans ZIO. Prenons l’exemple de la fonction &lt;code&gt;show&lt;/code&gt;. Cette dernière affiche par défaut les 20 premières lignes d’un &lt;code&gt;Dataset&lt;/code&gt; dans la console. C&amp;#39;est une action, ainsi notre plugin va générer la fonction suivante :&lt;/p&gt;
&lt;!-- end paragraph e31cf9d7-53f6-4bad-b918-44ba460f4ea7--&gt;
&lt;!-- begin code 89b15cbd-3584-4a4e-8212-d784dcd6f4d2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-89b15cbd-3584-4a4e-8212-d784dcd6f4d2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def show(): Task[Unit] = ZIO.attempt(underlying.show())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 89b15cbd-3584-4a4e-8212-d784dcd6f4d2--&gt;
&lt;!-- begin paragraph e95dafc8-0bc6-4776-a045-13785d211541--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e95dafc8-0bc6-4776-a045-13785d211541&quot;&gt;Cette fonction est ennuyante à tester pour l’utilisateur, il va forcément devoir passer par un mock puisque &lt;code&gt;show&lt;/code&gt; se base sur la fonction &lt;code&gt;println&lt;/code&gt; de Scala. Un utilisateur de ZIO n’a pas l’habitude de mocker &lt;code&gt;print&lt;/code&gt; parce qu’il utilise &lt;code&gt;Console.printLine&lt;/code&gt; à la place ! C’est chose faite dans zio-spark, la fonction &lt;code&gt;show&lt;/code&gt; a été réécrite pour se baser sur &lt;code&gt;Console.printLine&lt;/code&gt;. Ainsi, vous pouvez écrire le test suivant sans encombre :&lt;/p&gt;
&lt;!-- end paragraph e95dafc8-0bc6-4776-a045-13785d211541--&gt;
&lt;!-- begin code 5cf65c8c-0a4b-411f-92a6-e9976ef34e5e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5cf65c8c-0a4b-411f-92a6-e9976ef34e5e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;test(&amp;quot;Dataset should implement show correctly&amp;quot;) {
  val result =
    &amp;quot;&amp;quot;&amp;quot;+---------+---+
      &amp;gt;|     name|age|
      &amp;gt;+---------+---+
      &amp;gt;|    Maria| 93|
      &amp;gt;|     John| 24|
      &amp;gt;|    Peter| 19|
      &amp;gt;|Cassandra| 46|
      &amp;gt;+---------+---+
      &amp;gt;
      &amp;gt;&amp;quot;&amp;quot;&amp;quot;.stripMargin(&amp;#39;&amp;gt;&amp;#39;)
  for {
    df     &amp;lt;- read
    _      &amp;lt;- df.show
    output &amp;lt;- TestConsole.output
    representation = output.mkString(&amp;quot;\n&amp;quot;)
  } yield assertTrue(representation == result)
} @@ silent&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5cf65c8c-0a4b-411f-92a6-e9976ef34e5e--&gt;
&lt;!-- begin paragraph ac3e18f0-bd52-4a09-935f-7a35889263bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ac3e18f0-bd52-4a09-935f-7a35889263bb&quot;&gt;Ce comportement est très spécifique à la fonction show, la génération automatique de code n’est pas faite pour cela, alors comment l’intégrer ?&lt;/p&gt;
&lt;!-- end paragraph ac3e18f0-bd52-4a09-935f-7a35889263bb--&gt;
&lt;!-- begin heading_3 ce3624dc-77ee-486b-8bbe-bb4302fe7a31--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-ce3624dc-77ee-486b-8bbe-bb4302fe7a31&quot;&gt;Insérer du code custom dans la génération&lt;/h4&gt;
&lt;!-- end heading_3 ce3624dc-77ee-486b-8bbe-bb4302fe7a31--&gt;
&lt;!-- begin paragraph 9c1e2bf9-acc0-457d-8518-6d7bd7779222--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c1e2bf9-acc0-457d-8518-6d7bd7779222&quot;&gt;Ce code, il va falloir l’écrire nous même pardi ! Rassurez vous, si c&amp;#39;était aussi simple, je n’aurais pas pris la peine d’écrire cette partie. &lt;/p&gt;
&lt;!-- end paragraph 9c1e2bf9-acc0-457d-8518-6d7bd7779222--&gt;
&lt;!-- begin paragraph 9770b948-a32d-4230-82d0-0edacfccaf7b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9770b948-a32d-4230-82d0-0edacfccaf7b&quot;&gt;La façon la plus naïve de le faire est de tagger cette fonction afin qu’elle ne soit pas automatiquement générée puis de la rajouter à la main en l’écrivant sous forme de string pour l’insérer dans le string final. Avec cette méthode, nous manipulons un string ainsi, il n’y a pas d’autocomplétion, pas d’erreurs au compile time, pas de coloration syntaxique. Pour résumer, ce n’est pas plaisant.&lt;/p&gt;
&lt;!-- end paragraph 9770b948-a32d-4230-82d0-0edacfccaf7b--&gt;
&lt;!-- begin paragraph 1f1988c8-c457-4e8b-8ae9-6557ea323541--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f1988c8-c457-4e8b-8ae9-6557ea323541&quot;&gt;Il nous fallait un moyen d’écrire du code Scala qui serve uniquement pour la génération de code. Ce dernier doit se baser sur l’ancienne classe déjà générée d’un &lt;code&gt;Dataset&lt;/code&gt; afin de pouvoir réutiliser des fonctions déjà générées auparavant. &lt;/p&gt;
&lt;!-- end paragraph 1f1988c8-c457-4e8b-8ae9-6557ea323541--&gt;
&lt;!-- begin paragraph d46567a1-0f80-4e7d-b2b2-fba03fb35fbb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d46567a1-0f80-4e7d-b2b2-fba03fb35fbb&quot;&gt;Voici la marche à suivre que nous avons adoptée :&lt;/p&gt;
&lt;!-- end paragraph d46567a1-0f80-4e7d-b2b2-fba03fb35fbb--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Nous nous sommes servis du dossier &lt;code&gt;it&lt;/code&gt; pour entreposer le code custom de nos générations. Ce dossier est censé être destiné aux tests d’intégrations comme l’indique la documentation de &lt;a href=&quot;https://www.scala-sbt.org/1.x/docs/Testing.html#Integration+Tests&quot;&gt;SBT&lt;/a&gt;. Nous l’avons un peu détourné de son but premier. Il a l’avantage d’être reconnu aisément par l’IDE et de pouvoir utiliser notre librairie zio-spark.&lt;/li&gt;&lt;li&gt;Dans ce dossier, nous avons créé des fichiers “overlay”.  Ces fichiers contiennent les fonctions qui seront insérées lors de la prochaine génération de code. Il existe deux types de fichiers d’overlay, dans le cas du &lt;code&gt;Dataset&lt;/code&gt;, &lt;code&gt;DatasetOverlay.scala&lt;/code&gt; et &lt;code&gt;SpecificDatasetOverlay.scala&lt;/code&gt;. Le premier contient le code à insérer dans la génération, qu’importe la version de Scala. Le deuxième quant à lui est associé à une version.&lt;/li&gt;&lt;li&gt;Un fichier “overlay” n’est pas qu’une série de fonctions. Comme je vous l’ai dit, ce code doit compiler et donc être du code valide. Pour ce faire, nous avons opté pour la structure suivante :
&lt;!-- begin code 6ac58a14-e44d-459f-8e59-1ccca1db86ea--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-6ac58a14-e44d-459f-8e59-1ccca1db86ea&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import zio.spark.sql._

class DatasetOverlay[T](self: Dataset[T]) {
  import self._

  // template:on
  
  // template:off
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6ac58a14-e44d-459f-8e59-1ccca1db86ea--&gt;
&lt;!-- begin paragraph f80ee8e2-cc84-4b05-ae07-cb17d1da9a1a--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-f80ee8e2-cc84-4b05-ae07-cb17d1da9a1a&quot;&gt;Le code à l’intérieur de la class &lt;code&gt;DatasetOverlay&lt;/code&gt;, entre les balises template sera pris en compte lors de la génération. Il faut voir cette classe comme une extension d’un &lt;code&gt;Dataset&lt;/code&gt;, ce faisant, il peut utiliser toutes ses fonctions.&lt;/p&gt;
&lt;!-- end paragraph f80ee8e2-cc84-4b05-ae07-cb17d1da9a1a--&gt;&lt;/li&gt;&lt;li&gt;Maintenant que nous avons ces fichiers, le plugin va les prendre en compte lors de la génération. Tout comme pour les fichiers Spark, nous avons utilisé Scalameta pour extraire les fonctions entre les balises template. Nous les rajoutons ensuite dans le fichier final généré.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 e46202f5-a69b-4abf-845a-585a7d258336--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e46202f5-a69b-4abf-845a-585a7d258336&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 e46202f5-a69b-4abf-845a-585a7d258336--&gt;
&lt;!-- begin paragraph 6851086a-682e-44b9-8f92-51246a39a92b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6851086a-682e-44b9-8f92-51246a39a92b&quot;&gt;Voilà dans les grandes lignes comment nous sommes arrivés à générer du code zio-spark à partir du code Spark. Je me suis concentré ici sur la class &lt;code&gt;Dataset&lt;/code&gt;, mais cette méthode a ensuite été généralisée pour d’autres classes de Spark telle que la classe &lt;code&gt;SparkContext&lt;/code&gt;. Je l’ai rapidement mentionné avec les fichiers “overlay” mais cette méthode fonctionne pour différentes versions de Scala et de Spark au sein de notre système.&lt;/p&gt;
&lt;!-- end paragraph 6851086a-682e-44b9-8f92-51246a39a92b--&gt;
&lt;!-- begin paragraph 2c41ba64-cf29-4d42-802f-158e925f35f4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c41ba64-cf29-4d42-802f-158e925f35f4&quot;&gt;La génération nous a fait gagner énormément de temps puisqu’elle nous a évité d’écrire une énorme partie de notre codebase manuellement. Elle s’est aussi avérée très utile lorsque nous avons eu besoin de faire des gros refactoring. Ça a notamment été le cas lorsqu’il a fallu &lt;a href=&quot;https://zio.dev/guides/migrate/zio-2.x-migration-guide/#guidelines-for-library-authors&quot;&gt;rajouter les traces avec ZIO 2&lt;/a&gt; ou alors lorsque nous avons décidé de supporter Scala 3. Là où de telles actions auraient dû être coûteuses à implémenter, elles n’ont pris que quelques minutes.&lt;/p&gt;
&lt;!-- end paragraph 2c41ba64-cf29-4d42-802f-158e925f35f4--&gt;
&lt;!-- begin paragraph 6d6b6bf7-f315-4efe-98a8-38a97728a7eb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d6b6bf7-f315-4efe-98a8-38a97728a7eb&quot;&gt;Si jamais vous voulez aller plus loin, je vous conseille de regarder le plugin directement : &lt;a href=&quot;https://github.com/univalence/zio-spark/tree/master/zio-spark-codegen&quot;&gt;https://github.com/univalence/zio-spark/tree/master/zio-spark-codegen&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6d6b6bf7-f315-4efe-98a8-38a97728a7eb--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:f40ee817cca14b13ac0363cf03a2f4cc</id>
    <title>Introduction a zio-spark</title>
    <updated>2023-01-02T08:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/introduction-a-zio-spark.html"/>
    <!--summary Dans cet article on va parler de l’interêt d’utiliser Spark avec ZIO et de la library zio-spark -->    <author>
      <name>Bastien Guihard</name>
      <uri>https://univalence.io/blog/auteurs/bastien-guihard.html</uri>
    </author>    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <content type="html">
&lt;!-- begin heading_1 8f5ba72b-f287-48cb-9472-00878fbd0fec--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-8f5ba72b-f287-48cb-9472-00878fbd0fec&quot;&gt;Introduction&lt;/h2&gt;
&lt;!-- end heading_1 8f5ba72b-f287-48cb-9472-00878fbd0fec--&gt;
&lt;!-- begin paragraph cec73834-1530-4af2-a9d1-5a47a78221d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cec73834-1530-4af2-a9d1-5a47a78221d3&quot;&gt;Il y a deux semaines, nous avons décidé d’annoncer &lt;a href=&quot;https://github.com/univalence/zio-spark&quot;&gt;zio-spark&lt;/a&gt;. C’est un projet qui nous tient particulièrement à cœur puisqu’il résulte de la combinaison de deux outils que nous affectionnons particulièrement, à savoir &lt;a href=&quot;https://zio.dev/&quot;&gt;ZIO&lt;/a&gt; et &lt;a href=&quot;https://spark.apache.org/&quot;&gt;Spark&lt;/a&gt;. Son objectif premier est de faciliter l’intégration de Spark dans l’écosystème ZIO afin de profiter des différents avantages de ce dernier.&lt;/p&gt;
&lt;!-- end paragraph cec73834-1530-4af2-a9d1-5a47a78221d3--&gt;
&lt;!-- begin paragraph 5e6ed37d-8bfd-4a43-9b55-9fb646555576--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e6ed37d-8bfd-4a43-9b55-9fb646555576&quot;&gt;Le but de cet article est de vous montrer comment la combinaison de ces deux librairies peut vous faciliter la vie au quotidien.&lt;/p&gt;
&lt;!-- end paragraph 5e6ed37d-8bfd-4a43-9b55-9fb646555576--&gt;
&lt;!-- begin heading_1 d1645872-decd-431a-b466-70dfeb2a5f0d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d1645872-decd-431a-b466-70dfeb2a5f0d&quot;&gt;Installation&lt;/h2&gt;
&lt;!-- end heading_1 d1645872-decd-431a-b466-70dfeb2a5f0d--&gt;
&lt;!-- begin paragraph 650d8144-df8b-4ed1-8938-d5777b5a7ad6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-650d8144-df8b-4ed1-8938-d5777b5a7ad6&quot;&gt;Cet article se base sur la version 0.10.0 de zio-spark. Les dépendances suivantes ont été utilisées :&lt;/p&gt;
&lt;!-- end paragraph 650d8144-df8b-4ed1-8938-d5777b5a7ad6--&gt;
&lt;!-- begin code af8d848b-b31c-484d-96be-41a8bfd114e0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-af8d848b-b31c-484d-96be-41a8bfd114e0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val dependencies = Seq(
	&amp;quot;org.apache.spark&amp;quot; %% &amp;quot;spark-core&amp;quot; % &amp;quot;3.2.2&amp;quot;,
	&amp;quot;org.apache.spark&amp;quot; %% &amp;quot;spark-sql&amp;quot; % &amp;quot;3.2.2&amp;quot;,
	&amp;quot;io.univalence&amp;quot;    %% &amp;quot;zio-spark&amp;quot;  % &amp;quot;0.10.0&amp;quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code af8d848b-b31c-484d-96be-41a8bfd114e0--&gt;
&lt;!-- begin heading_1 b0b01a9d-7b9c-4a5c-90ef-6e2dd7827495--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-b0b01a9d-7b9c-4a5c-90ef-6e2dd7827495&quot;&gt;Avantages liés à ZIO&lt;/h2&gt;
&lt;!-- end heading_1 b0b01a9d-7b9c-4a5c-90ef-6e2dd7827495--&gt;
&lt;!-- begin paragraph c91219d4-2eef-4985-87f0-5ce6abe36a28--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c91219d4-2eef-4985-87f0-5ce6abe36a28&quot;&gt;Les caractéristiques de ZIO sont surtout utiles lorsque nous devons manipuler nos jobs. En effet, ici, on opère au niveau du driver, il n’a pas d’effet sur les workers et donc sur les traitements des jobs Spark. &lt;/p&gt;
&lt;!-- end paragraph c91219d4-2eef-4985-87f0-5ce6abe36a28--&gt;
&lt;!-- begin paragraph cd699707-5deb-47c1-9af1-c802239a7d8b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd699707-5deb-47c1-9af1-c802239a7d8b&quot;&gt;L’objectif premier est d’optimiser l’allocation des ressources en permettant à l’utilisateur de facilement lancer plusieurs jobs en parallèle et de pouvoir programmer ses différents jobs comme il le sent. &lt;/p&gt;
&lt;!-- end paragraph cd699707-5deb-47c1-9af1-c802239a7d8b--&gt;
&lt;!-- begin paragraph 9cd53d6d-ba6e-4b85-8efe-697a73b4d046--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9cd53d6d-ba6e-4b85-8efe-697a73b4d046&quot;&gt;Voyons voir ces différentes facettes.&lt;/p&gt;
&lt;!-- end paragraph 9cd53d6d-ba6e-4b85-8efe-697a73b4d046--&gt;
&lt;!-- begin heading_2 59303a12-0c6e-495c-9eac-26fb4d5d205d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-59303a12-0c6e-495c-9eac-26fb4d5d205d&quot;&gt;Planification des jobs&lt;/h3&gt;
&lt;!-- end heading_2 59303a12-0c6e-495c-9eac-26fb4d5d205d--&gt;
&lt;!-- begin paragraph bc6b173d-a689-4f0b-bd83-995836d7f65f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bc6b173d-a689-4f0b-bd83-995836d7f65f&quot;&gt;Il y a un moment où il faut se demander comment faire pour planifier nos jobs. On veut, par exemple, pouvoir lancer un job en particulier chaque début de semaine, le lundi à 9h du matin. &lt;/p&gt;
&lt;!-- end paragraph bc6b173d-a689-4f0b-bd83-995836d7f65f--&gt;
&lt;!-- begin paragraph 6e18ae3a-a665-403f-8fab-316cb43a68a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e18ae3a-a665-403f-8fab-316cb43a68a9&quot;&gt;Généralement, les ingénieurs choisissent d’utiliser &lt;a href=&quot;https://airflow.apache.org/&quot;&gt;Airflow&lt;/a&gt; pour répondre à cette problématique. &lt;/p&gt;
&lt;!-- end paragraph 6e18ae3a-a665-403f-8fab-316cb43a68a9--&gt;
&lt;!-- begin paragraph 9e8cf7af-5f9a-44f6-b922-f1af1e849353--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e8cf7af-5f9a-44f6-b922-f1af1e849353&quot;&gt;C’est un outil qui y répond parfaitement, mais qui peut paraitre un peu overkill pour certains projets. En effet, pour ce simple besoin, il va falloir :&lt;/p&gt;
&lt;!-- end paragraph 9e8cf7af-5f9a-44f6-b922-f1af1e849353--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Gérer une instance Airflow ou payer pour un service serverless équivalent&lt;/li&gt;&lt;li&gt;Former les ingénieurs à la notion de DAG et autres spécificités liées à l’outil&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 049089f2-0575-4f25-b122-651e4ed7b241--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-049089f2-0575-4f25-b122-651e4ed7b241&quot;&gt;ZIO donne une alternative beaucoup moins coûteuse en ressources et en temps grâce aux &lt;a href=&quot;https://zio.dev/version-1.x/datatypes/misc/schedule/&quot;&gt;Schedules&lt;/a&gt;. Il permet de répondre à cette problématique directement depuis notre code.&lt;/p&gt;
&lt;!-- end paragraph 049089f2-0575-4f25-b122-651e4ed7b241--&gt;
&lt;!-- begin paragraph 784c5cf2-47b9-4727-afe7-bcc03736366d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-784c5cf2-47b9-4727-afe7-bcc03736366d&quot;&gt;Voici un exemple qui répond à la problématique ci-dessus :&lt;/p&gt;
&lt;!-- end paragraph 784c5cf2-47b9-4727-afe7-bcc03736366d--&gt;
&lt;!-- begin code b7672f13-080b-44af-a90c-23f70642b133--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b7672f13-080b-44af-a90c-23f70642b133&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import zio.spark.sql._

val job: ZIO[SparkSession, Throwable, Int] = ???
val eachMondayAt9AM = Schedule.dayOfWeek(1) &amp;amp;&amp;amp; Schedule.hourOfDay(9)

val app = job schedule eachMondayAt9AM&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b7672f13-080b-44af-a90c-23f70642b133--&gt;
&lt;!-- begin heading_2 a1341652-e42c-4169-9b10-ed88f007c12c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-a1341652-e42c-4169-9b10-ed88f007c12c&quot;&gt;Gestions des erreurs&lt;/h3&gt;
&lt;!-- end heading_2 a1341652-e42c-4169-9b10-ed88f007c12c--&gt;
&lt;!-- begin paragraph b54234db-53d9-4d61-8d04-0239823988a5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b54234db-53d9-4d61-8d04-0239823988a5&quot;&gt;On peut aussi simplement gérer nos erreurs de pipeline et réagir en conséquence. Dans l’exemple ci-dessous, on lance un autre job si le premier crash :&lt;/p&gt;
&lt;!-- end paragraph b54234db-53d9-4d61-8d04-0239823988a5--&gt;
&lt;!-- begin code 5a17fbc2-6d67-4fb1-ba9e-5a5d5005d200--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5a17fbc2-6d67-4fb1-ba9e-5a5d5005d200&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import zio.spark.sql._

val job: ZIO[SparkSession, Throwable, Int] = ???
val fallbackJob: ZIO[SparkSession, Throwable, Int] = ???

val app = job orElse fallbackJob&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5a17fbc2-6d67-4fb1-ba9e-5a5d5005d200--&gt;
&lt;!-- begin paragraph 171cb03a-69a8-4277-87c2-f8842a63fa8f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-171cb03a-69a8-4277-87c2-f8842a63fa8f&quot;&gt;Nous ne sommes pas obligés de régler un problème de job Spark en lançant un autre job Spark, on aurait aussi très bien pu envoyer un mail à un service de support ou autre à la place.&lt;/p&gt;
&lt;!-- end paragraph 171cb03a-69a8-4277-87c2-f8842a63fa8f--&gt;
&lt;!-- begin heading_2 4f19ab91-81fb-4553-81ac-db8bf9f6916c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4f19ab91-81fb-4553-81ac-db8bf9f6916c&quot;&gt;P&lt;b&gt;arallélisation&lt;/b&gt; des jobs&lt;/h3&gt;
&lt;!-- end heading_2 4f19ab91-81fb-4553-81ac-db8bf9f6916c--&gt;
&lt;!-- begin paragraph 7196d5ef-8fae-459f-a6c0-e2a71cac889b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7196d5ef-8fae-459f-a6c0-e2a71cac889b&quot;&gt;Comme dit au-dessus, l’avantage de zio-spark est de pouvoir facilement lancer en parallèle nos différents jobs et ainsi permettre à nos workers de toujours être occupés lorsqu’il y a du travail.&lt;/p&gt;
&lt;!-- end paragraph 7196d5ef-8fae-459f-a6c0-e2a71cac889b--&gt;
&lt;!-- begin paragraph 5ecc3e8e-3ac1-4833-8265-629893fba550--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ecc3e8e-3ac1-4833-8265-629893fba550&quot;&gt;Voici un exemple pour lancer deux jobs simultanément :&lt;/p&gt;
&lt;!-- end paragraph 5ecc3e8e-3ac1-4833-8265-629893fba550--&gt;
&lt;!-- begin code 5096090c-44c5-4b0f-abb1-685b9c045af5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5096090c-44c5-4b0f-abb1-685b9c045af5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import zio.spark.sql._

val job: ZIO[SparkSession, Throwable, Int] = ???
val otherJob: ZIO[SparkSession, Throwable, Int] = ???

val app = job zipPar otherJob&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5096090c-44c5-4b0f-abb1-685b9c045af5--&gt;
&lt;!-- begin paragraph 34150295-701a-47e8-8023-385f54aab4c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-34150295-701a-47e8-8023-385f54aab4c1&quot;&gt;Bien évidemment, toutes ces caractéristiques ne sont pas propres à zio-spark mais à ZIO, j’ai notamment fait &lt;a href=&quot;https://univalence.io/blog/articles/zio-qu-est-ce-que-c-est/&quot;&gt;un article&lt;/a&gt; qui va un peu plus loin sur le sujet.&lt;/p&gt;
&lt;!-- end paragraph 34150295-701a-47e8-8023-385f54aab4c1--&gt;
&lt;!-- begin heading_1 3e6083ae-f894-4326-a0fa-9c83f7bc630e--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-3e6083ae-f894-4326-a0fa-9c83f7bc630e&quot;&gt;Qu’est-ce que zio-spark apporte en plus ?&lt;/h2&gt;
&lt;!-- end heading_1 3e6083ae-f894-4326-a0fa-9c83f7bc630e--&gt;
&lt;!-- begin paragraph 66c2e6cb-55b3-496b-a5ea-2f5e8dfef62e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-66c2e6cb-55b3-496b-a5ea-2f5e8dfef62e&quot;&gt;Avant que zio-spark existe, de nombreux ingénieurs ont déjà utilisé Spark et ZIO ensemble pour jouir des caractéristiques citées ci-dessus. Nous étions aussi dans ce cas et c’est pour cela que nous avons eu envie d’aller un peu plus loin afin d’avoir une intégration plus intelligente et plus transparente.&lt;/p&gt;
&lt;!-- end paragraph 66c2e6cb-55b3-496b-a5ea-2f5e8dfef62e--&gt;
&lt;!-- begin heading_2 64222fb9-db83-4026-87b4-04577f208deb--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-64222fb9-db83-4026-87b4-04577f208deb&quot;&gt;Une API plus plaisante et safe&lt;/h3&gt;
&lt;!-- end heading_2 64222fb9-db83-4026-87b4-04577f208deb--&gt;
&lt;!-- begin paragraph 5b32bc9a-9560-4f7b-a912-c376042b6643--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5b32bc9a-9560-4f7b-a912-c376042b6643&quot;&gt;Le principal objectif de zio-spark est de wrapper l’api spark pour l’adapter à ZIO. Pour comprendre la différence,  le mieux est de regarder du code avec zio-spark et sans zio-spark compatible avec Zio.&lt;/p&gt;
&lt;!-- end paragraph 5b32bc9a-9560-4f7b-a912-c376042b6643--&gt;
&lt;!-- begin paragraph caac3ca1-eeb5-411a-8555-5323ade52d01--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-caac3ca1-eeb5-411a-8555-5323ade52d01&quot;&gt;Sans Zio-spark compatible avec Zio :&lt;/p&gt;
&lt;!-- end paragraph caac3ca1-eeb5-411a-8555-5323ade52d01--&gt;
&lt;!-- begin code e0a4447f-ad86-4bbb-b7ae-443f7e5c7799--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e0a4447f-ad86-4bbb-b7ae-443f7e5c7799&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import org.apache.spark.sql._

val sparksession = SparkSession.builder().master(&amp;quot;local[1]&amp;quot;).getOrCreate()
val dataframe    = ZIO.attempt(sparksession.read.csv(&amp;quot;&amp;quot;))
val result       = dataframe.flatMap(df =&amp;gt; ZIO.attempt(df.count()))

val app = result&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e0a4447f-ad86-4bbb-b7ae-443f7e5c7799--&gt;
&lt;!-- begin paragraph ddf8891b-e269-4947-a97a-5dba5bdedad7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddf8891b-e269-4947-a97a-5dba5bdedad7&quot;&gt;Avec zio-spark :&lt;/p&gt;
&lt;!-- end paragraph ddf8891b-e269-4947-a97a-5dba5bdedad7--&gt;
&lt;!-- begin code d49fd7b6-1345-4d8f-b0eb-6f71c9ccbd1c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d49fd7b6-1345-4d8f-b0eb-6f71c9ccbd1c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio.spark.sql._

val sparksession = SparkSession.builder.master(&amp;quot;local[1]&amp;quot;).asLayer
val dataframe    = SparkSession.read.csv(&amp;quot;&amp;quot;)
val result       = dataframe.flatMap(_.count)

val app = result.provide(sparksession)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d49fd7b6-1345-4d8f-b0eb-6f71c9ccbd1c--&gt;
&lt;!-- begin paragraph e46ada5e-0ab3-412f-8a32-fd220851c0a5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e46ada5e-0ab3-412f-8a32-fd220851c0a5&quot;&gt;Avec cet exemple, on peut noter plusieurs changements :&lt;/p&gt;
&lt;!-- end paragraph e46ada5e-0ab3-412f-8a32-fd220851c0a5--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;zio-spark utilise le système de &lt;a href=&quot;https://zio.dev/reference/contextual/zlayer/&quot;&gt;Layer&lt;/a&gt; de ZIO pour passer la spark session. Cela a plusieurs avantages. Le premier étant que l’utilisateur n’a pas besoin de passer la spark session manuellement tout le long de son implication, il peut juste assumer qu’à la fin du monde une spark session sera donné et ne plus y penser. Cela permet aussi de scoper la spark session. Ainsi, en cas de crash ou autre, la spark session se ferme automatiquement.&lt;/li&gt;&lt;li&gt;zio-spark fait en sorte que toutes les actions soient automatiquement des effets. Ainsi, vous n’avez pas besoin de penser à ce qu’est ou non une action. Il suffit de regarder le type de retour pour s’en rendre compte. Cela rend aussi le code beaucoup plus lisible.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 3e67c5bf-90e7-4a00-b085-ba6f8016745e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3e67c5bf-90e7-4a00-b085-ba6f8016745e&quot;&gt;Une intégration pensée pour ZIO&lt;/h3&gt;
&lt;!-- end heading_2 3e67c5bf-90e7-4a00-b085-ba6f8016745e--&gt;
&lt;!-- begin paragraph 8cabce02-bfdc-46bb-8687-9b0c2c83a4f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8cabce02-bfdc-46bb-8687-9b0c2c83a4f6&quot;&gt;zio-spark essaye d’intégrer au maximum les caractéristiques de ZIO dans Spark. Nous avons déjà vu l’utilisation de layer pour la spark session, mais ça ne s’arrête pas là. &lt;/p&gt;
&lt;!-- end paragraph 8cabce02-bfdc-46bb-8687-9b0c2c83a4f6--&gt;
&lt;!-- begin paragraph 25f116ba-c7ce-4200-aa48-5e70d3a15035--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-25f116ba-c7ce-4200-aa48-5e70d3a15035&quot;&gt;À titre d’exemple, la fonction &lt;code&gt;printSchema&lt;/code&gt; utilise la &lt;a href=&quot;https://zio.dev/reference/services/console/&quot;&gt;Console&lt;/a&gt; de ZIO et non &lt;code&gt;print&lt;/code&gt; de Scala. Cela permet d’utiliser l’output dans nos tests. Voici un exemple de test rendu possible grâce à ce mécanisme (cela aurait aussi été possible avec Spark mais il aurait fallu faire un mock pour y arriver)  :&lt;/p&gt;
&lt;!-- end paragraph 25f116ba-c7ce-4200-aa48-5e70d3a15035--&gt;
&lt;!-- begin code f0c8671a-6c1d-4010-a20a-96f889cdea19--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f0c8671a-6c1d-4010-a20a-96f889cdea19&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import zio.test._
import zio.test.TestAspect._
import zio.spark.sql._

test(&amp;quot;Dataset should have the correct schema&amp;quot;) {
  val result =
    &amp;quot;&amp;quot;&amp;quot;root
      &amp;gt; |-- name: string (nullable = true)
      &amp;gt; |-- age: integer (nullable = true)&amp;quot;&amp;quot;&amp;quot;.stripMargin(&amp;#39;&amp;gt;&amp;#39;)

  for {
    df     &amp;lt;- SparkSession.read.csv(&amp;quot;&amp;quot;)
    _      &amp;lt;- df.printSchema
    output &amp;lt;- TestConsole.output
    representation = output.mkString(&amp;quot;\n&amp;quot;)
  } yield assertTrue(representation.contains(result))
} @@ silent&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f0c8671a-6c1d-4010-a20a-96f889cdea19--&gt;
&lt;!-- begin paragraph 66996ebc-ca4e-42b8-97d6-2fb4b893bb4a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-66996ebc-ca4e-42b8-97d6-2fb4b893bb4a&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 66996ebc-ca4e-42b8-97d6-2fb4b893bb4a--&gt;
&lt;!-- begin paragraph 3ac11d01-49a0-4cc7-992a-6b214f4a298c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ac11d01-49a0-4cc7-992a-6b214f4a298c&quot;&gt;Nous avons aussi essayé de rendre les jobs Spark annulable. En effet, ZIO joue beaucoup sur la concurrence et nous voulions que ceci s’applique aussi aux jobs Spark. Malheureusement, ceci ne fonctionne pas par le simple fait de wrapper notre action Spark dans ZIO. Le cas échéant, la fiber contenant le job sera supprimée, mais le job au niveau des workers continuera d’être traité. Nous fournissons un moyen pour répondre à cette problématique et je vous invite à regarder &lt;a href=&quot;https://univalence.github.io/zio-spark/experimental/cancellable&quot;&gt;la documentation&lt;/a&gt; pour plus d’informations.&lt;/p&gt;
&lt;!-- end paragraph 3ac11d01-49a0-4cc7-992a-6b214f4a298c--&gt;
&lt;!-- begin heading_1 62870164-14ba-4415-87d1-9ba6e217ba97--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-62870164-14ba-4415-87d1-9ba6e217ba97&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 62870164-14ba-4415-87d1-9ba6e217ba97--&gt;
&lt;!-- begin paragraph 09fe8564-54c3-4f43-82ac-ac9fd4e2243a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-09fe8564-54c3-4f43-82ac-ac9fd4e2243a&quot;&gt;J’espère, après lecture de cette introduction, que vous vous sentez plus à l’aise avec cette librairie, pourquoi nous l’avons créé et ce qu’elle peut vous apporter dans votre quotidien. &lt;/p&gt;
&lt;!-- end paragraph 09fe8564-54c3-4f43-82ac-ac9fd4e2243a--&gt;
&lt;!-- begin paragraph 89190d20-eb04-47b4-b828-67d9ef40dccf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89190d20-eb04-47b4-b828-67d9ef40dccf&quot;&gt;Vous vous demandez peut-être comment tout cela fonctionne sous le capot, pour cela, il faudra attendre le prochain article où nous expliquerons comment faire une librairie zio  (zio-spark, zio-notion, zio-aws, …) et où nous parlerons de code generation !&lt;/p&gt;
&lt;!-- end paragraph 89190d20-eb04-47b4-b828-67d9ef40dccf--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:5d812a2c073e48e0bc8220b08ecc27c5</id>
    <title>Configurer RouterOS comme client WireGuard</title>
    <updated>2022-12-21T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/configurer-routeros-comme-client-wireguard.html"/>
    <!--summary Dans cet article, nous allons découvrir comment configurer RouterOS comme client d’un serveur WireGuard. La configuration est enfantine.-->    <author>
      <name>Maxence Winandy</name>
      <uri>https://univalence.io/blog/auteurs/maxence-winandy.html</uri>
    </author>    <author>
      <name>Benjamin BRAYE</name>
      <uri>https://univalence.io/blog/auteurs/benjamin-braye.html</uri>
    </author>        <category term="RouterOS"></category>    <category term="WireGuard"></category>    <content type="html">
&lt;!-- begin callout 70822295-0018-4966-be95-d494908e2ecd--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-70822295-0018-4966-be95-d494908e2ecd&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;⚠️&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Les ressources de cet article ne sont pas supposées être utilisé en production, il y a une multitude de paramètres à affiner avant un passage en production.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout 70822295-0018-4966-be95-d494908e2ecd--&gt;
&lt;!-- begin paragraph 302ceb89-3411-4c3c-8cdb-ad9d45ff0aeb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-302ceb89-3411-4c3c-8cdb-ad9d45ff0aeb&quot;&gt;Cet article fait suite à &lt;a href=&quot;/2128222b24be4ed7a59c3eb897b5e401&quot;&gt;la configuration de RouterOS comme client OpenVPN&lt;/a&gt;, autant la configuration avec OpenVPN est fastidieuse que celle avec WireGuard est enfantine.&lt;/p&gt;
&lt;!-- end paragraph 302ceb89-3411-4c3c-8cdb-ad9d45ff0aeb--&gt;
&lt;!-- begin heading_1 27143cd0-55e9-47e8-8f6b-d648b6aa512a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-27143cd0-55e9-47e8-8f6b-d648b6aa512a&quot;&gt;Pré-requis&lt;/h2&gt;
&lt;!-- end heading_1 27143cd0-55e9-47e8-8f6b-d648b6aa512a--&gt;
&lt;!-- begin paragraph 7c1fa2f8-9d26-4c93-98f9-65432cfa4e39--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7c1fa2f8-9d26-4c93-98f9-65432cfa4e39&quot;&gt;Pour réaliser les étapes suivantes, nous aurons besoins de :&lt;/p&gt;
&lt;!-- end paragraph 7c1fa2f8-9d26-4c93-98f9-65432cfa4e39--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;RouterOS 7.7&lt;/li&gt;&lt;li&gt;WireGuard&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 21889d92-93df-4982-958f-d3125f6e6325--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-21889d92-93df-4982-958f-d3125f6e6325&quot;&gt;En dehors de ces versions, vous devrez peut-être adapter les instructions.&lt;/p&gt;
&lt;!-- end paragraph 21889d92-93df-4982-958f-d3125f6e6325--&gt;
&lt;!-- begin heading_1 c0fbb4d6-37eb-46dc-9d08-7d69f8450bac--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c0fbb4d6-37eb-46dc-9d08-7d69f8450bac&quot;&gt;Obtenir une configuration cliente&lt;/h2&gt;
&lt;!-- end heading_1 c0fbb4d6-37eb-46dc-9d08-7d69f8450bac--&gt;
&lt;!-- begin paragraph 444e55d7-9ee8-4fd0-b9ce-dae6f479a151--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-444e55d7-9ee8-4fd0-b9ce-dae6f479a151&quot;&gt;La distribution d’une configuration WireGuard se fait régulièrement sous forme de QR Code.&lt;/p&gt;
&lt;!-- end paragraph 444e55d7-9ee8-4fd0-b9ce-dae6f479a151--&gt;
&lt;!-- begin paragraph 2222f75b-7b15-459f-9033-556bb4489105--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2222f75b-7b15-459f-9033-556bb4489105&quot;&gt;Dans notre cas, nous avons besoin d’exploiter le contenu du fichier de configuration client, donc il faudra d’abord le &lt;a href=&quot;https://univalence.io/blog/articles/decoder-le-passe-sanitaire/#h-cea81f0a-c4d6-4590-86f5-ef09011597a5&quot;&gt;décoder&lt;/a&gt; si l’on n&amp;#39;a que le QR Code.&lt;/p&gt;
&lt;!-- end paragraph 2222f75b-7b15-459f-9033-556bb4489105--&gt;
&lt;!-- begin code f7fefa02-806a-4067-b4ce-614e070a6eb5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f7fefa02-806a-4067-b4ce-614e070a6eb5&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;[Interface]
PrivateKey = *REDACTED*
Address = 192.168.27.90/32
DNS = 8.8.8.8
MTU = 1360

[Peer]
PublicKey = *REDACTED*
Endpoint = wireguard.server.io:13231
AllowedIPs = 192.168.27.64/27, 192.168.0.0/24
PersistentKeepalive = 10
PresharedKey = *REDACTED*&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f7fefa02-806a-4067-b4ce-614e070a6eb5--&gt;
&lt;!-- begin heading_1 da0ddbed-687a-48eb-b163-d8062608e453--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-da0ddbed-687a-48eb-b163-d8062608e453&quot;&gt;Configurer WireGuard dans RouterOS&lt;/h2&gt;
&lt;!-- end heading_1 da0ddbed-687a-48eb-b163-d8062608e453--&gt;
&lt;!-- begin heading_2 de2f6bb4-dd0e-409b-9e89-05b64a8a7b2e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-de2f6bb4-dd0e-409b-9e89-05b64a8a7b2e&quot;&gt;Avec Winbox&lt;/h3&gt;
&lt;!-- end heading_2 de2f6bb4-dd0e-409b-9e89-05b64a8a7b2e--&gt;
&lt;!-- begin heading_3 a1c7fb9f-461a-4327-b933-25b36139b98f--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-a1c7fb9f-461a-4327-b933-25b36139b98f&quot;&gt;Ajouter l’interface WireGuard dans RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 a1c7fb9f-461a-4327-b933-25b36139b98f--&gt;
&lt;!-- begin paragraph ec2c2628-c857-4970-ae0a-ea0e61736d59--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec2c2628-c857-4970-ae0a-ea0e61736d59&quot;&gt;Dans le menu &lt;code&gt;WireGuard&lt;/code&gt; à gauche, ajoutons une interface comme ce qui suit.&lt;/p&gt;
&lt;!-- end paragraph ec2c2628-c857-4970-ae0a-ea0e61736d59--&gt;
&lt;!-- begin paragraph 57a33d27-1ef0-489b-963a-244141f3cb8e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-57a33d27-1ef0-489b-963a-244141f3cb8e&quot;&gt;Dans le volet &lt;code&gt;General&lt;/code&gt;, définissons le nom de l’interface, ainsi que le &lt;code&gt;MTU&lt;/code&gt;, &lt;code&gt;Listen Port&lt;/code&gt; et la &lt;code&gt;Private Key&lt;/code&gt; tel qu’ils sont indiqués dans le fichier de configuration.&lt;/p&gt;
&lt;!-- end paragraph 57a33d27-1ef0-489b-963a-244141f3cb8e--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5d812a2c-073e-48e0-bc82-20b08ecc27c5/winbox_1.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 a95cf42a-0167-41b2-860f-a8fb1411d08d--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-a95cf42a-0167-41b2-860f-a8fb1411d08d&quot;&gt;Ajouter le &lt;code&gt;Peer&lt;/code&gt;dans RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 a95cf42a-0167-41b2-860f-a8fb1411d08d--&gt;
&lt;!-- begin paragraph 42160300-b6a1-4750-9109-9c730354cede--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42160300-b6a1-4750-9109-9c730354cede&quot;&gt;Toujours dans la fenêtre WireGuard, dans le volet &lt;code&gt;Peers&lt;/code&gt;, ajoutons un nouveau &lt;code&gt;Peer&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 42160300-b6a1-4750-9109-9c730354cede--&gt;
&lt;!-- begin paragraph 5d01b319-cc67-456b-9be2-b8def0a68b4b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d01b319-cc67-456b-9be2-b8def0a68b4b&quot;&gt;Sélectionnons l’interface WireGuard créée juste avant et indiquons la &lt;code&gt;Public Key&lt;/code&gt; du fichier de configuration, indiquons de même le &lt;code&gt;Endpoint&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 5d01b319-cc67-456b-9be2-b8def0a68b4b--&gt;
&lt;!-- begin paragraph 81696781-7ecf-4797-a9b1-13bd635e493f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81696781-7ecf-4797-a9b1-13bd635e493f&quot;&gt;Pour chaque &lt;code&gt;AllowedIPs&lt;/code&gt;du fichier, mettre une des IP autorisée à transiter par le VPN, chaque ligne est une adresse CIDR unique et la flèche vers le bas permet d’en ajouter d’autres.&lt;/p&gt;
&lt;!-- end paragraph 81696781-7ecf-4797-a9b1-13bd635e493f--&gt;
&lt;!-- begin paragraph 7827f378-b324-4692-a3fc-b5d98b5d717b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7827f378-b324-4692-a3fc-b5d98b5d717b&quot;&gt;Indiquons la &lt;code&gt;Preshared key&lt;/code&gt; puisque le serveur en fournie une.&lt;/p&gt;
&lt;!-- end paragraph 7827f378-b324-4692-a3fc-b5d98b5d717b--&gt;
&lt;!-- begin paragraph 84b2217a-4179-455e-a4e4-6875ac19bd4a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-84b2217a-4179-455e-a4e4-6875ac19bd4a&quot;&gt;Il faut aussi reporter la valeur de maintien de connexion, &lt;code&gt;Persistent Keepalive&lt;/code&gt;, exprimée en secondes dans le fichier de configuration et à indiquer dans la fenêtre.&lt;/p&gt;
&lt;!-- end paragraph 84b2217a-4179-455e-a4e4-6875ac19bd4a--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5d812a2c-073e-48e0-bc82-20b08ecc27c5/winbox_3.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 16dac730-25e1-40c2-910d-53af468a786e--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-16dac730-25e1-40c2-910d-53af468a786e&quot;&gt;Ajouter l’adresse dans RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 16dac730-25e1-40c2-910d-53af468a786e--&gt;
&lt;!-- begin paragraph 8bbfd3d4-0042-42f9-9ece-fcc4a753a713--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8bbfd3d4-0042-42f9-9ece-fcc4a753a713&quot;&gt;Dans le menu &lt;code&gt;IP&lt;/code&gt;, &lt;code&gt;Addresses&lt;/code&gt;, ajoutons l’IP telle qu’indiquée dans le fichier de configuration et le segment CIDR ici. Dans mon exemple, le client à l’ip &lt;code&gt;192.168.27.90&lt;/code&gt; et le masque &lt;code&gt;/27&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 8bbfd3d4-0042-42f9-9ece-fcc4a753a713--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5d812a2c-073e-48e0-bc82-20b08ecc27c5/winbox_2.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph ddb9feb6-cf0c-403c-a66e-6a2fbdd0773d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddb9feb6-cf0c-403c-a66e-6a2fbdd0773d&quot;&gt;Et z’est parti 🎉&lt;/p&gt;
&lt;!-- end paragraph ddb9feb6-cf0c-403c-a66e-6a2fbdd0773d--&gt;
&lt;!-- begin callout 6b8f7fff-1c50-4097-8d3c-2cf72542fc95--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-6b8f7fff-1c50-4097-8d3c-2cf72542fc95&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;📃&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Si pour raison inconnue, cela ne fonctionne pas, regardez les logs de RouterOS et ceux du serveur WireGuard.&lt;br /&gt;
Il suffit normalement de reporter toutes les valeurs du fichier de configuration client que vous avez.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout 6b8f7fff-1c50-4097-8d3c-2cf72542fc95--&gt;
&lt;!-- begin heading_2 943fdd3a-e7d2-487b-b8a8-b2ac867879b4--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-943fdd3a-e7d2-487b-b8a8-b2ac867879b4&quot;&gt;Avec la console&lt;/h3&gt;
&lt;!-- end heading_2 943fdd3a-e7d2-487b-b8a8-b2ac867879b4--&gt;
&lt;!-- begin paragraph 4eadfb3e-8dfc-4805-af5c-17af37759a2b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4eadfb3e-8dfc-4805-af5c-17af37759a2b&quot;&gt;Voici la version console des étapes ci-dessus.&lt;/p&gt;
&lt;!-- end paragraph 4eadfb3e-8dfc-4805-af5c-17af37759a2b--&gt;
&lt;!-- begin paragraph 1292b627-8d29-43eb-b23d-c2907d3c4982--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1292b627-8d29-43eb-b23d-c2907d3c4982&quot;&gt;Ces étapes impliquent que nous ayons accès à la console de RouterOS.&lt;/p&gt;
&lt;!-- end paragraph 1292b627-8d29-43eb-b23d-c2907d3c4982--&gt;
&lt;!-- begin heading_3 1e660cf6-ac44-4ffe-837e-f4b7041a6549--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-1e660cf6-ac44-4ffe-837e-f4b7041a6549&quot;&gt;Ajouter l’interface WireGuard dans RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 1e660cf6-ac44-4ffe-837e-f4b7041a6549--&gt;
&lt;!-- begin code 8e1a1f0d-9540-48ba-a71d-9ebd9ea81904--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8e1a1f0d-9540-48ba-a71d-9ebd9ea81904&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;/interface/wireguard/add \
    name=wireguard_interface \
    listen-port=13231 \
    mtu=1360 \
    private-key=&amp;quot;**REDACTED**&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8e1a1f0d-9540-48ba-a71d-9ebd9ea81904--&gt;
&lt;!-- begin heading_3 cc60e599-272b-4948-bf96-7096bdb5ac2c--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-cc60e599-272b-4948-bf96-7096bdb5ac2c&quot;&gt;Ajouter le &lt;code&gt;Peer&lt;/code&gt;dans RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 cc60e599-272b-4948-bf96-7096bdb5ac2c--&gt;
&lt;!-- begin code 0dea0a9a-a19c-4733-923f-3147e2ceb750--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0dea0a9a-a19c-4733-923f-3147e2ceb750&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;/interface/wireguard/peers/add \
    allowed-address=192.168.167.0/24,0.0.0.0/0 \
    endpoint-address=wireguard.server.io \
    endpoint-port=13231 \
    interface=wireguard_interface \
    persistent-keepalive=10s \
    preshared-key=&amp;quot;**REDACTED**&amp;quot; \
    public-key=&amp;quot;**REDACTED**&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0dea0a9a-a19c-4733-923f-3147e2ceb750--&gt;
&lt;!-- begin heading_3 f95f29a6-d11e-439f-a883-ecc5473e2f75--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-f95f29a6-d11e-439f-a883-ecc5473e2f75&quot;&gt;Ajouter l’adresse dans RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 f95f29a6-d11e-439f-a883-ecc5473e2f75--&gt;
&lt;!-- begin code cbddf8d7-25c5-4351-8fa8-6598432210a2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-cbddf8d7-25c5-4351-8fa8-6598432210a2&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;/ip/address/add \
    address=192.168.27.90/27 \
    interface=wireguard_interface&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code cbddf8d7-25c5-4351-8fa8-6598432210a2--&gt;
&lt;!-- begin paragraph bc837cc5-e283-4019-a4ad-3d4c3f5af2f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bc837cc5-e283-4019-a4ad-3d4c3f5af2f9&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph bc837cc5-e283-4019-a4ad-3d4c3f5af2f9--&gt;
&lt;!-- begin paragraph 8de0f227-aa1f-4171-b4b8-a21aeb2036f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8de0f227-aa1f-4171-b4b8-a21aeb2036f6&quot;&gt;Et z’est parti 🎉&lt;/p&gt;
&lt;!-- end paragraph 8de0f227-aa1f-4171-b4b8-a21aeb2036f6--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:2128222b24be4ed7a59c3eb897b5e401</id>
    <title>Configurer RouterOS comme client OpenVPN</title>
    <updated>2022-12-21T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/configurer-routeros-comme-client-openvpn.html"/>
    <!--summary Découvrons comment ajouter une interface OpenVPN-Client dans RouterOS. La configuration n’est pas aussi simple que d’importer un fichier client dans OpenVPN Connect. On doit extraire les différents certificats, déchiffrer la clé et importer le tout dans RouterOS.-->    <author>
      <name>Maxence Winandy</name>
      <uri>https://univalence.io/blog/auteurs/maxence-winandy.html</uri>
    </author>    <author>
      <name>Benjamin BRAYE</name>
      <uri>https://univalence.io/blog/auteurs/benjamin-braye.html</uri>
    </author>        <category term="OpenVPN"></category>    <category term="RouterOS"></category>    <category term="TLS"></category>    <category term="SSL"></category>    <content type="html">
&lt;!-- begin paragraph 966c5c90-ae53-4ff2-b7a9-5bd7911f5fc7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-966c5c90-ae53-4ff2-b7a9-5bd7911f5fc7&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 966c5c90-ae53-4ff2-b7a9-5bd7911f5fc7--&gt;
&lt;!-- begin callout b805ac72-9832-41ae-b80f-924f89946da7--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-b805ac72-9832-41ae-b80f-924f89946da7&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;⚠️&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Les ressources de cet article ne sont pas supposées être utilisé en production, il y a une multitude de paramètres à affiner avant un passage en production.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout b805ac72-9832-41ae-b80f-924f89946da7--&gt;
&lt;!-- begin heading_1 5667e1d2-ba57-4458-872c-79a2c4f07a8d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5667e1d2-ba57-4458-872c-79a2c4f07a8d&quot;&gt;Pré-requis&lt;/h2&gt;
&lt;!-- end heading_1 5667e1d2-ba57-4458-872c-79a2c4f07a8d--&gt;
&lt;!-- begin paragraph 69acd59b-64ca-46d5-994e-e0e56b59706a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69acd59b-64ca-46d5-994e-e0e56b59706a&quot;&gt;Pour réaliser les étapes suivantes, nous aurons besoins de :&lt;/p&gt;
&lt;!-- end paragraph 69acd59b-64ca-46d5-994e-e0e56b59706a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;RouterOS 7.7&lt;/li&gt;&lt;li&gt;OpenVPN 2.4.7&lt;/li&gt;&lt;li&gt;OpenSSL/LibreSSL 3.3.6&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 478e93e2-f3b6-4c03-880b-6fb8b7f44522--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-478e93e2-f3b6-4c03-880b-6fb8b7f44522&quot;&gt;En dehors de ces versions, vous devrez peut-être adapter les instructions.&lt;/p&gt;
&lt;!-- end paragraph 478e93e2-f3b6-4c03-880b-6fb8b7f44522--&gt;
&lt;!-- begin heading_2 f4769373-51c3-465b-952c-0f1d54a5eb76--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f4769373-51c3-465b-952c-0f1d54a5eb76&quot;&gt;Configuration requise du serveur OpenVPN&lt;/h3&gt;
&lt;!-- end heading_2 f4769373-51c3-465b-952c-0f1d54a5eb76--&gt;
&lt;!-- begin paragraph a767da27-92c4-44d7-8923-a49988d3827c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a767da27-92c4-44d7-8923-a49988d3827c&quot;&gt;À l’heure de la version &lt;code&gt;7.7rc2&lt;/code&gt; de RouterOS, celle-ci ne supporte pas encore &lt;code&gt;tls-auth&lt;/code&gt;, donc dans la configuration du serveur le paramètre doit être absent ou commenté avec &lt;code&gt;#&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph a767da27-92c4-44d7-8923-a49988d3827c--&gt;
&lt;!-- begin paragraph 8b0a6aa5-7a8e-45da-af6b-485da5354182--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8b0a6aa5-7a8e-45da-af6b-485da5354182&quot;&gt;De même, RouterOS ne supporte pas la compression LZO, ainsi dans la configuration serveur, commenter &lt;code&gt;comp-lzo no&lt;/code&gt; et ajouter &lt;code&gt;push &amp;quot;comp-lzo no&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 8b0a6aa5-7a8e-45da-af6b-485da5354182--&gt;
&lt;!-- begin paragraph 9e46d85c-1518-4013-a4e3-e6b1a47cf49d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e46d85c-1518-4013-a4e3-e6b1a47cf49d&quot;&gt;Sur &lt;code&gt;7.7rc2&lt;/code&gt;, nous avons le support de la connectivité UDP.&lt;/p&gt;
&lt;!-- end paragraph 9e46d85c-1518-4013-a4e3-e6b1a47cf49d--&gt;
&lt;!-- begin paragraph 61df72bf-5af6-453b-9c58-02444696fe57--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-61df72bf-5af6-453b-9c58-02444696fe57&quot;&gt;Voici un exemple de la configuration serveur.&lt;/p&gt;
&lt;!-- end paragraph 61df72bf-5af6-453b-9c58-02444696fe57--&gt;
&lt;!-- begin paragraph 1ac77aac-6497-4cfe-a385-b977459e548a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1ac77aac-6497-4cfe-a385-b977459e548a&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1ac77aac-6497-4cfe-a385-b977459e548a--&gt;
&lt;!-- begin code 41145efb-713a-4834-becf-beb53c402913--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-41145efb-713a-4834-becf-beb53c402913&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;server 192.168.255.0 255.255.255.0
verb 3
key /etc/openvpn/pki/private/openvpn.server.io.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/openvpn.server.iovfgb.crt
dh /etc/openvpn/pki/dh.pem
#tls-auth /etc/openvpn/pki/ta.key
key-direction 0
keepalive 10 60
persist-key
persist-tun

cipher AES-256-CBC
auth sha1

proto udp4
port 1194
dev tun0
status /tmp/openvpn-status.log

user nobody
group nogroup
#comp-lzo no

### Route Configurations Below
route 192.168.254.0 255.255.255.0

### Push Configurations Below
push &amp;quot;block-outside-dns&amp;quot;
push &amp;quot;dhcp-option DNS 8.8.8.8&amp;quot;
push &amp;quot;dhcp-option DNS 8.8.4.4&amp;quot;
push &amp;quot;comp-lzo no&amp;quot;
push &amp;quot;compress stub-v2&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 41145efb-713a-4834-becf-beb53c402913--&gt;
&lt;!-- begin paragraph aeac358d-f17d-46be-ae12-c6c2eb2e3463--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aeac358d-f17d-46be-ae12-c6c2eb2e3463&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph aeac358d-f17d-46be-ae12-c6c2eb2e3463--&gt;
&lt;!-- begin heading_1 fe252d16-b306-4ad8-a78f-ed32b577067d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-fe252d16-b306-4ad8-a78f-ed32b577067d&quot;&gt;Obtenir un fichier &lt;code&gt;client.ovpn&lt;/code&gt;&lt;/h2&gt;
&lt;!-- end heading_1 fe252d16-b306-4ad8-a78f-ed32b577067d--&gt;
&lt;!-- begin paragraph 9deabbea-3d8d-48c8-9055-cf60bbcde973--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9deabbea-3d8d-48c8-9055-cf60bbcde973&quot;&gt;Selon le serveur, le service ainsi que les choix techniques, il existe beaucoup de méthodes pour obtenir un fichier client OpenVPN.&lt;/p&gt;
&lt;!-- end paragraph 9deabbea-3d8d-48c8-9055-cf60bbcde973--&gt;
&lt;!-- begin paragraph 8a56edab-aae1-45e1-8bb1-d1627bffaee5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8a56edab-aae1-45e1-8bb1-d1627bffaee5&quot;&gt;Pour les besoins de cet article, nous avons utilisé &lt;a href=&quot;https://pivpn.io/&quot;&gt;PiVPN&lt;/a&gt; afin de créer la configuration du serveur de même que celle des clients.&lt;/p&gt;
&lt;!-- end paragraph 8a56edab-aae1-45e1-8bb1-d1627bffaee5--&gt;
&lt;!-- begin paragraph 89f75aba-cac2-48b1-8d77-52000d87c388--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89f75aba-cac2-48b1-8d77-52000d87c388&quot;&gt;Par exemple, il existe aussi l’image docker &lt;a href=&quot;https://hub.docker.com/r/kylemanna/openvpn/&quot;&gt;🐳 kylemanna/openvpn&lt;/a&gt; qui permet rapidement de mettre en place un serveur.&lt;/p&gt;
&lt;!-- end paragraph 89f75aba-cac2-48b1-8d77-52000d87c388--&gt;
&lt;!-- begin paragraph b3e3ae83-e08f-4204-9708-3980738c2e34--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3e3ae83-e08f-4204-9708-3980738c2e34&quot;&gt;Une fois le fichier &lt;code&gt;.ovpn&lt;/code&gt; récupéré (ou les différents fichiers), nous allons extraire les informations nécessaires à la configuration de RouterOS.&lt;/p&gt;
&lt;!-- end paragraph b3e3ae83-e08f-4204-9708-3980738c2e34--&gt;
&lt;!-- begin heading_1 57653ecc-1e1b-4c5e-9269-ac9092f4c094--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-57653ecc-1e1b-4c5e-9269-ac9092f4c094&quot;&gt;Préparer les certificats pour RouterOS&lt;/h2&gt;
&lt;!-- end heading_1 57653ecc-1e1b-4c5e-9269-ac9092f4c094--&gt;
&lt;!-- begin paragraph e2ff3cee-af7d-4c89-a35b-2094a6c93738--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2ff3cee-af7d-4c89-a35b-2094a6c93738&quot;&gt;Notre fichier utilisateur ressemble à ça,&lt;/p&gt;
&lt;!-- end paragraph e2ff3cee-af7d-4c89-a35b-2094a6c93738--&gt;
&lt;!-- begin code 27e99778-4981-4ae6-a4e9-4bdd247cd9df--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-27e99778-4981-4ae6-a4e9-4bdd247cd9df&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;client
nobind

#ROS: Mode ip
dev tun

remote-cert-tls server

#ROS: Connect To / Port / Protocol
remote openvpn.server.io 1194 udp

#ROS: Clé privée
&amp;lt;key&amp;gt;
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIqCo7o53TV4YCAggA
**REDACTED**
1IyGuqUkRXJd2HY1SC3i3w==
-----END ENCRYPTED PRIVATE KEY-----
&amp;lt;/key&amp;gt;

#ROS: Certificat client
&amp;lt;cert&amp;gt;
-----BEGIN CERTIFICATE-----
MIIDVzCCAj+gAwIBAgIRAPVtgQ89n79UIpllih8JZjUwDQYJKoZIhvcNAQELBQAw
**REDACTED**
4iasQhOPIRyN4vIMIU2IRLuycLS29OFiKo0dB3EOa/c0zZk7683LnxKv/Q==
-----END CERTIFICATE-----
&amp;lt;/cert&amp;gt;

#ROS: Certificat de l&amp;#39;autorité de certification
&amp;lt;ca&amp;gt;
-----BEGIN CERTIFICATE-----
MIIDSzCCAjOgAwIBAgIUXb0Dr/Lrq6q3mhdgzUfNAoCmhjswDQYJKoZIhvcNAQEL
**REDACTED**
4JqvkASv/EK0BkLec83s4Y1Pcppx+h+5YeTz2/+T4g==
-----END CERTIFICATE-----
&amp;lt;/ca&amp;gt;
key-direction 1
&amp;lt;tls-auth&amp;gt;
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
d4b81257b426595a5d3faaf040a362ae
**REDACTED**
90863f9cdf9e1eb6d3815f6443a6383c
-----END OpenVPN Static key V1-----
&amp;lt;/tls-auth&amp;gt;

redirect-gateway def1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 27e99778-4981-4ae6-a4e9-4bdd247cd9df--&gt;
&lt;!-- begin heading_2 2518390e-5c2f-485c-b613-9104f9222777--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-2518390e-5c2f-485c-b613-9104f9222777&quot;&gt;Extraire les certificats et la clé&lt;/h3&gt;
&lt;!-- end heading_2 2518390e-5c2f-485c-b613-9104f9222777--&gt;
&lt;!-- begin paragraph f3dbaf88-cc3e-40cf-af9c-6ef419b8857b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f3dbaf88-cc3e-40cf-af9c-6ef419b8857b&quot;&gt;Nous allons extraire le certificat du serveur, le certificat client et la clé.&lt;/p&gt;
&lt;!-- end paragraph f3dbaf88-cc3e-40cf-af9c-6ef419b8857b--&gt;
&lt;!-- begin paragraph ecff39a3-4440-48ad-9c3e-3cba423be2c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ecff39a3-4440-48ad-9c3e-3cba423be2c0&quot;&gt;La clé arrive par défaut sous sa forme chiffrée dans la configuration.&lt;/p&gt;
&lt;!-- end paragraph ecff39a3-4440-48ad-9c3e-3cba423be2c0--&gt;
&lt;!-- begin paragraph eead91e1-baa1-46a6-8c1f-de7db0e013e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eead91e1-baa1-46a6-8c1f-de7db0e013e1&quot;&gt;C’est la data qui se trouve entre &lt;code&gt;&amp;lt;key&amp;gt;…&amp;lt;/key&amp;gt;&lt;/code&gt;, RouterOS échouera à la lire directement, donc nous allons devoir la convertir avant son import.&lt;/p&gt;
&lt;!-- end paragraph eead91e1-baa1-46a6-8c1f-de7db0e013e1--&gt;
&lt;!-- begin paragraph ad601d73-9584-4b69-bdef-27709388c6c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ad601d73-9584-4b69-bdef-27709388c6c1&quot;&gt;Le certificat client entre&lt;code&gt;&amp;lt;cert&amp;gt;…&amp;lt;/cert&amp;gt;&lt;/code&gt;, et le certificat de l’autorité de certification &lt;code&gt;&amp;lt;ca&amp;gt;…&amp;lt;⁣/ca&amp;gt;&lt;/code&gt; sont à extraire également.&lt;/p&gt;
&lt;!-- end paragraph ad601d73-9584-4b69-bdef-27709388c6c1--&gt;
&lt;!-- begin paragraph c75ac78a-db1f-4dca-87e7-ece6d172a3c8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c75ac78a-db1f-4dca-87e7-ece6d172a3c8&quot;&gt;On peut ouvrir le fichier avec un éditeur de texte et copier-coller les différentes sections dans les fichiers &lt;code&gt;ca.pem&lt;/code&gt;, &lt;code&gt;client.pem&lt;/code&gt;, &lt;code&gt;client.pem&lt;/code&gt; ou en shell avec &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph c75ac78a-db1f-4dca-87e7-ece6d172a3c8--&gt;
&lt;!-- begin code 645cf633-2766-4fae-97b0-fa677eb17ce0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-645cf633-2766-4fae-97b0-fa677eb17ce0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;grep -Poz &amp;#39;(?s)\K&amp;lt;key&amp;gt;(.*)&amp;lt;/key&amp;gt;?&amp;#39; client.ovpn | sed -E &amp;#39;/&amp;lt;?key&amp;gt;/d&amp;#39; &amp;gt; client.key
grep -Poz &amp;#39;(?s)\K&amp;lt;cert&amp;gt;(.*)&amp;lt;/cert&amp;gt;?&amp;#39; client.ovpn | sed -E &amp;#39;/&amp;lt;?cert&amp;gt;/d&amp;#39; &amp;gt; client.pem
grep -Poz &amp;#39;(?s)\K&amp;lt;ca&amp;gt;(.*)&amp;lt;/ca&amp;gt;?&amp;#39; client.ovpn | sed -E &amp;#39;/&amp;lt;?ca&amp;gt;/d&amp;#39; &amp;gt; ca.pem&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 645cf633-2766-4fae-97b0-fa677eb17ce0--&gt;
&lt;!-- begin callout 41c09d98-bd28-4d30-bd79-fb7bd9d6e4ca--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-41c09d98-bd28-4d30-bd79-fb7bd9d6e4ca&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;🍎&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Les opérations réalisées avec le terminal ci-dessus utilisent la version gnu de grep, par défaut sous macOS indisponible, elle peut être installée avec &lt;code&gt;brew install grep&lt;/code&gt;.&lt;br /&gt;
Pour la compatibilité, les commandes sont préfixées avec &lt;code&gt;g&lt;/code&gt;, c&amp;#39;est pourquoi on doit utiliser &lt;code&gt;ggrep&lt;/code&gt; et non &lt;code&gt;grep&lt;/code&gt; avec macOS.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout 41c09d98-bd28-4d30-bd79-fb7bd9d6e4ca--&gt;
&lt;!-- begin heading_2 6f94e95c-42c1-4b75-9171-a0fbf01beb73--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6f94e95c-42c1-4b75-9171-a0fbf01beb73&quot;&gt;Déchiffrer la clé&lt;/h3&gt;
&lt;!-- end heading_2 6f94e95c-42c1-4b75-9171-a0fbf01beb73--&gt;
&lt;!-- begin paragraph 7343a62d-99ad-48e0-9a1a-dd0bf8797ce1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7343a62d-99ad-48e0-9a1a-dd0bf8797ce1&quot;&gt;Maintenant, nous allons créer un fichier &lt;code&gt;pem&lt;/code&gt; qui va nous permettre d’importer le tout dans RouterOS, cependant, la clé est actuellement sous sa forme chiffrée donc nous allons la déchiffrer.&lt;/p&gt;
&lt;!-- end paragraph 7343a62d-99ad-48e0-9a1a-dd0bf8797ce1--&gt;
&lt;!-- begin code de908b9c-b4e4-4718-9d1e-c4d00fa15db5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-de908b9c-b4e4-4718-9d1e-c4d00fa15db5&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl rsa -in ./client.key -out ./client.key&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code de908b9c-b4e4-4718-9d1e-c4d00fa15db5--&gt;
&lt;!-- begin paragraph d0fea435-fa89-4a3f-b085-9a63277f246b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d0fea435-fa89-4a3f-b085-9a63277f246b&quot;&gt;Si tout est bon, la clé est déchiffrée. Dans le contenu&lt;/p&gt;
&lt;!-- end paragraph d0fea435-fa89-4a3f-b085-9a63277f246b--&gt;
&lt;!-- begin paragraph ff3ab754-addf-46bb-bc47-9d9e41e053d5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff3ab754-addf-46bb-bc47-9d9e41e053d5&quot;&gt;&lt;code&gt;----BEGIN ENCRYPTED PRIVATE KEY-----&lt;/code&gt; devient &lt;code&gt;----BEGIN RSA PRIVATE KEY-----&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph ff3ab754-addf-46bb-bc47-9d9e41e053d5--&gt;
&lt;!-- begin heading_2 dd5c9dec-c33a-4b49-bdfd-35be0737d5cd--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-dd5c9dec-c33a-4b49-bdfd-35be0737d5cd&quot;&gt;Création du fichier PEM&lt;/h3&gt;
&lt;!-- end heading_2 dd5c9dec-c33a-4b49-bdfd-35be0737d5cd--&gt;
&lt;!-- begin paragraph 9680cdac-5edd-4fdc-9a93-dbfb8db4f140--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9680cdac-5edd-4fdc-9a93-dbfb8db4f140&quot;&gt;À présent, créons le fichier &lt;code&gt;pem&lt;/code&gt; contenant le la clé, le certificat client et le certificat de l’autorité de certification.&lt;/p&gt;
&lt;!-- end paragraph 9680cdac-5edd-4fdc-9a93-dbfb8db4f140--&gt;
&lt;!-- begin code 415a27a8-9de1-4e76-a6fb-b4b04496ce0c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-415a27a8-9de1-4e76-a6fb-b4b04496ce0c&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;cat ca.pem client.pem client.key &amp;gt; certificate.pem&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 415a27a8-9de1-4e76-a6fb-b4b04496ce0c--&gt;
&lt;!-- begin paragraph eaca58f5-7fc8-460f-8948-45eaa3ef9a48--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eaca58f5-7fc8-460f-8948-45eaa3ef9a48&quot;&gt;Le certificat &lt;code&gt;client.pem&lt;/code&gt; ressemble à ça,&lt;/p&gt;
&lt;!-- end paragraph eaca58f5-7fc8-460f-8948-45eaa3ef9a48--&gt;
&lt;!-- begin code 94485d76-529d-4a07-96a6-d724267e6001--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-94485d76-529d-4a07-96a6-d724267e6001&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;-----BEGIN CERTIFICATE-----
MIIDSzCCAjOgAwIBAgIUXb0Dr/Lrq6q3mhdgzUfNAoCmhjswDQYJKoZIhvcNAQEL
**REDACTED**
4JqvkASv/EK0BkLec83s4Y1Pcppx+h+5YeTz2/+T4g==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDVzCCAj+gAwIBAgIRAPVtgQ89n79UIpllih8JZjUwDQYJKoZIhvcNAQELBQAw
**REDACTED**
4iasQhOPIRyN4vIMIU2IRLuycLS29OFiKo0dB3EOa/c0zZk7683LnxKv/Q==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA0WuDisr2CPUkBGSl09I+dg/aSdc2Hj4TRN4WcGRoq7I3Al+N
**REDACTED**
FZ4b5HGNV1kBgcIMmWC8+vrNAFEwawA0goYtcsri4SjcybEZRfU=
-----END RSA PRIVATE KEY-----&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 94485d76-529d-4a07-96a6-d724267e6001--&gt;
&lt;!-- begin paragraph 5700abc2-4ec6-4865-9742-d05fa77915a5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5700abc2-4ec6-4865-9742-d05fa77915a5&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5700abc2-4ec6-4865-9742-d05fa77915a5--&gt;
&lt;!-- begin paragraph f6c6a7b3-b6e5-4140-915f-f68aaa3bf178--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f6c6a7b3-b6e5-4140-915f-f68aaa3bf178&quot;&gt;L’ordre des certificats dans le fichier PEM a son importance, RouterOS les importe de manière séquentielle et s’il ne peut trouver le certificat auquel correspond la clé, ou le certificat permettant de vérifier l’autorité de certification, il ignorera ces derniers ou ne les approuvera pas.&lt;/p&gt;
&lt;!-- end paragraph f6c6a7b3-b6e5-4140-915f-f68aaa3bf178--&gt;
&lt;!-- begin paragraph 0df3264c-8c1e-4397-97a0-db62c83245d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0df3264c-8c1e-4397-97a0-db62c83245d2&quot;&gt;Ainsi l’ordre de lecture est en premier le certificat CA, puis le certificat client et la clé privée. Cela évite de répéter l’opération d’import 3 fois.&lt;/p&gt;
&lt;!-- end paragraph 0df3264c-8c1e-4397-97a0-db62c83245d2--&gt;
&lt;!-- begin heading_1 f34eafa3-c3ba-40ed-b88f-5fbf671bfb51--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f34eafa3-c3ba-40ed-b88f-5fbf671bfb51&quot;&gt;Configuration de l’interface OpenVPN-Client sur RouterOS&lt;/h2&gt;
&lt;!-- end heading_1 f34eafa3-c3ba-40ed-b88f-5fbf671bfb51--&gt;
&lt;!-- begin heading_2 3c8635fe-3b73-4368-ac48-eb74de453599--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3c8635fe-3b73-4368-ac48-eb74de453599&quot;&gt;Avec winbox&lt;/h3&gt;
&lt;!-- end heading_2 3c8635fe-3b73-4368-ac48-eb74de453599--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_1.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 573cace4-0411-4fd5-8146-d97c39def30a--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-573cace4-0411-4fd5-8146-d97c39def30a&quot;&gt;Import du fichier PEM&lt;/h4&gt;
&lt;!-- end heading_3 573cace4-0411-4fd5-8146-d97c39def30a--&gt;
&lt;!-- begin paragraph d2ef158a-10dc-441a-ad30-96dd05c59f5d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d2ef158a-10dc-441a-ad30-96dd05c59f5d&quot;&gt;Allons dans &lt;code&gt;Files&lt;/code&gt;, ensuite, téléversons le fichier &lt;code&gt;certificate.pem&lt;/code&gt; que nous avons créé plus haut,&lt;/p&gt;
&lt;!-- end paragraph d2ef158a-10dc-441a-ad30-96dd05c59f5d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_5.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 26170fae-28c0-4f18-89dc-d576b68283b6--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-26170fae-28c0-4f18-89dc-d576b68283b6&quot;&gt;Ajout des certificats au gestionnaire de RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 26170fae-28c0-4f18-89dc-d576b68283b6--&gt;
&lt;!-- begin paragraph 636db2f2-9867-474f-bc50-60a1f62affc7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-636db2f2-9867-474f-bc50-60a1f62affc7&quot;&gt;Dans le menu &lt;code&gt;System&lt;/code&gt;, &lt;code&gt;Certificates&lt;/code&gt;, cliquons sur &lt;code&gt;Import&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 636db2f2-9867-474f-bc50-60a1f62affc7--&gt;
&lt;!-- begin paragraph cc8845aa-8151-4af9-a52c-a7bb9296e026--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cc8845aa-8151-4af9-a52c-a7bb9296e026&quot;&gt;Dans &lt;code&gt;Name&lt;/code&gt;, on peut changer la racine du nom du certificat ou le laisser la valeur par défaut prise depuis le nom du fichier que l’on va importer.&lt;/p&gt;
&lt;!-- end paragraph cc8845aa-8151-4af9-a52c-a7bb9296e026--&gt;
&lt;!-- begin paragraph 42af9464-2b09-4ad7-9d3a-25bd3dd37436--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42af9464-2b09-4ad7-9d3a-25bd3dd37436&quot;&gt;Indiquons le fichier que l’on a téléversé comme &lt;code&gt;File Name&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 42af9464-2b09-4ad7-9d3a-25bd3dd37436--&gt;
&lt;!-- begin paragraph 335c905e-d561-4963-8f26-2f2fe003c31d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-335c905e-d561-4963-8f26-2f2fe003c31d&quot;&gt;La &lt;code&gt;Passphrase&lt;/code&gt; reste vide, car on a déchiffré la clé juste avant et nous n’avons pas chiffré le contenu du fichier PEM.&lt;/p&gt;
&lt;!-- end paragraph 335c905e-d561-4963-8f26-2f2fe003c31d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_7.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 1ea30adf-cac0-47cc-a8cf-9d395ee45bb3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1ea30adf-cac0-47cc-a8cf-9d395ee45bb3&quot;&gt;Une fois ajouté, deux lignes sont apparues dans le gestionnaire.&lt;/p&gt;
&lt;!-- end paragraph 1ea30adf-cac0-47cc-a8cf-9d395ee45bb3--&gt;
&lt;!-- begin paragraph 701fa51b-8043-4111-95f0-419f94e2204a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-701fa51b-8043-4111-95f0-419f94e2204a&quot;&gt;Dans mon exemple &lt;code&gt;certificat.pem_0&lt;/code&gt; avec les attributs &lt;code&gt;K&lt;/code&gt; et &lt;code&gt;T&lt;/code&gt; et &lt;code&gt;certificat.pem_1&lt;/code&gt; avec juste &lt;code&gt;T&lt;/code&gt; en attribut.&lt;/p&gt;
&lt;!-- end paragraph 701fa51b-8043-4111-95f0-419f94e2204a--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_8.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6bf66c5a-1931-4afc-902c-889acbd3a209--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6bf66c5a-1931-4afc-902c-889acbd3a209&quot;&gt;Double-cliquez sur celui ayant les attributs &lt;code&gt;KT&lt;/code&gt;, c’est le certificat client et renommons-le en &lt;code&gt;openvpn_client&lt;/code&gt; par exemple.&lt;/p&gt;
&lt;!-- end paragraph 6bf66c5a-1931-4afc-902c-889acbd3a209--&gt;
&lt;!-- begin paragraph 40b11151-b74c-4236-87c1-2a9993be9c51--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-40b11151-b74c-4236-87c1-2a9993be9c51&quot;&gt;Faisons de même pour le second et renommons-le en &lt;code&gt;openvpn_ca&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 40b11151-b74c-4236-87c1-2a9993be9c51--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_9.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin callout b1ce54b6-0b5c-4c18-bbd9-20c017b125d0--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-b1ce54b6-0b5c-4c18-bbd9-20c017b125d0&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;📌&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Le &lt;code&gt;K&lt;/code&gt; signifie que la clé pour ce certificat est présente dans le gestionnaire&lt;br /&gt;
Le &lt;code&gt;T&lt;/code&gt; signifie que le certificat est approuvé (trusted) dans le gestionnaire.&lt;br /&gt;
&lt;br /&gt;
Le fait d’ajouter un certificat autosigné implique que la vérification ne peut se faire qu’avec le certificat de l’autorité de certification depuis laquelle nous l’avons demandé.&lt;br /&gt;
Si le certificat de l’autorité de certification (CA) est absent, lors de la configuration de l’interface OpenVPN-client, l’option &lt;code&gt;Verify Server Certificate&lt;/code&gt; doit être désactivé, sinon la vérification du certificat client échouera et la connexion ne s’établira pas.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout b1ce54b6-0b5c-4c18-bbd9-20c017b125d0--&gt;
&lt;!-- begin heading_3 dd8859c7-6f84-4f1d-9cfa-54b40feaeb32--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-dd8859c7-6f84-4f1d-9cfa-54b40feaeb32&quot;&gt;Création du profil Point-to-Point&lt;/h4&gt;
&lt;!-- end heading_3 dd8859c7-6f84-4f1d-9cfa-54b40feaeb32--&gt;
&lt;!-- begin paragraph 8103a9c5-1016-4f61-8c41-ff325d456652--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8103a9c5-1016-4f61-8c41-ff325d456652&quot;&gt;Dans le menu &lt;code&gt;PPP&lt;/code&gt;, nommons-le &lt;code&gt;ovpn&lt;/code&gt;, dans le volet &lt;code&gt;Protocols&lt;/code&gt;, définissons &lt;code&gt;IPv6&lt;/code&gt; sur no, &lt;code&gt;MPLS&lt;/code&gt; sur no, &lt;code&gt;Compression&lt;/code&gt; sur yes et &lt;code&gt;Encryption&lt;/code&gt; sur required.&lt;/p&gt;
&lt;!-- end paragraph 8103a9c5-1016-4f61-8c41-ff325d456652--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_16.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 6b9d3979-e07c-4849-b975-b866ce4cef1c--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-6b9d3979-e07c-4849-b975-b866ce4cef1c&quot;&gt;Ajout de l’interface OpenVPN-client&lt;/h4&gt;
&lt;!-- end heading_3 6b9d3979-e07c-4849-b975-b866ce4cef1c--&gt;
&lt;!-- begin paragraph e4011dec-9a11-4218-bfea-c23443b3e241--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e4011dec-9a11-4218-bfea-c23443b3e241&quot;&gt;Dans le menu &lt;code&gt;Interfaces&lt;/code&gt;, dans le volet &lt;code&gt;Interface&lt;/code&gt;, ajoutons une interface &lt;code&gt;OVPN Client&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph e4011dec-9a11-4218-bfea-c23443b3e241--&gt;
&lt;!-- begin paragraph 66781a70-926a-4dd5-bbd9-c7c201763dd0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-66781a70-926a-4dd5-bbd9-c7c201763dd0&quot;&gt;On fait correspondre les différents champs au contenu du fichier &lt;code&gt;client.ovpn&lt;/code&gt;, l’User correspond à l&amp;#39;username de profil OpenVPN, les paramètres &lt;code&gt;Auth.&lt;/code&gt; et &lt;code&gt;Cipher&lt;/code&gt; sont à adapter de votre serveur OpenVPN.&lt;/p&gt;
&lt;!-- end paragraph 66781a70-926a-4dd5-bbd9-c7c201763dd0--&gt;
&lt;!-- begin paragraph d6ca469a-4135-42bc-8ca5-1b407fb7a0a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d6ca469a-4135-42bc-8ca5-1b407fb7a0a9&quot;&gt;Dans notre exemple, je n’ai pas coché l’ajout de la route par défaut ainsi que de celles qui seraient poussées par le serveur, car cela routerait tout le trafic sortant de RouterOS au travers du tunnel une fois ouvert.&lt;/p&gt;
&lt;!-- end paragraph d6ca469a-4135-42bc-8ca5-1b407fb7a0a9--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_17.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph f8d204f8-b764-42cf-9a07-f8dc2064813a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f8d204f8-b764-42cf-9a07-f8dc2064813a&quot;&gt;Indiquons le profil PPP &lt;code&gt;ovpn&lt;/code&gt; que l’on a créé juste avant. Faisons &lt;code&gt;Apply&lt;/code&gt; puis vérifions dans le volet &lt;code&gt;Status&lt;/code&gt; que tout est bon.&lt;/p&gt;
&lt;!-- end paragraph f8d204f8-b764-42cf-9a07-f8dc2064813a--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2128222b-24be-4ed7-a59c-3eb897b5e401/winbox_19.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph be3423c6-bce2-4d70-bcfe-e5514b002443--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be3423c6-bce2-4d70-bcfe-e5514b002443&quot;&gt;Et z’est parti 🎉&lt;/p&gt;
&lt;!-- end paragraph be3423c6-bce2-4d70-bcfe-e5514b002443--&gt;
&lt;!-- begin paragraph 5b0177f7-f5ea-4ee3-8e68-656816fbe5c6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5b0177f7-f5ea-4ee3-8e68-656816fbe5c6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5b0177f7-f5ea-4ee3-8e68-656816fbe5c6--&gt;
&lt;!-- begin paragraph 6b2236ea-3bb6-4452-a981-476b419413f4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6b2236ea-3bb6-4452-a981-476b419413f4&quot;&gt;Désormais, à l’aide d’un autre client du même serveur OpenVPN, on doit pouvoir exécuter un &lt;code&gt;ping&lt;/code&gt; vers l’IP du matériel exécutant RouterOS.&lt;/p&gt;
&lt;!-- end paragraph 6b2236ea-3bb6-4452-a981-476b419413f4--&gt;
&lt;!-- begin paragraph 25a9c244-9151-412d-824c-3b04d41bbb24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-25a9c244-9151-412d-824c-3b04d41bbb24&quot;&gt;Pour connaitre l’adresse obtenue par le serveur, on peut la voir dans les logs du serveur ainsi que le menu &lt;code&gt;IP&lt;/code&gt;, &lt;code&gt;Addresses&lt;/code&gt; à la ligne de l’interface OpenVPN-Client. &lt;/p&gt;
&lt;!-- end paragraph 25a9c244-9151-412d-824c-3b04d41bbb24--&gt;
&lt;!-- begin callout 05f0622c-9535-49c0-a1bf-189a02aff160--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-05f0622c-9535-49c0-a1bf-189a02aff160&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;📃&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Si pour raison inconnue, cela ne fonctionne pas, il faut regarder les logs de RouterOS et ceux du serveur OpenVPN.&lt;br /&gt;
Cela donne une grande aide pour corriger les options de chiffrement qui ne seraient pas les bonnes par rapport au serveur et bien d’autres paramètres qui ne seraient pas cohérents.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout 05f0622c-9535-49c0-a1bf-189a02aff160--&gt;
&lt;!-- begin heading_2 d7d089cb-a47d-485b-bf1b-d8c520834ca4--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d7d089cb-a47d-485b-bf1b-d8c520834ca4&quot;&gt;Avec la console&lt;/h3&gt;
&lt;!-- end heading_2 d7d089cb-a47d-485b-bf1b-d8c520834ca4--&gt;
&lt;!-- begin paragraph 06750777-abb3-4042-b64e-1b991dc3704a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06750777-abb3-4042-b64e-1b991dc3704a&quot;&gt;Voici la version console des étapes ci-dessus.&lt;/p&gt;
&lt;!-- end paragraph 06750777-abb3-4042-b64e-1b991dc3704a--&gt;
&lt;!-- begin paragraph 52b9e755-ef80-4148-b56a-73427e231323--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-52b9e755-ef80-4148-b56a-73427e231323&quot;&gt;Ces étapes impliquent d’avoir l’accès &lt;code&gt;SCP&lt;/code&gt; pour téléverser le fichier PEM sur RouterOS et l’accès console (voir la configuration dans &lt;code&gt;IP&lt;/code&gt;, &lt;code&gt;Services&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph 52b9e755-ef80-4148-b56a-73427e231323--&gt;
&lt;!-- begin heading_3 54961ff2-1f96-444d-82ee-1c24d4a70045--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-54961ff2-1f96-444d-82ee-1c24d4a70045&quot;&gt;Import du fichier PEM avec SCP&lt;/h4&gt;
&lt;!-- end heading_3 54961ff2-1f96-444d-82ee-1c24d4a70045--&gt;
&lt;!-- begin code 2b14cc85-4f73-4aa1-b437-251c36902f88--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2b14cc85-4f73-4aa1-b437-251c36902f88&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;scp ./certificat.pem admin@192.168.167.250:certificat.pem&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2b14cc85-4f73-4aa1-b437-251c36902f88--&gt;
&lt;!-- begin heading_3 f1bf9df2-8e5f-43f0-9482-33207bfa73bf--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-f1bf9df2-8e5f-43f0-9482-33207bfa73bf&quot;&gt;Ajout des certificats au gestionnaire de RouterOS&lt;/h4&gt;
&lt;!-- end heading_3 f1bf9df2-8e5f-43f0-9482-33207bfa73bf--&gt;
&lt;!-- begin code e6d6c8c8-3359-45cf-b65f-2c73c1ee6024--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e6d6c8c8-3359-45cf-b65f-2c73c1ee6024&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;/certificate/import \
    file-name=certificat.pem \
    name=openvpn \
    passphrase=&amp;quot;&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e6d6c8c8-3359-45cf-b65f-2c73c1ee6024--&gt;
&lt;!-- begin paragraph 472b8d98-6020-43a1-b101-98f61649fb5d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-472b8d98-6020-43a1-b101-98f61649fb5d&quot;&gt;La sortie doit afficher comme ce qui suit&lt;/p&gt;
&lt;!-- end paragraph 472b8d98-6020-43a1-b101-98f61649fb5d--&gt;
&lt;!-- begin code b87aca96-bb96-4fcb-b2c5-1daf92ad4d29--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b87aca96-bb96-4fcb-b2c5-1daf92ad4d29&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;certificates-imported: 2
private-keys-imported: 1
files-imported: 1
decryption-failures: 0
keys-with-no-certificate: 0&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b87aca96-bb96-4fcb-b2c5-1daf92ad4d29--&gt;
&lt;!-- begin heading_3 15df4034-1dec-4cbd-87b1-f1b62bf18677--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-15df4034-1dec-4cbd-87b1-f1b62bf18677&quot;&gt;Renommage des certificats importés&lt;/h4&gt;
&lt;!-- end heading_3 15df4034-1dec-4cbd-87b1-f1b62bf18677--&gt;
&lt;!-- begin code 67281612-5f83-47d0-ad9d-48ca80046aa5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-67281612-5f83-47d0-ad9d-48ca80046aa5&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;/certificate/set openvpn_1 name=&amp;quot;openvpn_ca&amp;quot;
/certificate/set openvpn name=&amp;quot;openvpn_client&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 67281612-5f83-47d0-ad9d-48ca80046aa5--&gt;
&lt;!-- begin heading_3 7a35a7e0-fbe6-42d5-959b-c116bd3847e1--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-7a35a7e0-fbe6-42d5-959b-c116bd3847e1&quot;&gt;Création du profil Point-to-Point&lt;/h4&gt;
&lt;!-- end heading_3 7a35a7e0-fbe6-42d5-959b-c116bd3847e1--&gt;
&lt;!-- begin code e91bdc0d-835d-47b4-8b11-f84f744f6c83--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e91bdc0d-835d-47b4-8b11-f84f744f6c83&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;/ppp/profile/add \
    use-ipv6=no \
    use-compression=yes \
    use-encryption=required \
    use-mpls=no \
    name=ovpn&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e91bdc0d-835d-47b4-8b11-f84f744f6c83--&gt;
&lt;!-- begin heading_3 6b0fd12e-9702-453c-9b6e-0846082dd7b0--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-6b0fd12e-9702-453c-9b6e-0846082dd7b0&quot;&gt;Ajout de l’interface OpenVPN-client&lt;/h4&gt;
&lt;!-- end heading_3 6b0fd12e-9702-453c-9b6e-0846082dd7b0--&gt;
&lt;!-- begin code 1f331927-50a2-4319-b1a1-b7e0e800821e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1f331927-50a2-4319-b1a1-b7e0e800821e&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;/interface/ovpn-client/add \
    connect-to=&amp;quot;openvpn.server.io&amp;quot; \
    port=1194 \
    mode=ip \
    protocol=udp \
    user=&amp;quot;utilisateur&amp;quot; \
    profile=ovpn \
    certificate=openvpn_client \
    verify-server-certificate=yes \
    tls-version=any \
    auth=sha1 \
    cipher=aes256-cbc \
    add-default-route=no \
    route-nopull=yes&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1f331927-50a2-4319-b1a1-b7e0e800821e--&gt;
&lt;!-- begin paragraph 643b67dc-b30a-4df4-a6a0-1f510ca53053--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-643b67dc-b30a-4df4-a6a0-1f510ca53053&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 643b67dc-b30a-4df4-a6a0-1f510ca53053--&gt;
&lt;!-- begin paragraph 1fca46b4-709d-441a-921b-d93319110c5b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1fca46b4-709d-441a-921b-d93319110c5b&quot;&gt;Et z’est parti 🎉&lt;/p&gt;
&lt;!-- end paragraph 1fca46b4-709d-441a-921b-d93319110c5b--&gt;
&lt;!-- begin paragraph 5c960c44-57c3-4128-8478-bcee0f5aa9ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5c960c44-57c3-4128-8478-bcee0f5aa9ad&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5c960c44-57c3-4128-8478-bcee0f5aa9ad--&gt;
&lt;!-- begin bookmark 74d9763e-a696-42e4-8b11-23ccb4ea0460--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-74d9763e-a696-42e4-8b11-23ccb4ea0460&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://forum.mikrotik.com/t/importing-a-pem-certificat/105545&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://data-discourse.cdn.mikrotik.com/original/3X/d/d/ddbbaa5ff66c3b66f5983f834fad4ef1e0c449cd.svg&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Importing a pem certificat&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;When a pem certificate is imported that holds the ca, crt and the private key (client.pem). The key is not handled/recognised. 
When the pem without the key (client_wokey.pem) is imported and after that the private key (&amp;hellip;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://forum.mikrotik.com/t/importing-a-pem-certificat/105545&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 74d9763e-a696-42e4-8b11-23ccb4ea0460--&gt;
&lt;!-- begin bookmark 0119611f-b8cd-4fe1-bd60-07b89442ee10--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-0119611f-b8cd-4fe1-bd60-07b89442ee10&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;http://missinglink.github.io/mikrotik-openvpn-client/&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Mikrotik router as OpenVPN Client&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;configure your mikrotik routerboard as an openvpn client&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;&quot;&gt; http://missinglink.github.io/mikrotik-openvpn-client/&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 0119611f-b8cd-4fe1-bd60-07b89442ee10--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:fcfea31328314e9ca1045d9d446b3dfc</id>
    <title>Comment augmenter la sûreté de nos types en Scala (partie 2)</title>
    <updated>2022-12-18T07:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/comment-augmenter-la-surete-de-nos-types-en-scala-2.html"/>
    <!--summary La partie 2 pour avoir des types encore plus fort !-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Scala"></category>    <category term="Compilation"></category>    <content type="html">
&lt;!-- begin paragraph ee312c93-a767-4d63-996c-5ae6d808f40d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ee312c93-a767-4d63-996c-5ae6d808f40d&quot;&gt;Nous avons vu dans la &lt;a href=&quot;https://univalence.io/blog/articles/comment-augmenter-la-surete-de-nos-types-en-scala/&quot;&gt;partie 1&lt;/a&gt; comment rendre nos types plus forts en utilisant seulement les outils que nous donne le langage sans utiliser aucune librairie. Dans cette partie, nous allons voir les réponses de la communauté pour répondre aux deux problématiques que j’avais établi dans le premier article, à savoir :&lt;/p&gt;
&lt;!-- end paragraph ee312c93-a767-4d63-996c-5ae6d808f40d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Deux valeurs du même type peuvent être interchangeables (un paramètre String peut recevoir à la fois un mail et un nom d’utilisateur sans provoquer une erreur).&lt;/li&gt;&lt;li&gt;Une valeur peut avoir un domaine plus restreint que ce que permet un type (un email est un String, mais tous les Strings ne sont pas des emails).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 06576d39-63e6-458e-98ca-f2f8aa9a94e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06576d39-63e6-458e-98ca-f2f8aa9a94e7&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 06576d39-63e6-458e-98ca-f2f8aa9a94e7--&gt;
&lt;!-- begin heading_1 21db0b61-deea-4c65-9f3a-010284823e82--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-21db0b61-deea-4c65-9f3a-010284823e82&quot;&gt;La première problématique&lt;/h2&gt;
&lt;!-- end heading_1 21db0b61-deea-4c65-9f3a-010284823e82--&gt;
&lt;!-- begin paragraph 3d3d61a7-f64d-4b24-a160-4b092fb04566--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d3d61a7-f64d-4b24-a160-4b092fb04566&quot;&gt;Nous avons déjà longuement répondu à cette dernière et la réponse fournie par le langage semble avoir aussi convaincu toute la communauté puisque, à ma connaissance, il n’y a pas eu beaucoup de travaux open source pour répondre à cette problématique.&lt;/p&gt;
&lt;!-- end paragraph 3d3d61a7-f64d-4b24-a160-4b092fb04566--&gt;
&lt;!-- begin paragraph 555ec765-51e3-49f8-ab69-b6b9e37c8b96--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-555ec765-51e3-49f8-ab69-b6b9e37c8b96&quot;&gt;La seule véritable proposition atypique que j’ai croisée est celle de &lt;a href=&quot;https://gist.github.com/milessabin/89c9b47a91017973a35f&quot;&gt;Miles Sabin&lt;/a&gt; qui a été ensuite implémentée par &lt;a href=&quot;https://github.com/softwaremill/scala-common#tagging&quot;&gt;softwaremill&lt;/a&gt;. Elle repose sur la tagging pour différencier deux mêmes types. Si on reprend l’exemple de la partie 1 :&lt;/p&gt;
&lt;!-- end paragraph 555ec765-51e3-49f8-ab69-b6b9e37c8b96--&gt;
&lt;!-- begin code 7f4335d7-9054-43e4-9b59-6b403e9fcc23--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7f4335d7-9054-43e4-9b59-6b403e9fcc23&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import com.softwaremill.tagging._

case class User {
	id: String @@ UserId
}

trait UserId

case class Team {
	id: String @@ TeamId,
	users: List[User]
}

trait TeamId&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7f4335d7-9054-43e4-9b59-6b403e9fcc23--&gt;
&lt;!-- begin paragraph b481270c-1d5c-4fe5-85de-7da6b2857b47--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b481270c-1d5c-4fe5-85de-7da6b2857b47&quot;&gt;On peut résoudre correctement l’exemple ci-contre :&lt;/p&gt;
&lt;!-- end paragraph b481270c-1d5c-4fe5-85de-7da6b2857b47--&gt;
&lt;!-- begin code 34bf2a2c-6932-4852-8a4e-a9ff40b6f1aa--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-34bf2a2c-6932-4852-8a4e-a9ff40b6f1aa&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val user = User(&amp;quot;uuid&amp;quot;.taggedWith[UserId])

def getWorkingHoursLastWeek(id: String @@ TeamId): Int = ???

val hours = getWorkingHoursLastWeek(user.id) // It does not compile 🔥&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 34bf2a2c-6932-4852-8a4e-a9ff40b6f1aa--&gt;
&lt;!-- begin paragraph f0af9869-cc63-4706-bdeb-4db3f419d15f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f0af9869-cc63-4706-bdeb-4db3f419d15f&quot;&gt;Ça fonctionne ! Cette méthode a aussi l’avantage d’être totalement transparente au runtime (tout comme les types opaques et dans une moindre mesure les value classes). Il a aussi un autre avantage comparé à ce qu’on a déjà vu : on peut automatiquement récupérer n’importe quelle typeclass du type taggé avec quasiment aucun overhead. &lt;/p&gt;
&lt;!-- end paragraph f0af9869-cc63-4706-bdeb-4db3f419d15f--&gt;
&lt;!-- begin paragraph 9c5b3216-ce86-4d1a-b319-f2f2cf3bf6b3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c5b3216-ce86-4d1a-b319-f2f2cf3bf6b3&quot;&gt;Il suffit d’utiliser l’import suivant :&lt;/p&gt;
&lt;!-- end paragraph 9c5b3216-ce86-4d1a-b319-f2f2cf3bf6b3--&gt;
&lt;!-- begin code 97cad2ab-5941-41d8-a855-d712d6c73828--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-97cad2ab-5941-41d8-a855-d712d6c73828&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import com.softwaremill.tagging.AnyTypeclassTaggingCompat._&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 97cad2ab-5941-41d8-a855-d712d6c73828--&gt;
&lt;!-- begin heading_1 8f4e3764-08d5-41c0-b8fa-6608365cb0bc--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-8f4e3764-08d5-41c0-b8fa-6608365cb0bc&quot;&gt;La deuxième problématique&lt;/h2&gt;
&lt;!-- end heading_1 8f4e3764-08d5-41c0-b8fa-6608365cb0bc--&gt;
&lt;!-- begin paragraph 23603f6a-0f9f-4f0c-8ec1-3ff403db0925--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-23603f6a-0f9f-4f0c-8ec1-3ff403db0925&quot;&gt;Passons maintenant à la deuxième problématique que j’ai soulevée. Afin de comprendre en quoi cela en est une, nous allons pour cela prendre le cas d’une fonction assez connue :&lt;/p&gt;
&lt;!-- end paragraph 23603f6a-0f9f-4f0c-8ec1-3ff403db0925--&gt;
&lt;!-- begin code e822cdcd-2b9c-4b97-a253-26be689319e1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e822cdcd-2b9c-4b97-a253-26be689319e1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def take[A](list: List[A], n: Int): List[A] = ??? &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e822cdcd-2b9c-4b97-a253-26be689319e1--&gt;
&lt;!-- begin paragraph 1f3955d5-a4d7-451a-971e-27930ae4e087--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f3955d5-a4d7-451a-971e-27930ae4e087&quot;&gt;Cette dernière prend les n premiers éléments d’une List. Vous êtes-vous déjà demandé ce qui arriverait si on renseignait un &lt;code&gt;n&lt;/code&gt; négatif ?&lt;/p&gt;
&lt;!-- end paragraph 1f3955d5-a4d7-451a-971e-27930ae4e087--&gt;
&lt;!-- begin paragraph 72774196-caaf-4994-a93a-82f732fbe1ba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-72774196-caaf-4994-a93a-82f732fbe1ba&quot;&gt;Il existe une infinité de possibilités, les plus courantes sont les suivantes :&lt;/p&gt;
&lt;!-- end paragraph 72774196-caaf-4994-a93a-82f732fbe1ba--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;La fonction renvoie une exception&lt;/li&gt;&lt;li&gt;La fonction gère les valeurs incohérentes de &lt;code&gt;n&lt;/code&gt; en les remplaçant par zéro&lt;/li&gt;&lt;li&gt;La fonction renvoie la List à partir du dernier élément (coucou python)&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 79d21872-f1e9-4f98-97d8-d931f3045952--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79d21872-f1e9-4f98-97d8-d931f3045952&quot;&gt;On pourrait discuter des heures sur laquelle de ces possibilités est la meilleure. En réalité, le problème ici est que la fonction devrait ne pouvoir prendre que des nombres supérieurs à zéro.&lt;/p&gt;
&lt;!-- end paragraph 79d21872-f1e9-4f98-97d8-d931f3045952--&gt;
&lt;!-- begin paragraph 4abea457-47ca-430c-ace1-2d4845f4e848--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4abea457-47ca-430c-ace1-2d4845f4e848&quot;&gt;Malheureusement, le langage ne nous fournit pas les moyens pour régler ce genre de problème directement au compile time. Fort heureusement, des librairies permettent de pallier ce besoin.&lt;/p&gt;
&lt;!-- end paragraph 4abea457-47ca-430c-ace1-2d4845f4e848--&gt;
&lt;!-- begin heading_2 fb049490-dd48-46cb-954e-58fc95b7c1d1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-fb049490-dd48-46cb-954e-58fc95b7c1d1&quot;&gt;Refined&lt;/h3&gt;
&lt;!-- end heading_2 fb049490-dd48-46cb-954e-58fc95b7c1d1--&gt;
&lt;!-- begin paragraph b09e2abb-b7ca-42db-8480-df177de02e4a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b09e2abb-b7ca-42db-8480-df177de02e4a&quot;&gt;La plus connue est très certainement &lt;a href=&quot;https://github.com/fthomas/refined&quot;&gt;refined&lt;/a&gt;. Si on reprend l’exemple précédent :&lt;/p&gt;
&lt;!-- end paragraph b09e2abb-b7ca-42db-8480-df177de02e4a--&gt;
&lt;!-- begin code 8802d310-1e92-4b7e-90d1-8908218e6481--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8802d310-1e92-4b7e-90d1-8908218e6481&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._

def take[A](list: List[A], n: Int Refined Positive): List[A] = ??? &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8802d310-1e92-4b7e-90d1-8908218e6481--&gt;
&lt;!-- begin paragraph 4016626d-d6e6-45a9-b4c0-ab4e191bcda1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4016626d-d6e6-45a9-b4c0-ab4e191bcda1&quot;&gt;Grâce à refined, on peut spécialiser nos types. Ici, on précise que &lt;code&gt;n&lt;/code&gt;, en plus de devoir être un int, doit aussi être un nombre positif (cela n’a pas de sens de demander un nombre négatif comme dit au-dessus, mais 0 n’a aussi pas vraiment de sens). Refined fournis un grand nombre de modificateurs que vous pouvez combiner avec un grand nombre de types. À titre d’exemple, vous pouvez :&lt;/p&gt;
&lt;!-- end paragraph 4016626d-d6e6-45a9-b4c0-ab4e191bcda1--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Forcer des Strings à être des URLs&lt;/li&gt;&lt;li&gt;Forcer des Strings à ne pas être vide&lt;/li&gt;&lt;li&gt;Forcer des Strings à être des adresses mails&lt;/li&gt;&lt;li&gt;Forcer des Floats à être entre 0 et 1&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph b2788022-9446-4fe2-a27b-9ab32ba6ec43--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b2788022-9446-4fe2-a27b-9ab32ba6ec43&quot;&gt;Bien évidemment, la librairie vous donne aussi la possibilité de créer vos propres modificateurs, je vous invite à lire &lt;a href=&quot;https://github.com/fthomas/refined#refined-simple-refinement-types-for-scala&quot;&gt;la documentation&lt;/a&gt; pour plus d’informations.&lt;/p&gt;
&lt;!-- end paragraph b2788022-9446-4fe2-a27b-9ab32ba6ec43--&gt;
&lt;!-- begin paragraph 38f6922e-6bc6-422d-b6a3-048b2e67caa5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38f6922e-6bc6-422d-b6a3-048b2e67caa5&quot;&gt;Il faut cependant différencier les valeurs reçues au compile time de celles reçues au runtime. Cette librairie ne peut pas savoir à l’avance que la valeur rentrée par un utilisateur correspond au prédicat. Pour pallier ce problème, une fonction vous permet de récupérer la valeur ou une erreur dans le cas où le prédicat n’est pas respecté au runtime :&lt;/p&gt;
&lt;!-- end paragraph 38f6922e-6bc6-422d-b6a3-048b2e67caa5--&gt;
&lt;!-- begin code ee142790-82e5-4b04-9512-1e2cd1c75f0e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ee142790-82e5-4b04-9512-1e2cd1c75f0e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val userPositiveInt: Either[String, Int Refined Positive] = refineV[Positive](???)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ee142790-82e5-4b04-9512-1e2cd1c75f0e--&gt;
&lt;!-- begin heading_2 4a0b5da6-5655-4637-99cb-30eea5f24764--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4a0b5da6-5655-4637-99cb-30eea5f24764&quot;&gt;NewType&lt;/h3&gt;
&lt;!-- end heading_2 4a0b5da6-5655-4637-99cb-30eea5f24764--&gt;
&lt;!-- begin paragraph 77c1e823-b2f0-4ca8-ade0-1c0acd7f2cdf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77c1e823-b2f0-4ca8-ade0-1c0acd7f2cdf&quot;&gt;Refined répond parfaitement à la problématique numéro 2. Malheureusement, cela ne répond pas à la problématique numéro 1 et il nous faudra alors combiner deux méthodes pour obtenir quelque chose de concluant.&lt;/p&gt;
&lt;!-- end paragraph 77c1e823-b2f0-4ca8-ade0-1c0acd7f2cdf--&gt;
&lt;!-- begin paragraph bc18fd9d-9805-488e-ab75-e1bc7dc4ba72--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bc18fd9d-9805-488e-ab75-e1bc7dc4ba72&quot;&gt;Pour être honnête, ce genre de syntaxe est assez lourde :&lt;/p&gt;
&lt;!-- end paragraph bc18fd9d-9805-488e-ab75-e1bc7dc4ba72--&gt;
&lt;!-- begin code ee602dc7-291f-4537-bdaa-5db532d2e269--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ee602dc7-291f-4537-bdaa-5db532d2e269&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class User {
	id: String Refined NonEmpty @@ UserId
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ee602dc7-291f-4537-bdaa-5db532d2e269--&gt;
&lt;!-- begin paragraph 7c3a040a-48d7-4074-af32-b968391f9fd5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7c3a040a-48d7-4074-af32-b968391f9fd5&quot;&gt;Ce serait génial si une librairie pouvait faire les deux en même temps ! &lt;/p&gt;
&lt;!-- end paragraph 7c3a040a-48d7-4074-af32-b968391f9fd5--&gt;
&lt;!-- begin paragraph 601e3059-9445-450f-967d-4d9cc7543e59--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-601e3059-9445-450f-967d-4d9cc7543e59&quot;&gt;Fort heureusement pour nous, il existe une solution : les &lt;a href=&quot;https://zio.github.io/zio-prelude/docs/newtypes/&quot;&gt;NewTypes&lt;/a&gt; de zio-prelude. Prenons le cas de notre User :&lt;/p&gt;
&lt;!-- end paragraph 601e3059-9445-450f-967d-4d9cc7543e59--&gt;
&lt;!-- begin code 1f1a6d90-43fa-45bf-a35b-262b2f3cc11d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1f1a6d90-43fa-45bf-a35b-262b2f3cc11d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio.prelude.Assertion.isEmptyString
import zio.prelude.Subtype

case class User(id: UserId)

object UserId extends Subtype[String] {
  override def assertion = assert { !isEmptyString }
}

type UserId = UserId.Type&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1f1a6d90-43fa-45bf-a35b-262b2f3cc11d--&gt;
&lt;!-- begin paragraph b3bd6a0e-a8fc-4344-9645-f8f4cda3293f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3bd6a0e-a8fc-4344-9645-f8f4cda3293f&quot;&gt;La réponse donnée par les NewTypes répond bien aux deux problématiques puisque :&lt;/p&gt;
&lt;!-- end paragraph b3bd6a0e-a8fc-4344-9645-f8f4cda3293f--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;UserId est un type bien définit qui ne peut pas être remplacé par un String, ce qui permet d’empêcher les erreurs d’étourderies.&lt;/li&gt;&lt;li&gt;UserId a une “assertion” qui permet au compile time de valider que la donnée renseignée est celle attendue&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph cb5eeea4-f335-49ab-b448-c0c73f57c606--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cb5eeea4-f335-49ab-b448-c0c73f57c606&quot;&gt;Tout comme pour refined, vous pouvez aussi saisir des valeurs au runtime de la manière suivante :&lt;/p&gt;
&lt;!-- end paragraph cb5eeea4-f335-49ab-b448-c0c73f57c606--&gt;
&lt;!-- begin code 4a45b08d-00ac-4f8a-84ae-c7afa2cb9589--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4a45b08d-00ac-4f8a-84ae-c7afa2cb9589&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val userPositiveInt: Validation[String, UserId] = UserId.make(???)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4a45b08d-00ac-4f8a-84ae-c7afa2cb9589--&gt;
&lt;!-- begin heading_1 c26065fd-0512-4a4d-9292-a3f1b5ded921--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c26065fd-0512-4a4d-9292-a3f1b5ded921&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 c26065fd-0512-4a4d-9292-a3f1b5ded921--&gt;
&lt;!-- begin paragraph 7df54e1f-e539-471c-ba32-f82cbbb695fd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7df54e1f-e539-471c-ba32-f82cbbb695fd&quot;&gt;Il existe beaucoup de possibilités dans l’écosystème Scala pour avoir des types beaucoup plus précis, au compile time et sans overhead. Selon moi, chacun à son utilité, ses avantages et ses inconvénients.&lt;/p&gt;
&lt;!-- end paragraph 7df54e1f-e539-471c-ba32-f82cbbb695fd--&gt;
&lt;!-- begin paragraph 723fd4a3-5e3a-4d89-be48-d0df9a107336--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-723fd4a3-5e3a-4d89-be48-d0df9a107336&quot;&gt;Si vous voulez mon avis, le choix devrait dépendre du contexte :&lt;/p&gt;
&lt;!-- end paragraph 723fd4a3-5e3a-4d89-be48-d0df9a107336--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Refined offre une syntaxe très élégante lorsqu’il s’agit de traiter des cas génériques, mais qui ne permet pas de différencier les types métiers. &lt;/li&gt;&lt;li&gt;Les NewTypes de zio-prelude quant à eux offrent une syntaxe plus encombrante, mais gère correctement le besoin d’avoir des types ayant les mêmes prédicats, mais pas la même signification d’un point de vue métier.&lt;/li&gt;&lt;li&gt;Les opaque types sont directement intégrés au langage et donc censés mieux résister à l’épreuve du temps, mais ils ne prennent pas en compte l’aspect validation au compile time.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 3c007090-af73-4998-9a73-b64170f3de8e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3c007090-af73-4998-9a73-b64170f3de8e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 3c007090-af73-4998-9a73-b64170f3de8e--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:4b449a2590da47a2a390ed59b20ca6a5</id>
    <title>Comment augmenter la sûreté de nos types en Scala (partie 1)</title>
    <updated>2022-12-11T07:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/comment-augmenter-la-surete-de-nos-types-en-scala.html"/>
    <!--summary Le typage fort n’est pas suffisant pour vous ? Alors ne ratez surtout pas cet article !-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Scala"></category>    <category term="Compilation"></category>    <content type="html">
&lt;!-- begin paragraph 8bff2386-3e57-4717-bf27-dcaaa9f529d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8bff2386-3e57-4717-bf27-dcaaa9f529d7&quot;&gt;Produire du code qui fonctionne du premier coup est une sensation incroyable. Au-delà de l’accomplissement personnel, cela réduit aussi considérablement le temps de développement. En effet, lorsque le projet prend de l’ampleur, il n’est pas toujours facile de remarquer les erreurs d’étourderies et ces dernières se manifestent (normalement) dans l’environnement de test.&lt;/p&gt;
&lt;!-- end paragraph 8bff2386-3e57-4717-bf27-dcaaa9f529d7--&gt;
&lt;!-- begin paragraph a6cf6925-6218-497c-9cc8-f8feb52ff0e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a6cf6925-6218-497c-9cc8-f8feb52ff0e1&quot;&gt;Le problème ? Le temps pour que le code arrive en test est incroyablement long, il faut que la CI lance tous les checks, que la PR soit validée, puis qu’elle soit déployée, et ensuite que le bug soit détecté manuellement. Et même si on se dit qu’avec tous ces filtres, l’étourderie devrait disparaitre, en réalité, il m’est déjà arrivé un nombre incalculable de fois que cette dernière persiste jusqu’en production.&lt;/p&gt;
&lt;!-- end paragraph a6cf6925-6218-497c-9cc8-f8feb52ff0e1--&gt;
&lt;!-- begin heading_1 ed5496d5-d356-483f-9938-e6c86531c559--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ed5496d5-d356-483f-9938-e6c86531c559&quot;&gt;Le typage fort à la rescousse !&lt;/h2&gt;
&lt;!-- end heading_1 ed5496d5-d356-483f-9938-e6c86531c559--&gt;
&lt;!-- begin paragraph 54964c4a-0ba1-4914-9b89-7c494616af2e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-54964c4a-0ba1-4914-9b89-7c494616af2e&quot;&gt;C’est pour cette raison que j’estime qu’un typage fort est un véritable plus. Il est peut-être embêtant sur le court terme (pour des POCs par exemple), mais sur le long terme, il permet une très grande stabilité.&lt;/p&gt;
&lt;!-- end paragraph 54964c4a-0ba1-4914-9b89-7c494616af2e--&gt;
&lt;!-- begin paragraph c78223d0-972e-434a-86f2-59efff768dd3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c78223d0-972e-434a-86f2-59efff768dd3&quot;&gt;On a de la chance, avec Scala c’est exactement ce qu’on a ! Ce genre d’ânerie n’est pas possible puisque le compilateur ne voudra pas laisser passer votre code :&lt;/p&gt;
&lt;!-- end paragraph c78223d0-972e-434a-86f2-59efff768dd3--&gt;
&lt;!-- begin code aeff1cf0-9444-402d-9719-78f4ec0900b9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-aeff1cf0-9444-402d-9719-78f4ec0900b9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val id: String = 5&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code aeff1cf0-9444-402d-9719-78f4ec0900b9--&gt;
&lt;!-- begin paragraph e75a6590-53d2-4c54-b977-082835b465c9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e75a6590-53d2-4c54-b977-082835b465c9&quot;&gt;Comme toute bonne chose, on en veut toujours plus ! Les Strings, les Ints, tout cela reste très générique dans l’idée. &lt;/p&gt;
&lt;!-- end paragraph e75a6590-53d2-4c54-b977-082835b465c9--&gt;
&lt;!-- begin paragraph 7638dbe2-7c16-4080-9a4f-9aaf69da192b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7638dbe2-7c16-4080-9a4f-9aaf69da192b&quot;&gt;Cela soulève deux problèmes :&lt;/p&gt;
&lt;!-- end paragraph 7638dbe2-7c16-4080-9a4f-9aaf69da192b--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Deux valeurs du même type peuvent être interchangeables (un paramètre String peut recevoir à la fois un mail et un nom d’utilisateur sans provoquer une erreur).&lt;/li&gt;&lt;li&gt;Une valeur peut avoir un domaine plus restreint que ce que permet un type (un email est un String, mais tous les Strings ne sont pas des emails).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 11caef55-497e-427c-a053-85c611242d21--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-11caef55-497e-427c-a053-85c611242d21&quot;&gt;Aujourd’hui, je vous propose de répondre au premier problème et de voir les solutions que nous avons à notre disposition pour le résoudre.&lt;/p&gt;
&lt;!-- end paragraph 11caef55-497e-427c-a053-85c611242d21--&gt;
&lt;!-- begin paragraph 9121a514-8c19-4953-abd7-9be8e82e0ce4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9121a514-8c19-4953-abd7-9be8e82e0ce4&quot;&gt;Partons d’un modèle de donnée qui regroupe des utilisateurs repartis dans des projets. On peut créer le domaine de la manière suivante :&lt;/p&gt;
&lt;!-- end paragraph 9121a514-8c19-4953-abd7-9be8e82e0ce4--&gt;
&lt;!-- begin code 488716d3-2b32-4ae4-808f-19258b6ddc23--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-488716d3-2b32-4ae4-808f-19258b6ddc23&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class User (
	id: String
)

case class Team (
	id: String,
	users: List[User]
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 488716d3-2b32-4ae4-808f-19258b6ddc23--&gt;
&lt;!-- begin paragraph 297946a6-8efb-4bbf-b7f4-42c2aa934b9b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-297946a6-8efb-4bbf-b7f4-42c2aa934b9b&quot;&gt;Jusque-là, il n’y a aucun souci particulier. Mais, imaginons maintenant que vous avez la fonction suivante :&lt;/p&gt;
&lt;!-- end paragraph 297946a6-8efb-4bbf-b7f4-42c2aa934b9b--&gt;
&lt;!-- begin code 562a0aa7-8e34-409c-9d4c-93233d9abd8b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-562a0aa7-8e34-409c-9d4c-93233d9abd8b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def getWorkingHoursLastWeek(id: String): Int = ???&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 562a0aa7-8e34-409c-9d4c-93233d9abd8b--&gt;
&lt;!-- begin paragraph 73bdbc62-1e34-455c-96e0-3a8a0da85ebe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-73bdbc62-1e34-455c-96e0-3a8a0da85ebe&quot;&gt;Pouvez-vous me dire, en étant 100% sûr de vous, à quoi fait référence &lt;code&gt;id&lt;/code&gt; ? &lt;/p&gt;
&lt;!-- end paragraph 73bdbc62-1e34-455c-96e0-3a8a0da85ebe--&gt;
&lt;!-- begin heading_1 13fde8c4-871c-486f-93bb-3f3ad92777b3--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-13fde8c4-871c-486f-93bb-3f3ad92777b3&quot;&gt;Utiliser un alias de type&lt;/h2&gt;
&lt;!-- end heading_1 13fde8c4-871c-486f-93bb-3f3ad92777b3--&gt;
&lt;!-- begin paragraph ed878bf2-408c-45e9-af63-43a509329863--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ed878bf2-408c-45e9-af63-43a509329863&quot;&gt;Un moyen simple pour régler ce quiproquo est d’utiliser les alias de type. Voici comment faire avec notre exemple :&lt;/p&gt;
&lt;!-- end paragraph ed878bf2-408c-45e9-af63-43a509329863--&gt;
&lt;!-- begin code 99fb35ca-795e-44f9-ad18-cf8da8591246--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-99fb35ca-795e-44f9-ad18-cf8da8591246&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class User (
	id: UserId
)

type UserId = String

case class Team (
	id: TeamId,
	users: List[User]
)

type TeamId = String&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 99fb35ca-795e-44f9-ad18-cf8da8591246--&gt;
&lt;!-- begin paragraph 34c186ae-5f1e-4651-9e17-d78088cb38d5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-34c186ae-5f1e-4651-9e17-d78088cb38d5&quot;&gt;Il faut bien comprendre que UserId et TeamId sont des alias de String et peuvent être utilisés à la place de String en affectant uniquement la lisibilité du code.&lt;/p&gt;
&lt;!-- end paragraph 34c186ae-5f1e-4651-9e17-d78088cb38d5--&gt;
&lt;!-- begin paragraph f53f19a6-8f4e-480d-bd46-815311735c75--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f53f19a6-8f4e-480d-bd46-815311735c75&quot;&gt;Et voilà la nouvelle fonction associée :&lt;/p&gt;
&lt;!-- end paragraph f53f19a6-8f4e-480d-bd46-815311735c75--&gt;
&lt;!-- begin code db5a2ed1-61d7-4fdf-ad9a-782bb4bfa949--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-db5a2ed1-61d7-4fdf-ad9a-782bb4bfa949&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def getWorkingHoursLastWeek(id: TeamId): Int = ???&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code db5a2ed1-61d7-4fdf-ad9a-782bb4bfa949--&gt;
&lt;!-- begin paragraph f1adcc2b-0d4d-4316-b916-a4c01f29335e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f1adcc2b-0d4d-4316-b916-a4c01f29335e&quot;&gt;On y voit tout de suite plus clair ! L’utilisation des alias de type rentre dans le cadre du “types as documentation”. Ici, la signature de la fonction se suffit à elle-même, nous n’avons ni besoin de lire le contenu de la fonction pour comprendre ce qu’elle fait, ni besoin de rédiger une docstring pour l’expliquer.&lt;/p&gt;
&lt;!-- end paragraph f1adcc2b-0d4d-4316-b916-a4c01f29335e--&gt;
&lt;!-- begin paragraph 48d8e9ad-261e-41b9-abfc-6dae00db8c8b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-48d8e9ad-261e-41b9-abfc-6dae00db8c8b&quot;&gt;Malheureusement, ce n’est pas aussi simple et il y a un hic…&lt;/p&gt;
&lt;!-- end paragraph 48d8e9ad-261e-41b9-abfc-6dae00db8c8b--&gt;
&lt;!-- begin code aa9e4fe1-6b48-4c0b-9a12-cc036d6513e7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-aa9e4fe1-6b48-4c0b-9a12-cc036d6513e7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val user = User(&amp;quot;uuid&amp;quot;)

val hours = getWorkingHoursLastWeek(user.id) // It compiles 💀&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code aa9e4fe1-6b48-4c0b-9a12-cc036d6513e7--&gt;
&lt;!-- begin paragraph c4a46946-0fbb-4585-82ab-916fecd0ff65--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c4a46946-0fbb-4585-82ab-916fecd0ff65&quot;&gt;Les alias de types aiguillent les programmeurs, mais pas le compilateur. Pour lui, ce n’est que des simples String sous le capot. Il va falloir changer de stratégie.&lt;/p&gt;
&lt;!-- end paragraph c4a46946-0fbb-4585-82ab-916fecd0ff65--&gt;
&lt;!-- begin heading_1 2ff05ddb-6e7f-4e87-8a4a-f06f23941ea9--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2ff05ddb-6e7f-4e87-8a4a-f06f23941ea9&quot;&gt;Utiliser une case class&lt;/h2&gt;
&lt;!-- end heading_1 2ff05ddb-6e7f-4e87-8a4a-f06f23941ea9--&gt;
&lt;!-- begin paragraph dcd5553a-0802-4f1d-9951-5e44fdaa925f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dcd5553a-0802-4f1d-9951-5e44fdaa925f&quot;&gt;Reprenons l’exemple précédent, mais au lieu d’utiliser un alias de type, nous allons utiliser une case class pour représenter notre id :&lt;/p&gt;
&lt;!-- end paragraph dcd5553a-0802-4f1d-9951-5e44fdaa925f--&gt;
&lt;!-- begin code 884ef9d4-fca1-423e-ae7c-5af8168f23c4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-884ef9d4-fca1-423e-ae7c-5af8168f23c4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class User (
	id: UserId
)

case class UserId(value: String)

case class Team (
	id: TeamId,
	users: List[User]
)

case class TeamId(value: String)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 884ef9d4-fca1-423e-ae7c-5af8168f23c4--&gt;
&lt;!-- begin paragraph 115aa896-0430-4c5b-a03e-aad2249ad118--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-115aa896-0430-4c5b-a03e-aad2249ad118&quot;&gt;Si on reprend là où on s’était arrêté :&lt;/p&gt;
&lt;!-- end paragraph 115aa896-0430-4c5b-a03e-aad2249ad118--&gt;
&lt;!-- begin code bc6fb0ed-e1c3-4b30-aa86-7954fe987959--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bc6fb0ed-e1c3-4b30-aa86-7954fe987959&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val user = User(UserId(&amp;quot;uuid&amp;quot;))

val hours = getWorkingHoursLastWeek(user.id) // It does not compile 🔥&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bc6fb0ed-e1c3-4b30-aa86-7954fe987959--&gt;
&lt;!-- begin paragraph 86e4c174-8336-411f-bdd5-6fb9924b95d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86e4c174-8336-411f-bdd5-6fb9924b95d1&quot;&gt;C’est plutôt pas mal ! Notre fonction nous indiquera immédiatement si l’id qu’on lui renseigne est bien un id de team ou pas. Cela rajoute un peu d’overhead puisqu’il faut wrapper l’id dans une case class. Mais en réalité, hormis dans les tests, on ne le fait quasiment jamais. Généralement, l’utilisateur sera créé suite à un processus de désérialisation.&lt;/p&gt;
&lt;!-- end paragraph 86e4c174-8336-411f-bdd5-6fb9924b95d1--&gt;
&lt;!-- begin paragraph 768f57fb-4ce4-4c54-829d-0994816bfb5b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-768f57fb-4ce4-4c54-829d-0994816bfb5b&quot;&gt;À ce sujet, je vous conseille de vous-même définir des Encoders et des Decoders qui imite le comportement d’un String dans ce cas précis, voici un exemple avec zio-json :&lt;/p&gt;
&lt;!-- end paragraph 768f57fb-4ce4-4c54-829d-0994816bfb5b--&gt;
&lt;!-- begin code 5dec4e2b-db3d-4e5e-82d3-f253dd93b630--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5dec4e2b-db3d-4e5e-82d3-f253dd93b630&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class UserId(value: String)

object UserId {
	implicit val codec:JsonCodec[UserId] = JsonCodec.string.transform(UserId.apply, _.value)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5dec4e2b-db3d-4e5e-82d3-f253dd93b630--&gt;
&lt;!-- begin paragraph b278264e-f445-4c4b-a864-658c988b775e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b278264e-f445-4c4b-a864-658c988b775e&quot;&gt; En utilisant ce codec, le JSON ressemblera à ceci :&lt;/p&gt;
&lt;!-- end paragraph b278264e-f445-4c4b-a864-658c988b775e--&gt;
&lt;!-- begin code df5989e0-a94c-4901-b2b9-2aa5fd75d39f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-df5989e0-a94c-4901-b2b9-2aa5fd75d39f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;{&amp;quot;id&amp;quot;: &amp;quot;uuid&amp;quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code df5989e0-a94c-4901-b2b9-2aa5fd75d39f--&gt;
&lt;!-- begin paragraph e34d74de-255f-4111-8c3e-e9df9f0dec98--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e34d74de-255f-4111-8c3e-e9df9f0dec98&quot;&gt;Alors que si on utilise l’auto-dérivation, le JSON ressemblera à :&lt;/p&gt;
&lt;!-- end paragraph e34d74de-255f-4111-8c3e-e9df9f0dec98--&gt;
&lt;!-- begin code db6f5eae-345e-465f-908d-717bc98f1c88--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-db6f5eae-345e-465f-908d-717bc98f1c88&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;{&amp;quot;id&amp;quot;: {&amp;quot;value&amp;quot;: &amp;quot;uuid&amp;quot;}}   &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code db6f5eae-345e-465f-908d-717bc98f1c88--&gt;
&lt;!-- begin paragraph 50eb9095-d462-4b27-ab43-ab6fd37a5ef4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50eb9095-d462-4b27-ab43-ab6fd37a5ef4&quot;&gt;Cela alourdit le format pour rien puisque &lt;code&gt;value&lt;/code&gt; ne donne aucune information supplémentaire.&lt;/p&gt;
&lt;!-- end paragraph 50eb9095-d462-4b27-ab43-ab6fd37a5ef4--&gt;
&lt;!-- begin paragraph adb2a238-2f06-46cd-8222-1d8701c8a406--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-adb2a238-2f06-46cd-8222-1d8701c8a406&quot;&gt;Wrapper l’id a aussi un autre avantage, vous ne pouvez plus utiliser les opérations de String sur un &lt;code&gt;UserId&lt;/code&gt;. Concaténer des ids n’a, par exemple, aucun sens. Vous pouvez donc définir vos propres opérations et créer une véritable API autour de votre type.&lt;/p&gt;
&lt;!-- end paragraph adb2a238-2f06-46cd-8222-1d8701c8a406--&gt;
&lt;!-- begin paragraph e7d2c0ce-0246-4a97-90b8-b5f30b5b976d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e7d2c0ce-0246-4a97-90b8-b5f30b5b976d&quot;&gt;Mais il y a encore un hic… Cette façon de faire va créer un overhead qui, sur le long terme, peut impacter les performances de vos applications. Il va falloir peaufiner notre stratégie.&lt;/p&gt;
&lt;!-- end paragraph e7d2c0ce-0246-4a97-90b8-b5f30b5b976d--&gt;
&lt;!-- begin heading_1 9f4c5933-0a69-4a94-b484-ee939cbeaca0--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9f4c5933-0a69-4a94-b484-ee939cbeaca0&quot;&gt;Utiliser une value class&lt;/h2&gt;
&lt;!-- end heading_1 9f4c5933-0a69-4a94-b484-ee939cbeaca0--&gt;
&lt;!-- begin paragraph 8c8802be-d7cf-4c1c-a458-b656593c0bf1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c8802be-d7cf-4c1c-a458-b656593c0bf1&quot;&gt;Il existe une manière pour enlever cette overhead au runtime : les value class (depuis Scala 2.10). Si vous voulez plus d’informations, vous pouvez vous rendre dans &lt;a href=&quot;https://docs.scala-lang.org/overviews/core/value-classes.html&quot;&gt;la documentation&lt;/a&gt; (liée au &lt;a href=&quot;https://docs.scala-lang.org/sips/value-classes.html&quot;&gt;SIP-15&lt;/a&gt;) sur le site du langage Scala. C’est assez simple à mettre en place en reprenant l’exemple précédent : &lt;/p&gt;
&lt;!-- end paragraph 8c8802be-d7cf-4c1c-a458-b656593c0bf1--&gt;
&lt;!-- begin code dfcc0284-80bc-4aa6-953a-bf0a58a1261a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dfcc0284-80bc-4aa6-953a-bf0a58a1261a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class User (
	id: UserId
)

case class UserId(value: String) extends AnyVal

case class Team (
	id: TeamId,
	users: List[User]
)

case class TeamId(value: String) extends AnyVal&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dfcc0284-80bc-4aa6-953a-bf0a58a1261a--&gt;
&lt;!-- begin paragraph b826c068-9c0b-46c5-95bc-590f41b7771b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b826c068-9c0b-46c5-95bc-590f41b7771b&quot;&gt;Pour vous, rien ne change. Mais au runtime tous les UserId et TeamId seront considérés comme des Strings. On élimine complétement l’overhead !&lt;/p&gt;
&lt;!-- end paragraph b826c068-9c0b-46c5-95bc-590f41b7771b--&gt;
&lt;!-- begin paragraph 8fd00fc0-52cc-4e2a-bc8d-de5d57a54b36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8fd00fc0-52cc-4e2a-bc8d-de5d57a54b36&quot;&gt;En fait, non ! Il y a encore un hic… J’ai menti, l’overhead n’est pas complétement éliminé. Je vous laisse lire la partie motivation des types opaques (que nous allons voir ci-dessous) dans la documentation du &lt;a href=&quot;https://docs.scala-lang.org/sips/opaque-types.html#motivation&quot;&gt;SIP-35&lt;/a&gt; pour en apprendre plus à ce sujet.&lt;/p&gt;
&lt;!-- end paragraph 8fd00fc0-52cc-4e2a-bc8d-de5d57a54b36--&gt;
&lt;!-- begin heading_1 33ca7d8f-05f7-4fbd-8dd3-49139f46bc6d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-33ca7d8f-05f7-4fbd-8dd3-49139f46bc6d&quot;&gt;Utiliser un opaque type (Scala 3 only)&lt;/h2&gt;
&lt;!-- end heading_1 33ca7d8f-05f7-4fbd-8dd3-49139f46bc6d--&gt;
&lt;!-- begin paragraph 2c27c56c-ae7c-4c06-bfb6-ce7778838c4b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c27c56c-ae7c-4c06-bfb6-ce7778838c4b&quot;&gt;Comme dit précédemment, une value class n’élimine pas complétement l’overhead. Scala 3 introduit en conséquence une nouvelle feature qui est censé régler ce problème, à savoir les &lt;a href=&quot;https://docs.scala-lang.org/scala3/reference/other-new-features/opaques.html&quot;&gt;&lt;i&gt;opaques types&lt;/i&gt;&lt;/a&gt;. Reprenons donc notre exemple :&lt;/p&gt;
&lt;!-- end paragraph 2c27c56c-ae7c-4c06-bfb6-ce7778838c4b--&gt;
&lt;!-- begin code c5dc2578-f9ee-4ef9-a732-df813e51f153--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c5dc2578-f9ee-4ef9-a732-df813e51f153&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class User (
	id: UserId
)

opaque type UserId = String

object UserId {
	def apply(value: String): UserId = value
}

case class Team (
	id: TeamId,
	users: List[User]
)

opaque type TeamId = String

object TeamId {
	def apply(value: String): TeamId = value
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c5dc2578-f9ee-4ef9-a732-df813e51f153--&gt;
&lt;!-- begin paragraph 13082f42-9e3b-4f21-8999-9c19d01d3781--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-13082f42-9e3b-4f21-8999-9c19d01d3781&quot;&gt;Cette manière de faire ne comporte plus aucun overhead et répond 100% à notre problématique de départ.&lt;/p&gt;
&lt;!-- end paragraph 13082f42-9e3b-4f21-8999-9c19d01d3781--&gt;
&lt;!-- begin paragraph fc0ae361-f05c-44cf-9ff4-fbad01a6e756--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fc0ae361-f05c-44cf-9ff4-fbad01a6e756&quot;&gt;Mais il y a encore un hic… Pour l’avoir utilisé à titre personnel, ces derniers ne sont pas encore bien supportés par les différentes librairies qui composent l’écosystème Scala. J’ai eu notamment des problèmes avec &lt;a href=&quot;https://tapir.softwaremill.com/en/latest/index.html&quot;&gt;Tapir&lt;/a&gt; et &lt;a href=&quot;https://github.com/zio/zio-quill&quot;&gt;ZIO quill&lt;/a&gt; notamment.&lt;/p&gt;
&lt;!-- end paragraph fc0ae361-f05c-44cf-9ff4-fbad01a6e756--&gt;
&lt;!-- begin heading_1 a7804ca9-e161-4240-92a2-bb85b91ec283--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a7804ca9-e161-4240-92a2-bb85b91ec283&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 a7804ca9-e161-4240-92a2-bb85b91ec283--&gt;
&lt;!-- begin paragraph 5b89fdb3-f500-4024-b18f-61f8dca55171--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5b89fdb3-f500-4024-b18f-61f8dca55171&quot;&gt;Pour conclure, il y a eu un énorme chemin de parcouru pour arriver à avoir des sous types safes et sans overhead. Avoir des sous types qui précisent les différentes valeurs que nous manipulons permet d’avoir un code encore plus clair et précis qu’avec les types de base. C’est selon moi un vrai plus dans une codebase et je n’ai, pour l’instant, vu aucun point négatif à les utiliser. Les opaques types semblent extrêmement prometteurs dans ce sens-là. Mais du fait du manque de compatibilité actuel, je ne vous les conseille pas en production pour autant. De manière plus étendue, comme Scala 3 reste assez jeune (cette version est sortie l’année dernière), je considère qu’il vaut mieux attendre avant de l’envoyer en production. Particulièrement avec l’effort fourni par VirtusLab &lt;a href=&quot;https://virtuslab.com/scala-3-support-and-migration/#free-support-for-scala-2-to-3-migration&quot;&gt;qui propose gratuitement de migrer les projets Scala 2 vers 3&lt;/a&gt;, quasiment tout l’écosystème est d’ores et déjà fonctionnel en Scala 3 ! Selon moi, ce sont surtout les IDEs qui peinent pour le moment à s’adapter.&lt;/p&gt;
&lt;!-- end paragraph 5b89fdb3-f500-4024-b18f-61f8dca55171--&gt;
&lt;!-- begin paragraph b37bcdcf-451f-48d3-8777-8977d643d710--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b37bcdcf-451f-48d3-8777-8977d643d710&quot;&gt;À titre personnel, je me suis résigné à repasser aux value classes pour le moment. &lt;/p&gt;
&lt;!-- end paragraph b37bcdcf-451f-48d3-8777-8977d643d710--&gt;
&lt;!-- begin paragraph 01f860d3-c835-4c70-9001-d72098952b3b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-01f860d3-c835-4c70-9001-d72098952b3b&quot;&gt;Dans cet article, nous avons vu les solutions que proposait le langage Scala. Dans le prochain article, nous passerons aux différentes librairies qui composent cet écosystème et qui nous permet d’être encore plus précis et safe.&lt;/p&gt;
&lt;!-- end paragraph 01f860d3-c835-4c70-9001-d72098952b3b--&gt;
&lt;!-- begin paragraph 2ffd5d24-3b45-4c1e-bbc8-d4d07fcb8592--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2ffd5d24-3b45-4c1e-bbc8-d4d07fcb8592&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2ffd5d24-3b45-4c1e-bbc8-d4d07fcb8592--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:ddd25ab7b53541f68b63d44d1b0ee0cc</id>
    <title>Scala Thank You : Organizing Scala.IO</title>
    <updated>2022-11-09T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/scala-thank-you-organizing-scala-io.html"/>
    <!--summary Sum up, and thanks on Scala.IO-->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="ScalaIO"></category>    <category term="Conférence"></category>    <category term="FP"></category>    <category term="Scala"></category>    <category term="Community"></category>    <content type="html">
&lt;!-- begin paragraph f7d39a36-adb2-4a2c-bcf7-519433e20ae9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f7d39a36-adb2-4a2c-bcf7-519433e20ae9&quot;&gt;&lt;a href=&quot;http://scala.io/&quot;&gt;Scala.IO&lt;/a&gt; is done for this year, only a couple of things to wrap up (publishing the talks, a bit of accounting, …) before we move on to the organization of Scala.IO 2023! &lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;&lt;u&gt;This edition was outstanding&lt;/u&gt;&lt;/u&gt;. We had such a warm support from the community, from our speakers, our sponsors and our attendees. It was a bit overwhelming at some point, and very very friendly.&lt;br /&gt;
&lt;br /&gt;
Personally, I have been in the Scala Community for the major part of my professional life, so seeing people again, growing with them and sharing those moments is highly rewarding.&lt;/p&gt;
&lt;!-- end paragraph f7d39a36-adb2-4a2c-bcf7-519433e20ae9--&gt;
&lt;!-- begin heading_1 76faa0a6-3746-491a-a613-bfd4b4c88d72--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-76faa0a6-3746-491a-a613-bfd4b4c88d72&quot;&gt;What went wrong?&lt;/h2&gt;
&lt;!-- end heading_1 76faa0a6-3746-491a-a613-bfd4b4c88d72--&gt;
&lt;!-- begin paragraph d16d0b42-fb1f-4f43-abc2-870a6997f875--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d16d0b42-fb1f-4f43-abc2-870a6997f875&quot;&gt;Not much, it went well, a couple of things we need to fix for the next edition : &lt;/p&gt;
&lt;!-- end paragraph d16d0b42-fb1f-4f43-abc2-870a6997f875--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;We had to cancel workshops, we announced them too late, so we were unable to gather enough participants. However, having the Scala Spree was a success.&lt;/li&gt;&lt;li&gt;Video problems with MacOS. That led to reprogramming the talk by Jules Ivanic (GraphQL/ zio-test / Caliban) + a couple of issues repeatedly during the day. We have a fix and a backup process for next year.&lt;/li&gt;&lt;li&gt;Catering problems. Water and drinks were not freely available during the afternoon due to a misunderstanding with our caterer. That will be fixed, we will make sure you stay hydrated and caffeinated if needed.&lt;/li&gt;&lt;li&gt;Women attendance and beginner attendance. We had very few attendees (and speakers) from those two populations. We don’t have a fix on it yet, but we will make sure to make efforts in that direction to gather more new and diverse people for the next edition.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 b335a29a-341e-41f7-a74b-fcf6fc2e5ae8--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-b335a29a-341e-41f7-a74b-fcf6fc2e5ae8&quot;&gt;How do you manage? &lt;/h2&gt;
&lt;!-- end heading_1 b335a29a-341e-41f7-a74b-fcf6fc2e5ae8--&gt;
&lt;!-- begin paragraph 25ad0854-70e3-4e2d-a421-1cc718291190--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-25ad0854-70e3-4e2d-a421-1cc718291190&quot;&gt;There have been plenty of friendly questions in that direction. Short answer: we don&amp;#39;t!&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph 25ad0854-70e3-4e2d-a421-1cc718291190--&gt;
&lt;!-- begin paragraph 6fbef33b-376a-4930-97c0-2fcbd7a93923--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6fbef33b-376a-4930-97c0-2fcbd7a93923&quot;&gt;More seriously, from year to year, the team pretty much follows this structure: &lt;/p&gt;
&lt;!-- end paragraph 6fbef33b-376a-4930-97c0-2fcbd7a93923--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;core organization team &lt;/b&gt;(managing sponsors, suppliers, speakers, tickets, communications before the event + leading the pace during the event).&lt;/li&gt;&lt;li&gt;a&lt;b&gt;dditional team members&lt;/b&gt; (CFP, communications, support before the event + crowd and room management during the event).&lt;/li&gt;&lt;li&gt;and &lt;b&gt;outside event &lt;/b&gt;&lt;b&gt;organizer&lt;/b&gt;&lt;b&gt;s&lt;/b&gt;, especially for Paris (suppliers, stands, venue … and everything that can go wrong during the installation of the event)&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph d29df7ec-c6b9-4f67-8a83-4aa73f68e7ef--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d29df7ec-c6b9-4f67-8a83-4aa73f68e7ef&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d29df7ec-c6b9-4f67-8a83-4aa73f68e7ef--&gt;
&lt;!-- begin paragraph 71c5ce2a-4c46-4072-8529-f430f62bcec1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71c5ce2a-4c46-4072-8529-f430f62bcec1&quot;&gt;We have a very efficient event organizer in Paris (&lt;a href=&quot;http://321idcom.fr/&quot;&gt;http://321idcom.fr/&lt;/a&gt;), Karine manages all the things that could go wrong fast directly, and she comes back to us for the rest which makes everything easy.&lt;/p&gt;
&lt;!-- end paragraph 71c5ce2a-4c46-4072-8529-f430f62bcec1--&gt;
&lt;!-- begin paragraph 16a1cc71-d10d-44ed-bd49-fe42e92b0875--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-16a1cc71-d10d-44ed-bd49-fe42e92b0875&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 16a1cc71-d10d-44ed-bd49-fe42e92b0875--&gt;
&lt;!-- begin paragraph caff8d42-c9b1-48c7-9bee-0c29b185bc3a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-caff8d42-c9b1-48c7-9bee-0c29b185bc3a&quot;&gt;We had a lot of core team members from the French Scala community over the years, with &lt;a href=&quot;https://twitter.com/jeanhelou&quot;&gt;Jean Helou&lt;/a&gt; being invested a lot in the conference since 2013, &lt;a href=&quot;https://twitter.com/fsznajderman&quot;&gt;Fabrice Sznajderman&lt;/a&gt; since 2016 … &lt;a href=&quot;https://twitter.com/ValentinKasas&quot;&gt;Valentin Kasas&lt;/a&gt; for past editions in Lyon, &lt;a href=&quot;https://twitter.com/fsarradin&quot;&gt;François Sarradin&lt;/a&gt; and I since 2013, on and off depending on the editions… &lt;/p&gt;
&lt;!-- end paragraph caff8d42-c9b1-48c7-9bee-0c29b185bc3a--&gt;
&lt;!-- begin divider c96235eb-e7d5-4223-9e45-008eceb3bd79--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-c96235eb-e7d5-4223-9e45-008eceb3bd79&quot;&gt;
&lt;!-- end divider c96235eb-e7d5-4223-9e45-008eceb3bd79--&gt;
&lt;!-- begin paragraph ecb355a1-38fe-42c5-bb49-ac40994fff25--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ecb355a1-38fe-42c5-bb49-ac40994fff25&quot;&gt;This year we pushed and helped Fabrice restart the conference as fast as possible, with people from Univalence (&lt;a href=&quot;https://twitter.com/dylandmrl&quot;&gt;Dylan Do Amaral&lt;/a&gt;, François Sarradin and I), with additional members (&lt;a href=&quot;https://www.linkedin.com/in/valentinbergeron/&quot;&gt;Valentin Bergeron&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/nicolas-couturier-a76a922/&quot;&gt;Nicolas Couturier&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/william-machi-aa6ba4196&quot;&gt;William Machi&lt;/a&gt;) to help with the call for papers, tickets, suppliers …&lt;/p&gt;
&lt;!-- end paragraph ecb355a1-38fe-42c5-bb49-ac40994fff25--&gt;
&lt;!-- begin paragraph b924f940-1851-41ac-8fec-87b91f4535cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b924f940-1851-41ac-8fec-87b91f4535cb&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b924f940-1851-41ac-8fec-87b91f4535cb--&gt;
&lt;!-- begin paragraph 97cc83d0-1e20-425b-b51e-dc351222c975--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-97cc83d0-1e20-425b-b51e-dc351222c975&quot;&gt;We were joined for the day of the event by Jean Helou himself, and &lt;a href=&quot;https://warisradji.com/&quot;&gt;Warris Radji &lt;/a&gt;who help a lot with the organization during the day.&lt;br /&gt;
&lt;br /&gt;
With all of that, things fall in motion once set, and we are happy with the outcome.&lt;/p&gt;
&lt;!-- end paragraph 97cc83d0-1e20-425b-b51e-dc351222c975--&gt;
&lt;!-- begin heading_1 7a0e85d6-1f2a-43a2-abc1-7cd03e95ba8f--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-7a0e85d6-1f2a-43a2-abc1-7cd03e95ba8f&quot;&gt;Why and what’s next?&lt;/h2&gt;
&lt;!-- end heading_1 7a0e85d6-1f2a-43a2-abc1-7cd03e95ba8f--&gt;
&lt;!-- begin paragraph 592bc44f-618d-49ac-b20b-9886f373e67b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-592bc44f-618d-49ac-b20b-9886f373e67b&quot;&gt;&lt;a href=&quot;http://scala.io/&quot;&gt;Scala.IO&lt;/a&gt; was created in 2013 to help people use Scala professionally. At the time, the population in the meetups were split between people that would use Scala at work and others that would use it on the weekends. The creation of the conference helped with the implantation of Scala, especially for hobbyists to make a career out of it.&lt;/p&gt;
&lt;!-- end paragraph 592bc44f-618d-49ac-b20b-9886f373e67b--&gt;
&lt;!-- begin paragraph 50dd1b53-12e5-409a-a06b-952bbb44acb8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50dd1b53-12e5-409a-a06b-952bbb44acb8&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 50dd1b53-12e5-409a-a06b-952bbb44acb8--&gt;
&lt;!-- begin paragraph 9a3edebb-5112-44ea-96e5-514748f36efd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9a3edebb-5112-44ea-96e5-514748f36efd&quot;&gt;This edition was significant for us, we haven’t really had time to meet and share our passion for Scala, and its ecosystem over the past 3 years. It was lovely to meet again.&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph 9a3edebb-5112-44ea-96e5-514748f36efd--&gt;
&lt;!-- begin paragraph 5aa2009e-61b8-48c5-aa24-c797d83189df--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5aa2009e-61b8-48c5-aa24-c797d83189df&quot;&gt;Since Scala has gained a lot more traction, in the future we aim to leverage the conference to welcome newcomers and ease the transition to Scala using the friendly vibes and kindness we have around the event.&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph 5aa2009e-61b8-48c5-aa24-c797d83189df--&gt;
&lt;!-- begin paragraph 436e9c8e-2b7c-4ca3-bcb3-9fe377b683e3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-436e9c8e-2b7c-4ca3-bcb3-9fe377b683e3&quot;&gt;It was very interesting to be able to connect during the event with the Scala Center and Darja to start to see what can be done for the community with the leverage of common resources, and help go in that direction.&lt;br /&gt;
&lt;br /&gt;
There is a lot of movement in Scala with the adoption of Scala 3, the different IOs, and all the great things that are coming in our ecosystem. We are pleased to be a part of it and follow through.&lt;/p&gt;
&lt;!-- end paragraph 436e9c8e-2b7c-4ca3-bcb3-9fe377b683e3--&gt;
&lt;!-- begin heading_1 4a2b0e6f-f964-4098-ac78-0ab0c13a1b9c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-4a2b0e6f-f964-4098-ac78-0ab0c13a1b9c&quot;&gt;Next format&lt;/h2&gt;
&lt;!-- end heading_1 4a2b0e6f-f964-4098-ac78-0ab0c13a1b9c--&gt;
&lt;!-- begin paragraph 4af7e559-15c6-45df-a462-d442db0dd727--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4af7e559-15c6-45df-a462-d442db0dd727&quot;&gt;We are going back to what we were doing. For the next edition, on the 26th and 27th of October 2023, we are aiming for something closer to our old format:&lt;/p&gt;
&lt;!-- end paragraph 4af7e559-15c6-45df-a462-d442db0dd727--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;2 days&lt;/li&gt;&lt;li&gt;3 tracks, 48 talks &lt;/li&gt;&lt;li&gt;at least 12 talks for beginner in Scala (dedicated track for 1.5 days)&lt;/li&gt;&lt;li&gt;Workshops on Wednesday (beginner, effect systems, spark, …)&lt;/li&gt;&lt;li&gt;a bigger community Party on Thursday&lt;/li&gt;&lt;li&gt;crêpes (we make sure they come back to the event)&lt;/li&gt;&lt;li&gt;…&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 11ad315b-a99e-436a-927d-e5a9062b74ef--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-11ad315b-a99e-436a-927d-e5a9062b74ef&quot;&gt;&lt;br /&gt;
For 300, up to 400 attendees, if we can convince more students and beginners to come.&lt;/p&gt;
&lt;!-- end paragraph 11ad315b-a99e-436a-927d-e5a9062b74ef--&gt;
&lt;!-- begin paragraph a40b5c5a-b9ab-4265-880c-3902925f4a0c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a40b5c5a-b9ab-4265-880c-3902925f4a0c&quot;&gt;We will use all the experience gathered for the past 10 years, and we will make a well-rounded event in Paris, and we can’t wait to share that with you again!&lt;/p&gt;
&lt;!-- end paragraph a40b5c5a-b9ab-4265-880c-3902925f4a0c--&gt;
&lt;!-- begin heading_1 5c3dacdd-46ba-412d-a63c-e1e4f99e43c4--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5c3dacdd-46ba-412d-a63c-e1e4f99e43c4&quot;&gt;Thank you and see you soon &lt;/h2&gt;
&lt;!-- end heading_1 5c3dacdd-46ba-412d-a63c-e1e4f99e43c4--&gt;
&lt;!-- begin paragraph 05961118-6b39-4f45-82a4-e1e38fe71228--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-05961118-6b39-4f45-82a4-e1e38fe71228&quot;&gt;As always, thanks a lot to Martin Odersky, for everything and especially greeting us with a great keynote.&lt;br /&gt;
&lt;br /&gt;
Thanks to all the attendees, speakers, sponsors for making that moment memorable.&lt;br /&gt;
&lt;br /&gt;
See you very soon!&lt;/p&gt;
&lt;!-- end paragraph 05961118-6b39-4f45-82a4-e1e38fe71228--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:bb1ec3911d08498ca4514f53d0cf00f0</id>
    <title>Dérivation récursive de typeclass avec Scala 3</title>
    <updated>2022-11-09T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/derivation-recursive-de-typeclass-avec-scala-3.html"/>
    <!--summary Un article sur la dérivation de typeclass récursive en Scala 3-->    <author>
      <name>Bastien Guihard</name>
      <uri>https://univalence.io/blog/auteurs/bastien-guihard.html</uri>
    </author>        <category term="Magnolia"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin paragraph 9dd7153c-c1ba-47e9-8af3-6a3df656a4c9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9dd7153c-c1ba-47e9-8af3-6a3df656a4c9&quot;&gt;&lt;a href=&quot;https://univalence.io/blog/articles/magnolia-chopped-and-screwed/&quot;&gt;Dans un précédent article&lt;/a&gt;, nous avions parlé de dérivation de typeclass en Scala 2 avec Magnolia. Nous avions vu qu’il est possible de dériver des  &lt;code&gt;typeclass&lt;/code&gt; pour des &lt;code&gt;sealed trait&lt;/code&gt; à l’aide de la méthode &lt;code&gt;split&lt;/code&gt;. En revanche, nous avons aussi observé que dans les cas de dérivation récursive, il nous fallait forcer la dérivation de notre &lt;code&gt;sealed trait&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 9dd7153c-c1ba-47e9-8af3-6a3df656a4c9--&gt;
&lt;!-- begin paragraph a1cc8910-c32e-48ae-bc11-740722919bbd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a1cc8910-c32e-48ae-bc11-740722919bbd&quot;&gt;Dans cet article, nous allons tâcher de montrer comment faire une dérivation récursive de typeclass avec magnolia en Scala 3.&lt;/p&gt;
&lt;!-- end paragraph a1cc8910-c32e-48ae-bc11-740722919bbd--&gt;
&lt;!-- begin heading_2 b86c3993-869b-47ef-8fb5-19a8fdb64040--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b86c3993-869b-47ef-8fb5-19a8fdb64040&quot;&gt;Reprenons…&lt;/h3&gt;
&lt;!-- end heading_2 b86c3993-869b-47ef-8fb5-19a8fdb64040--&gt;
&lt;!-- begin paragraph d45c9fe6-cc6a-4b11-96b0-265ab23f16ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d45c9fe6-cc6a-4b11-96b0-265ab23f16ad&quot;&gt;Imaginons que l’on ait le co-produit suivant :&lt;/p&gt;
&lt;!-- end paragraph d45c9fe6-cc6a-4b11-96b0-265ab23f16ad--&gt;
&lt;!-- begin code aa88881d-1131-4b70-aa05-4571b5a501f2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-aa88881d-1131-4b70-aa05-4571b5a501f2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;sealed trait A
case object B(str: String) extends A
case class C(next: A)      extends A&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code aa88881d-1131-4b70-aa05-4571b5a501f2--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/bb1ec391-1d08-498c-a451-4f53d0cf00f0/hrops(1).svg&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Figure 1 : Trait &lt;code&gt;A&lt;/code&gt; et case class / objects&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph ac169a7e-c8e0-4b45-a6a6-0ec220ea6e16--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ac169a7e-c8e0-4b45-a6a6-0ec220ea6e16&quot;&gt;On souhaite dériver la typeclass &lt;code&gt;Show&lt;/code&gt; suivante pour toute instance de &lt;code&gt;A&lt;/code&gt; : &lt;/p&gt;
&lt;!-- end paragraph ac169a7e-c8e0-4b45-a6a6-0ec220ea6e16--&gt;
&lt;!-- begin code a808847c-8504-40df-9bd9-2c71eed9b7ab--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a808847c-8504-40df-9bd9-2c71eed9b7ab&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Show[T] {
  def show(t: T): String
}

trait ShowObj[T] extends Show[T] {
  def showObj(t: T): String
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a808847c-8504-40df-9bd9-2c71eed9b7ab--&gt;
&lt;!-- begin paragraph 466224a5-8a0c-419a-ae17-9c32ed9b7e24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-466224a5-8a0c-419a-ae17-9c32ed9b7e24&quot;&gt;On peut se demander pourquoi nous avons &lt;code&gt;Show&lt;/code&gt; et &lt;code&gt;ShowObj&lt;/code&gt; ?&lt;/p&gt;
&lt;!-- end paragraph 466224a5-8a0c-419a-ae17-9c32ed9b7e24--&gt;
&lt;!-- begin paragraph ce1daa88-a122-48ac-9d59-1932f97e07a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce1daa88-a122-48ac-9d59-1932f97e07a9&quot;&gt;Ce pattern peut être utile lorsque l’on ne souhaite pas implémenter des comportements spécifiques à un type.&lt;/p&gt;
&lt;!-- end paragraph ce1daa88-a122-48ac-9d59-1932f97e07a9--&gt;
&lt;!-- begin paragraph 7551d4ff-63a6-431a-aca6-9204b37a890b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7551d4ff-63a6-431a-aca6-9204b37a890b&quot;&gt;Par exemple, on peut imaginer que l’on ait besoin de &lt;code&gt;Show&lt;/code&gt; pour afficher les types primitifs sur la console (&lt;code&gt;String&lt;/code&gt;,&lt;code&gt; Int&lt;/code&gt;…) et &lt;code&gt;ShowObj&lt;/code&gt; pour afficher les &lt;code&gt;case class&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 7551d4ff-63a6-431a-aca6-9204b37a890b--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/bb1ec391-1d08-498c-a451-4f53d0cf00f0/hrops.svg&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Figure 2 : Schéma de dérivation de typeclass qui explique pourquoi avoir &lt;code&gt;Show&lt;/code&gt; et &lt;code&gt;ShowObj&lt;/code&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;Unsupported block 2e2d5288-2960-45c8-9bcc-ef822fbcd276 unsupported
in https://www.notion.so/D-rivation-r-cursive-de-typeclass-avec-Scala-3-bb1ec3911d08498ca4514f53d0cf00f0&lt;/code&gt;&lt;/pre&gt;
&lt;!-- begin paragraph 9fe22340-aa96-4424-a9bd-3573c6155857--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9fe22340-aa96-4424-a9bd-3573c6155857&quot;&gt;Dans notre exemple, on va essayer de dériver Show de manière à obtenir : &lt;/p&gt;
&lt;!-- end paragraph 9fe22340-aa96-4424-a9bd-3573c6155857--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;Show[String]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Show[Int]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[CaseClassOne]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[CaseClassTwo]&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph dbc628a1-3a7a-4221-8cb7-e42bf9c4aa30--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dbc628a1-3a7a-4221-8cb7-e42bf9c4aa30&quot;&gt;Maintenant, projetons notre exemple sur notre Trait &lt;code&gt;A&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph dbc628a1-3a7a-4221-8cb7-e42bf9c4aa30--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/bb1ec391-1d08-498c-a451-4f53d0cf00f0/hrops(2).svg&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Figure 3 : Schéma de dérivation de typeclass pour l’ADT de la &lt;i&gt;Figure 1&lt;/i&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 0ca479d9-c085-4f0f-bda4-9dbbd33bc4ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0ca479d9-c085-4f0f-bda4-9dbbd33bc4ac&quot;&gt;On remarque que l’on va souhaiter obtenir les dérivations suivantes : &lt;/p&gt;
&lt;!-- end paragraph 0ca479d9-c085-4f0f-bda4-9dbbd33bc4ac--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;Show[String]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[A]&lt;/code&gt; → va être obtenu par la méthode split&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[B]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[C]&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 60f1ad96-e3da-4b5c-9b9f-edf000dcbbd0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-60f1ad96-e3da-4b5c-9b9f-edf000dcbbd0&quot;&gt;On peut voir avec la figure 3 que &lt;code&gt;Show[C]&lt;/code&gt; va être récursive.&lt;/p&gt;
&lt;!-- end paragraph 60f1ad96-e3da-4b5c-9b9f-edf000dcbbd0--&gt;
&lt;!-- begin paragraph ce662605-c43b-43e5-8bb1-3c894c41ddd7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce662605-c43b-43e5-8bb1-3c894c41ddd7&quot;&gt;En effet, &lt;code&gt;C&lt;/code&gt; contient un &lt;code&gt;A&lt;/code&gt; qui peut être un &lt;code&gt;C&lt;/code&gt; qui contient un &lt;code&gt;A&lt;/code&gt;…&lt;/p&gt;
&lt;!-- end paragraph ce662605-c43b-43e5-8bb1-3c894c41ddd7--&gt;
&lt;!-- begin paragraph fe9b7cb3-5fe3-4533-b8fc-7725b1914316--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fe9b7cb3-5fe3-4533-b8fc-7725b1914316&quot;&gt;Nous avons vu qu’avec Magnolia en Scala 2, la dérivation récursive de typeclass semble avoir des bugs. Partons d’un exemple rapide proche de ce que nous voulons faire.&lt;/p&gt;
&lt;!-- end paragraph fe9b7cb3-5fe3-4533-b8fc-7725b1914316--&gt;
&lt;!-- begin code 1bcfdbeb-4225-4621-9db3-1bf8e366713c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1bcfdbeb-4225-4621-9db3-1bf8e366713c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Tc[T]

trait TcObj[T] extends Tc[T]

object ShowDerivation {
  type Typeclass[T] = Tc[T]

  def join[T](ctx: CaseClass[Tc, T]): TcObj[T] = new TcObj[T] {}
  def split[T](ctx: SealedTrait[Tc, T]): TcObj[T] = new TcObj[T] {}

  def gen[T]: Tc[T] = macro Magnolia.gen[T]
  def genObj[T]: TcObj[T] = macro Magnolia.gen[T]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1bcfdbeb-4225-4621-9db3-1bf8e366713c--&gt;
&lt;!-- begin paragraph 48e605c4-135b-4422-92f3-575698b34ba4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-48e605c4-135b-4422-92f3-575698b34ba4&quot;&gt;Voici l’utilisation de ce code.&lt;/p&gt;
&lt;!-- end paragraph 48e605c4-135b-4422-92f3-575698b34ba4--&gt;
&lt;!-- begin code 13d5b9cf-26c6-4aa7-9289-db4fbe8f491c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-13d5b9cf-26c6-4aa7-9289-db4fbe8f491c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;ShowDerivation.gen[B.type]    // success ☑️

ShowDerivation.genObj[B.type] // success ☑️

ShowDerivation.gen[A]         // success ☑️

//ShowDerivation.genObj[A]    // failure ❌
/*
type mismatch;
 found   : Playground.ShowDerivation.Typeclass[Playground.A]
    (which expands to)  Playground.Tc[Playground.A]
 required: Playground.TcObj[Playground.A]
*/

implicit val tca:Tc[A] = null

ShowDerivation.gen[C]        // success ☑️

ShowDerivation.genObj[C]     // success ☑️&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 13d5b9cf-26c6-4aa7-9289-db4fbe8f491c--&gt;
&lt;!-- begin heading_2 830a27dc-20c5-49b7-b0f6-37c8ba8f9aa4--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-830a27dc-20c5-49b7-b0f6-37c8ba8f9aa4&quot;&gt;Qu’en est-il en Scala 3 ?&lt;/h3&gt;
&lt;!-- end heading_2 830a27dc-20c5-49b7-b0f6-37c8ba8f9aa4--&gt;
&lt;!-- begin paragraph 278df5e1-0cec-4b99-b414-8285c4171661--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-278df5e1-0cec-4b99-b414-8285c4171661&quot;&gt;On est toujours obligé de forcer la dérivation. La documentation de Magnolia l’explique ici : &lt;a href=&quot;https://github.com/softwaremill/magnolia#limitations&quot;&gt;https://github.com/softwaremill/magnolia#limitations&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 278df5e1-0cec-4b99-b414-8285c4171661--&gt;
&lt;!-- begin heading_2 822adcc1-bb01-4d1e-8df9-62a5106c7834--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-822adcc1-bb01-4d1e-8df9-62a5106c7834&quot;&gt;Un exemple&lt;/h3&gt;
&lt;!-- end heading_2 822adcc1-bb01-4d1e-8df9-62a5106c7834--&gt;
&lt;!-- begin paragraph 309af8f3-4f26-4c93-b47c-4ffdc757e1b1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-309af8f3-4f26-4c93-b47c-4ffdc757e1b1&quot;&gt;Dans cet exemple, l’idée est de reprendre le problème de la &lt;code&gt;figure 2&lt;/code&gt;, mais en Scala 3.&lt;/p&gt;
&lt;!-- end paragraph 309af8f3-4f26-4c93-b47c-4ffdc757e1b1--&gt;
&lt;!-- begin paragraph e05b7acd-ae95-4cdf-98af-9c06b2d29b0f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e05b7acd-ae95-4cdf-98af-9c06b2d29b0f&quot;&gt;On va tâcher de dériver la typeclass &lt;code&gt;Show&lt;/code&gt; pour le trait suivant :&lt;/p&gt;
&lt;!-- end paragraph e05b7acd-ae95-4cdf-98af-9c06b2d29b0f--&gt;
&lt;!-- begin code a93db360-d17e-4416-92b5-ff7a36c146c3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a93db360-d17e-4416-92b5-ff7a36c146c3&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  sealed trait Kind
  case class Human(name: String) extends Kind
  case class Elf(elvishName: String) extends Kind
  case class Hybrid(kind: Kind, other: Kind) extends Kind&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a93db360-d17e-4416-92b5-ff7a36c146c3--&gt;
&lt;!-- begin paragraph ba19fad5-756b-4ae2-9e1d-b98bee25af79--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba19fad5-756b-4ae2-9e1d-b98bee25af79&quot;&gt;Le type &lt;code&gt;Kind&lt;/code&gt; représente donc soit des humains, soit des elfes, soit des êtres hybrides (avec deux noms ^^).&lt;/p&gt;
&lt;!-- end paragraph ba19fad5-756b-4ae2-9e1d-b98bee25af79--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/bb1ec391-1d08-498c-a451-4f53d0cf00f0/human-elf-noah.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph c4c1cb05-ed2d-4893-b1d2-898cb2bd6bd2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c4c1cb05-ed2d-4893-b1d2-898cb2bd6bd2&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c4c1cb05-ed2d-4893-b1d2-898cb2bd6bd2--&gt;
&lt;!-- begin paragraph 8ff57fd8-8f39-4140-9424-5c1e9230f0bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8ff57fd8-8f39-4140-9424-5c1e9230f0bc&quot;&gt;Reprenons le code de la figure 4 pour le porter en Scala 3 : &lt;/p&gt;
&lt;!-- end paragraph 8ff57fd8-8f39-4140-9424-5c1e9230f0bc--&gt;
&lt;!-- begin code a772356d-c357-4bb3-b07f-762f609bf725--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a772356d-c357-4bb3-b07f-762f609bf725&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Show extends Derivation[Show]{

  override def join[T](ctx: CaseClass[Show, T]): ShowObj[T] = new ShowObj[T] {???}

  override def split[T](ctx: SealedTrait[Show, T]): ShowObj[T] = new ShowObj[T] {???}

  inline given autoDerived[A](using Mirror.Of[A]): Show[A] =
    derived.asInstanceOf[Show[A]]

  inline given derivedObj[A](using Mirror.Of[A]): ShowObj[A] =
    derived.asInstanceOf[ShowObj[A]]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a772356d-c357-4bb3-b07f-762f609bf725--&gt;
&lt;!-- begin paragraph c6791d99-24f6-49cc-a2c2-b6b9e82a5a58--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c6791d99-24f6-49cc-a2c2-b6b9e82a5a58&quot;&gt;Comme on l’a vu dans la &lt;i&gt;Figure 3&lt;/i&gt; nous allons avoir besoin de &lt;code&gt;Show[String]&lt;/code&gt; pour afficher les &lt;code&gt;String&lt;/code&gt; sur la console&lt;/p&gt;
&lt;!-- end paragraph c6791d99-24f6-49cc-a2c2-b6b9e82a5a58--&gt;
&lt;!-- begin code bc18e73a-6f59-48c0-b9d8-9f8e1eca818c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bc18e73a-6f59-48c0-b9d8-9f8e1eca818c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  given Show[String] with
    def show(t: String): String = s&amp;quot;$t&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bc18e73a-6f59-48c0-b9d8-9f8e1eca818c--&gt;
&lt;!-- begin paragraph d06d0571-df23-4494-9d1c-810e8990ea22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d06d0571-df23-4494-9d1c-810e8990ea22&quot;&gt;Désormais, nous avons un moyen d’afficher nos &lt;code&gt;String&lt;/code&gt; et le squelette principal de notre dérivation.&lt;/p&gt;
&lt;!-- end paragraph d06d0571-df23-4494-9d1c-810e8990ea22--&gt;
&lt;!-- begin paragraph acffb1d5-d4a3-4e46-8036-8913d4513787--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-acffb1d5-d4a3-4e46-8036-8913d4513787&quot;&gt;Il nous faut alors implémenter &lt;code&gt;split&lt;/code&gt; et &lt;code&gt;join&lt;/code&gt; qui vont nous permettre de dériver les typeclass pour les sealed trait et les case class :&lt;/p&gt;
&lt;!-- end paragraph acffb1d5-d4a3-4e46-8036-8913d4513787--&gt;
&lt;!-- begin code c7a21f04-2374-45dd-ad18-7d749117bda6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c7a21f04-2374-45dd-ad18-7d749117bda6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  override def join[T](ctx: CaseClass[Show, T]): ShowObj[T] = new ShowObj[T] {
    override def show(t: T): String = t.toString.toUpperCase()

    override def showObj(t: T): String =
      ctx.params.map(param =&amp;gt; param.typeclass match {
        case showObj: ShowObj[param.PType] =&amp;gt;
          s&amp;quot;${param.label} part: [ ${showObj.showObj(param.deref(t))} ] &amp;quot;
        case show: Show[param.PType] =&amp;gt;
          s&amp;quot;${param.label}: [${show.show(param.deref(t))}]&amp;quot;
      }).mkString
  }

  override def split[T](ctx: SealedTrait[Show, T]): ShowObj[T] = new ShowObj[T] {
    override def show(t: T): String = ctx.choose(t) { sub =&amp;gt;
      sub.typeclass.show(sub.cast(t))
    }
    override def showObj(t: T): String = ctx.choose(t) { sub =&amp;gt;
      val tc = sub.typeclass.asInstanceOf[ShowObj[T]]
      tc.showObj(sub.cast(t))
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c7a21f04-2374-45dd-ad18-7d749117bda6--&gt;
&lt;!-- begin paragraph 509128f6-5819-49f5-ba42-6dea36a6c50b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-509128f6-5819-49f5-ba42-6dea36a6c50b&quot;&gt;Arrêtons-nous sur deux choses : &lt;/p&gt;
&lt;!-- end paragraph 509128f6-5819-49f5-ba42-6dea36a6c50b--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Le &lt;code&gt;showObj&lt;/code&gt; de &lt;code&gt;join&lt;/code&gt; :&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;On regarde si on souhaite dériver un &lt;code&gt;Show&lt;/code&gt; ou un &lt;code&gt;ShowObj&lt;/code&gt; de notre case class (en l’occurrence, on voudra un &lt;code&gt;Show&lt;/code&gt;, si on a un type primitif, ou un &lt;code&gt;ShowObj&lt;/code&gt;, si on a une case class).&lt;/li&gt;&lt;li&gt;On formate correctement les valeurs des paramètres en les déréférençant, c&amp;#39;est-à-dire en récupérant leur valeur “réelle”.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Le &lt;code&gt;showObj&lt;/code&gt; de &lt;code&gt;split&lt;/code&gt; :&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;On parcourt tous les sous-types d’un sealed trait (en l’occurrence, les sous-types de &lt;code&gt;Kind&lt;/code&gt;) pour trouver celui que l’on cherche à dériver.&lt;/li&gt;&lt;li&gt;On cast notre sous-type dans son type réel (eg. j’ai un sous-type de &lt;code&gt;Kind&lt;/code&gt;, si je vois que son type réel est &lt;code&gt;Elf&lt;/code&gt;, alors je le cast en &lt;code&gt;Elf&lt;/code&gt;) et on appelle &lt;code&gt;showObj&lt;/code&gt; pour ce bon type.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph 435deca1-7452-4d94-a8a5-6ec3d0db9808--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-435deca1-7452-4d94-a8a5-6ec3d0db9808&quot;&gt;Utilisons tout cela désormais.&lt;/p&gt;
&lt;!-- end paragraph 435deca1-7452-4d94-a8a5-6ec3d0db9808--&gt;
&lt;!-- begin code 60f5463e-c03a-4b69-9cd8-2106034cab69--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-60f5463e-c03a-4b69-9cd8-2106034cab69&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val aelfraed: Kind = Elf(&amp;quot;Aelfraed&amp;quot;)
val maeva: Kind = Human(&amp;quot;Maeva&amp;quot;)
val kind :Kind = Hybrid(aelfraed, maeva)

given instance2: Show[Kind] = Show.derived

val showKind: Show[Kind] = summon[Show[Kind]]
println(s&amp;quot;1 - ${showKind.show(kind)}&amp;quot;)

given instance3: ShowObj[Kind] = Show.derivedObj

val showObjKind: ShowObj[Kind] = summon[ShowObj[Kind]]
println(s&amp;quot;2 - ${showObjKind.showObj(kind)}&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 60f5463e-c03a-4b69-9cd8-2106034cab69--&gt;
&lt;!-- begin paragraph f2a69ad7-5d88-4c94-be06-43965e3a1f5d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f2a69ad7-5d88-4c94-be06-43965e3a1f5d&quot;&gt;Ce qui donne.&lt;/p&gt;
&lt;!-- end paragraph f2a69ad7-5d88-4c94-be06-43965e3a1f5d--&gt;
&lt;!-- begin code e5e78443-2e8e-48f3-8490-f314ff0fc9dc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e5e78443-2e8e-48f3-8490-f314ff0fc9dc&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;1 - HYBRID(ELF(AELFRAED),HUMAN(MAEVA))
2 - kind part: [ elvishName: [Aelfraed] ] other part: [ name: [Maeva] ]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e5e78443-2e8e-48f3-8490-f314ff0fc9dc--&gt;
&lt;!-- begin paragraph fb2d8d78-0e07-4a45-9e1e-2f2fe9f8d456--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fb2d8d78-0e07-4a45-9e1e-2f2fe9f8d456&quot;&gt;Si on retourne sur la &lt;i&gt;Figure 3 &lt;/i&gt;on voit que l’on a bien obtenu :&lt;/p&gt;
&lt;!-- end paragraph fb2d8d78-0e07-4a45-9e1e-2f2fe9f8d456--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;Show[String]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[A]&lt;/code&gt; → obtenu par la méthode split&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[B]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ShowObj[C]&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 28f5cd83-689e-46f9-8bb1-051658732b21--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-28f5cd83-689e-46f9-8bb1-051658732b21&quot;&gt;Et que &lt;code&gt;Show[T]&lt;/code&gt; peut aussi dériver un &lt;code&gt;A&lt;/code&gt; (cf. &lt;code&gt;HYBRID(ELF(AELFRAED),HUMAN(MAEVA))&lt;/code&gt;) comme indiqué dans la &lt;i&gt;Figure 2.&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph 28f5cd83-689e-46f9-8bb1-051658732b21--&gt;
&lt;!-- begin paragraph 0cc78fd2-8eaf-4fd9-a14a-9280b4936f8c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0cc78fd2-8eaf-4fd9-a14a-9280b4936f8c&quot;&gt;Voilà le code exécutable dans une worksheet, &lt;i&gt;happy hacking!&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph 0cc78fd2-8eaf-4fd9-a14a-9280b4936f8c--&gt;
&lt;!-- begin code cc75dd0f-d29f-42a3-ac37-cb29e662d929--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-cc75dd0f-d29f-42a3-ac37-cb29e662d929&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import language.experimental.macros
import magnolia1.*

import scala.deriving.Mirror

trait Show[T] {
  def show(t: T): String
}
trait ShowObj[T] extends Show[T] {
  def showObj(t: T): String
}

object Show extends Derivation[Show]{
  given Show[String] with
    def show(t: String): String = s&amp;quot;$t&amp;quot;

  given Show[Int] with
    def show(t: Int): String = s&amp;quot;#${t.toString} -&amp;quot;

  override def join[T](ctx: CaseClass[Show, T]): ShowObj[T] = new ShowObj[T] {
    override def show(t: T): String = t.toString.toUpperCase()
    override def showObj(t: T): String =  ctx.params.map(param =&amp;gt; param.typeclass match {
      case showObj: ShowObj[param.PType] =&amp;gt; s&amp;quot;${param.label} part: [ ${showObj.showObj(param.deref(t))} ] &amp;quot;
      case show: Show[param.PType] =&amp;gt; s&amp;quot;${param.label}: [${show.show(param.deref(t))}]&amp;quot;
    }).mkString
  }

  override def split[T](ctx: SealedTrait[Show, T]): ShowObj[T] = new ShowObj[T] {
    override def show(t: T): String = ctx.choose(t) { sub =&amp;gt;
      sub.typeclass.show(sub.cast(t))
    }
    override def showObj(t: T): String = ctx.choose(t) { sub =&amp;gt;
      val tc = sub.typeclass.asInstanceOf[ShowObj[T]]
      tc.showObj(sub.cast(t))
    }
  }

  inline given autoDerived[A](using Mirror.Of[A]): Show[A] = derived.asInstanceOf[Show[A]]
  inline given derivedObj[A](using Mirror.Of[A]): ShowObj[A] = derived.asInstanceOf[ShowObj[A]]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code cc75dd0f-d29f-42a3-ac37-cb29e662d929--&gt;
&lt;!-- begin paragraph 40fb3de4-fc58-4897-8e0f-096e65e76191--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-40fb3de4-fc58-4897-8e0f-096e65e76191&quot;&gt;Et voici l’utilisation.&lt;/p&gt;
&lt;!-- end paragraph 40fb3de4-fc58-4897-8e0f-096e65e76191--&gt;
&lt;!-- begin code 02b585ff-333e-45f5-b41f-de50c7bc7ce3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-02b585ff-333e-45f5-b41f-de50c7bc7ce3&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import Show.given_Show_String

sealed trait Root
case object B extends Root
case class C(next:Root) extends Root

given instance: Show[Root] = Show.derived

val showA: Show[Root] = summon[Show[Root]]
val root: Root = C(C(B))
println(showA.show(root))

sealed trait Kind
case class Human(name: String) extends Kind
case class Elf(elvishName: String) extends Kind
case class Hybrid(kind: Kind, other: Kind) extends Kind

val aelfraed: Kind = Elf(&amp;quot;Aelfraed&amp;quot;)
val maeva: Kind = Human(&amp;quot;Maeva&amp;quot;)
val kind :Kind = Hybrid(aelfraed, maeva)

given instance2: Show[Kind] = Show.derived

val showKind: Show[Kind] = summon[Show[Kind]]
println(showKind.show(kind))

given instance3: ShowObj[Kind] = Show.derivedObj
val showObjKind: ShowObj[Kind] = summon[ShowObj[Kind]]
println(showObjKind.showObj(kind))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 02b585ff-333e-45f5-b41f-de50c7bc7ce3--&gt;
&lt;!-- begin heading_2 c7566951-bbe6-4d12-8c7b-6e2512a0362e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c7566951-bbe6-4d12-8c7b-6e2512a0362e&quot;&gt;Ressources&lt;/h3&gt;
&lt;!-- end heading_2 c7566951-bbe6-4d12-8c7b-6e2512a0362e--&gt;
&lt;!-- begin paragraph 1132008a-5663-4c1b-9bc7-16b2635208e5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1132008a-5663-4c1b-9bc7-16b2635208e5&quot;&gt;code de l’article : &lt;a href=&quot;https://github.com/univalence/recursive-derivation-magnolia/blob/main/src/main/scala/ShowDerivationMagnolia3.scala&quot;&gt;https://github.com/univalence/recursive-derivation-magnolia/blob/main/src/main/scala/ShowDerivationMagnolia3.scala&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 1132008a-5663-4c1b-9bc7-16b2635208e5--&gt;
&lt;!-- begin paragraph 0b9faa1d-f874-442d-97ba-fc53d80291dd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0b9faa1d-f874-442d-97ba-fc53d80291dd&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0b9faa1d-f874-442d-97ba-fc53d80291dd--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:accf38862d1448608549a4bd363a917c</id>
    <title>HR-OPS: Automatiser la création de documents administratifs avec zio-notion</title>
    <updated>2022-10-19T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/hr-ops-automatiser-la-creation-de-documents-administratifs-avec-zio-notion.html"/>
    <!--summary Dans cet article, nous regarderons comment faire un système qui génère des documents PDF à partir d’une base de données notion-->    <author>
      <name>Bastien Guihard</name>
      <uri>https://univalence.io/blog/auteurs/bastien-guihard.html</uri>
    </author>        <category term="Scala"></category>    <category term="ZIO"></category>    <category term="zio-notion"></category>    <category term="admin"></category>    <category term="library"></category>    <content type="html">
&lt;!-- begin heading_2 3618c46c-ccf4-4617-b22f-8e0cc30cad8e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3618c46c-ccf4-4617-b22f-8e0cc30cad8e&quot;&gt;Introduction &lt;/h3&gt;
&lt;!-- end heading_2 3618c46c-ccf4-4617-b22f-8e0cc30cad8e--&gt;
&lt;!-- begin paragraph 28119d78-6428-4ea2-82e3-1da73b2d4990--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-28119d78-6428-4ea2-82e3-1da73b2d4990&quot;&gt;Quand l’informatique est perçue comme une source de coûts, on peut être tenté d’utiliser des outils de no-code pour répondre à ses problèmes. &lt;/p&gt;
&lt;!-- end paragraph 28119d78-6428-4ea2-82e3-1da73b2d4990--&gt;
&lt;!-- begin paragraph e0a9a8a8-b9e0-4595-b509-a470a82e74cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e0a9a8a8-b9e0-4595-b509-a470a82e74cc&quot;&gt;Et le no-code, ça marche bien jusqu’à ce que ça marche plus. La question de savoir ce que l’on veut faire est progressivement remplacée par “qu’est-ce que l’on peut faire” ?&lt;/p&gt;
&lt;!-- end paragraph e0a9a8a8-b9e0-4595-b509-a470a82e74cc--&gt;
&lt;!-- begin paragraph 3dcfeb73-c322-46fd-b223-f6146e28a20a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3dcfeb73-c322-46fd-b223-f6146e28a20a&quot;&gt;Certaines tâches administratives et de gestion coûtent du temps, sont répétitives, sont difficilement automatisable par des solutions de no-code, en particulier quand il s’agit de faire des transformations avec un contexte entier qui ne se gère pas avec du 1 pour 1 (ex. N notes de frais, M personnes ⇒ M piéces pour la comptabilité, avec une synthèse et une concatenation des éléments des notes frais)&lt;/p&gt;
&lt;!-- end paragraph 3dcfeb73-c322-46fd-b223-f6146e28a20a--&gt;
&lt;!-- begin paragraph 3b47079a-6d25-4022-a107-779114001144--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b47079a-6d25-4022-a107-779114001144&quot;&gt;Dans cet article, nous allons voir comment faire un générateur de PDF qui source ces données depuis &lt;a href=&quot;http://notion.so/&quot;&gt;Notion&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 3b47079a-6d25-4022-a107-779114001144--&gt;
&lt;!-- begin heading_2 a7bde12e-5e02-4296-aed5-8fea047f9f9e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-a7bde12e-5e02-4296-aed5-8fea047f9f9e&quot;&gt;Étude de cas&lt;/h3&gt;
&lt;!-- end heading_2 a7bde12e-5e02-4296-aed5-8fea047f9f9e--&gt;
&lt;!-- begin paragraph 168b9465-3020-422d-87a5-028c49ef1f8c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-168b9465-3020-422d-87a5-028c49ef1f8c&quot;&gt;Imaginons plusieurs cas d’usage : &lt;/p&gt;
&lt;!-- end paragraph 168b9465-3020-422d-87a5-028c49ef1f8c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Notes de frais&lt;/b&gt; : Vous devez rassembler les notes de frais de vos équipiers dans un simple document, par équipier, pour l’envoyer à la comptabilité tous les mois.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Dossiers locatifs&lt;/b&gt; : si vous cherchez un endroit où habiter/travailler, il est possible que vous ayez à envoyer plusieurs dossiers. Certaines pièces changent régulièrement (3 derniers bulletins de salaire, justificatif de domicile récent, etc.) le reste de pièces est stable dans le temps.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Proposition commerciale&lt;/b&gt; pour enseigner dans une école : le programme change, le CV des formateurs évoluent moins vite.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph e5730013-e0f4-4243-bf41-162e3ac43d67--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5730013-e0f4-4243-bf41-162e3ac43d67&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e5730013-e0f4-4243-bf41-162e3ac43d67--&gt;
&lt;!-- begin paragraph 67c0e2bd-6377-4d0a-9742-75ad4f57de08--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67c0e2bd-6377-4d0a-9742-75ad4f57de08&quot;&gt;Toutes ces pièces jointes sont soigneusement rangées dans votre base documentaire &lt;/p&gt;
&lt;!-- end paragraph 67c0e2bd-6377-4d0a-9742-75ad4f57de08--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/accf3886-2d14-4860-8549-a4bd363a917c/capture_decran_2022-10-12_a_16.56.12.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Figure 1 - Base de données Notion regroupant des fichiers PDF ainsi que leurs informations contextuelles&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 530e0a7d-f223-4674-9c53-1d3000fd7dbb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-530e0a7d-f223-4674-9c53-1d3000fd7dbb&quot;&gt;Vous aimeriez pouvoir fusionner les PDF pour constituer un document à partir de votre page Notion, tout en gardant des informations sur le contenu de votre page, et en annotant les pièces avec des éléments supplémentaires (en tête, pagination, etc.).&lt;/p&gt;
&lt;!-- end paragraph 530e0a7d-f223-4674-9c53-1d3000fd7dbb--&gt;
&lt;!-- begin heading_2 44f54f20-a9aa-4ada-8d32-af9407ca72e9--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-44f54f20-a9aa-4ada-8d32-af9407ca72e9&quot;&gt;Prérequis&lt;/h3&gt;
&lt;!-- end heading_2 44f54f20-a9aa-4ada-8d32-af9407ca72e9--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Vous faites du Scala ;&lt;/li&gt;&lt;li&gt;Vous utilisez les effets &lt;code&gt;ZIO&lt;/code&gt; ;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 536777c7-3aec-49e8-974b-acf8c16bc6b8--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-536777c7-3aec-49e8-974b-acf8c16bc6b8&quot;&gt;Zio-notion&lt;/h3&gt;
&lt;!-- end heading_2 536777c7-3aec-49e8-974b-acf8c16bc6b8--&gt;
&lt;!-- begin paragraph bbfb8acf-dadf-42c8-aac2-cf5dac19eb69--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bbfb8acf-dadf-42c8-aac2-cf5dac19eb69&quot;&gt;&lt;code&gt;zio-notion&lt;/code&gt; est une bibliothèque logicielle qui permet d’interagir avec Notion.&lt;/p&gt;
&lt;!-- end paragraph bbfb8acf-dadf-42c8-aac2-cf5dac19eb69--&gt;
&lt;!-- begin paragraph 366bb05f-a4f2-4f40-92ad-786c6e2b47f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-366bb05f-a4f2-4f40-92ad-786c6e2b47f0&quot;&gt;zio-notion est livré avec en ensemble de features pratiques telles que : &lt;/p&gt;
&lt;!-- end paragraph 366bb05f-a4f2-4f40-92ad-786c6e2b47f0--&gt;
&lt;!-- begin paragraph 05880a68-d3cf-4346-b7c2-543c789d113e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-05880a68-d3cf-4346-b7c2-543c789d113e&quot;&gt;&lt;b&gt;Un DSL pour écrire des filtres ou manipuler des colonnes :&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 05880a68-d3cf-4346-b7c2-543c789d113e--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/accf3886-2d14-4860-8549-a4bd363a917c/capture_decran_2022-10-13_a_11.16.53.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Figure 2 - Exemple de filtre sur l’interface Notion&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 91b444d2-111d-47fa-949a-a286ac3fb4e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-91b444d2-111d-47fa-949a-a286ac3fb4e7&quot;&gt;Et son équivalent avec zio-notion : &lt;/p&gt;
&lt;!-- end paragraph 91b444d2-111d-47fa-949a-a286ac3fb4e7--&gt;
&lt;!-- begin code 5dd78ccb-e770-4f0d-b0eb-69ecde31b406--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5dd78ccb-e770-4f0d-b0eb-69ecde31b406&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val filters: Filter =
    $&amp;quot;Tag&amp;quot;.asSelect equals &amp;quot;filter-1&amp;quot; and
      $&amp;quot;Author&amp;quot;.asPeople.contains(&amp;quot;Bastien Guihard&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5dd78ccb-e770-4f0d-b0eb-69ecde31b406--&gt;
&lt;!-- begin paragraph 2e08bba8-3911-49cd-83ad-bbd77c75c522--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2e08bba8-3911-49cd-83ad-bbd77c75c522&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2e08bba8-3911-49cd-83ad-bbd77c75c522--&gt;
&lt;!-- begin paragraph 93917f6e-8f6e-43f2-a073-a15ddfdd86a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-93917f6e-8f6e-43f2-a073-a15ddfdd86a0&quot;&gt;&lt;b&gt;Un mécanisme de déstructuration :&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 93917f6e-8f6e-43f2-a073-a15ddfdd86a0--&gt;
&lt;!-- begin paragraph 2f273298-09c7-4b39-86a7-088977a6e240--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2f273298-09c7-4b39-86a7-088977a6e240&quot;&gt;Vous pouvez choisir de déstructurer une page et la représenter par une &lt;code&gt;case class&lt;/code&gt; directement.&lt;/p&gt;
&lt;!-- end paragraph 2f273298-09c7-4b39-86a7-088977a6e240--&gt;
&lt;!-- begin paragraph 13834dbc-7277-4f97-903e-3a69e04e39b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-13834dbc-7277-4f97-903e-3a69e04e39b7&quot;&gt;Soit une &lt;code&gt;case class&lt;/code&gt; &lt;code&gt;DatabaseEntry&lt;/code&gt; : &lt;/p&gt;
&lt;!-- end paragraph 13834dbc-7277-4f97-903e-3a69e04e39b7--&gt;
&lt;!-- begin code e2d1ef2e-7ab7-408f-940a-0e02e5b3759a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e2d1ef2e-7ab7-408f-940a-0e02e5b3759a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;final case class DatabaseEntry(
      @NotionColumn(&amp;quot;Name&amp;quot;)   name: String,
      @NotionColumn(&amp;quot;Author&amp;quot;) author: String
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e2d1ef2e-7ab7-408f-940a-0e02e5b3759a--&gt;
&lt;!-- begin paragraph 610c4cb1-2480-43db-b5a2-7a96ef48f8e9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-610c4cb1-2480-43db-b5a2-7a96ef48f8e9&quot;&gt;Déstructuration de la &lt;code&gt;Page&lt;/code&gt; en &lt;code&gt;DatabaseEntry&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 610c4cb1-2480-43db-b5a2-7a96ef48f8e9--&gt;
&lt;!-- begin code 7e33bd27-34d5-43d2-94b7-a978b414ee79--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7e33bd27-34d5-43d2-94b7-a978b414ee79&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;for {
      database   &amp;lt;- Notion.queryAllDatabase(&amp;quot;ID-DB-FIGURE-1&amp;quot;, filter)
      maybeEntry &amp;lt;- database.results.headOption.map(_.propertiesAs[DatabaseEntry].toZIO)
			_          &amp;lt;- maybeEntry match {
		     case Some(entry) =&amp;gt; Console.printLine(entry.name)
         case None.       =&amp;gt; Console.printLine(&amp;quot;There is no database entry.&amp;quot;) 
	    } 
} yield()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7e33bd27-34d5-43d2-94b7-a978b414ee79--&gt;
&lt;!-- begin paragraph 30dbfc18-fb26-44cc-9439-8ed08acb92ec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-30dbfc18-fb26-44cc-9439-8ed08acb92ec&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 30dbfc18-fb26-44cc-9439-8ed08acb92ec--&gt;
&lt;!-- begin paragraph 2c35276a-01a7-4561-b0d0-db388d997123--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c35276a-01a7-4561-b0d0-db388d997123&quot;&gt;Vous pouvez retrouver la documentation ici : &lt;a href=&quot;https://univalence.github.io/zio-notion/&quot;&gt;https://univalence.github.io/zio-notion/&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 2c35276a-01a7-4561-b0d0-db388d997123--&gt;
&lt;!-- begin heading_2 52377f3e-0283-4288-8a08-42ac8cab9656--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-52377f3e-0283-4288-8a08-42ac8cab9656&quot;&gt;Fusionneur de PDF “maison”&lt;/h3&gt;
&lt;!-- end heading_2 52377f3e-0283-4288-8a08-42ac8cab9656--&gt;
&lt;!-- begin paragraph 20d52841-7438-4060-a1a8-76f7090c0571--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-20d52841-7438-4060-a1a8-76f7090c0571&quot;&gt;Revenons à notre projet initial &lt;/p&gt;
&lt;!-- end paragraph 20d52841-7438-4060-a1a8-76f7090c0571--&gt;
&lt;!-- begin paragraph 89728a22-2c19-4ae5-8a5a-20ad9900ca43--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89728a22-2c19-4ae5-8a5a-20ad9900ca43&quot;&gt;Notre projet doit être capable &lt;/p&gt;
&lt;!-- end paragraph 89728a22-2c19-4ae5-8a5a-20ad9900ca43--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;D’interagir avec Notion ;&lt;/li&gt;&lt;li&gt;De télécharger les PDF à fusionner ;&lt;/li&gt;&lt;li&gt;De créer un PDF contenant une page récapitulative et les PDF fusionnés ;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/accf3886-2d14-4860-8549-a4bd363a917c/hrops(1).png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Figure 3 - Architecture de notre fusionneur de PDF fait maison&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 79f1cb69-b8f3-42dd-8b72-e55c598739a3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79f1cb69-b8f3-42dd-8b72-e55c598739a3&quot;&gt;Le projet de cet article est disponible ici : &lt;a href=&quot;https://github.com/univalence/hr-ops&quot;&gt;https://github.com/univalence/hr-ops&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 79f1cb69-b8f3-42dd-8b72-e55c598739a3--&gt;
&lt;!-- begin paragraph 1b2e9656-ac3b-4304-8cea-66b2f77c4192--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1b2e9656-ac3b-4304-8cea-66b2f77c4192&quot;&gt;On va se pencher sur la logique de l’application :&lt;/p&gt;
&lt;!-- end paragraph 1b2e9656-ac3b-4304-8cea-66b2f77c4192--&gt;
&lt;!-- begin code b3aa9a0b-e7c8-49dd-bdfb-078156cec12f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b3aa9a0b-e7c8-49dd-bdfb-078156cec12f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;
/**
 * DocumentContext représente un document PDF une fois telechargé augmenté de 
 * son nom, son nombre de pages et les informations de la base de donnée
 */
final case class DocumentContext(name: String, bytes: Array[Byte], numberOfPage: Int, databaseRow: DatabaseEntry)

...

/**
 * Tout d&amp;#39;abord on souhaite fusionner les PDF dont la valeur de Tag
 * est égale à &amp;quot;filter-1&amp;quot;
 */
val filter: Filter = $&amp;quot;Tag&amp;quot;.asSelect.equals(&amp;quot;filter-1&amp;quot;)

...

def program: ZIO[Downloader with Notion, Throwable, Unit] =
    for {
			/**
			 * `queryAllDatabase` nous permet de passer en revue l&amp;#39;intégralité de la base de données
			 * 
			 * En effet, zio-notion est livré avec une gestion de la pagination des bases de données, 
			 * vous pouvez aisément parcourir votre base de donnée de manière séquentielle
			 * https://univalence.github.io/zio-notion/pagination
			 */
      database &amp;lt;- Notion.queryAllDatabase(Constants.dbId, filter)
			// on destructure toutes les entrées qui nous interesse
      destructureEffects = database.results.map(_.propertiesAs[DatabaseEntry].toZIO)
      entries &amp;lt;- ZIO.collectAllPar(destructureEffects)
      // On construit les effets qui représentent le téléchargement des documents
			contextualizeEffects = entries.map(_.contextualize)
      documentContexts &amp;lt;- ZIO.collectAllPar(contextualizeEffects)
      flattenDocumentContexts = documentContexts.flatten
			// La création d&amp;#39;un encodeur déclaratif rend la construction d&amp;#39;un PDF évidente 
      documentDescription =
        CreateNewPDF &amp;gt;&amp;gt;
          AddTable(flattenDocumentContexts) &amp;gt;&amp;gt; AddPDFs(flattenDocumentContexts) &amp;gt;&amp;gt; Paginate &amp;gt;&amp;gt; SavePDF(&amp;quot;concat&amp;quot;)
			/**
			 * On interprete cette description, en l&amp;#39;occurence via PDF BOX
			 * 
			 * Pour changer de technologie il nous suffit de changer d&amp;#39;interpreteur, 
			 * la description reste la même
			 */
      _ &amp;lt;- documentDescription.interpret.orDie
    } yield ()
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b3aa9a0b-e7c8-49dd-bdfb-078156cec12f--&gt;
&lt;!-- begin heading_2 e841323a-0474-4de8-9dc7-b670252c89d4--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e841323a-0474-4de8-9dc7-b670252c89d4&quot;&gt;Conclusion&lt;/h3&gt;
&lt;!-- end heading_2 e841323a-0474-4de8-9dc7-b670252c89d4--&gt;
&lt;!-- begin paragraph aca7310a-ba33-4228-8197-baff83a58d1d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aca7310a-ba33-4228-8197-baff83a58d1d&quot;&gt;En deux coups de cuillère à pot, vous pouvez vous affranchir des limites du no code.&lt;/p&gt;
&lt;!-- end paragraph aca7310a-ba33-4228-8197-baff83a58d1d--&gt;
&lt;!-- begin paragraph a3a4ae23-2f7a-49d6-9b98-310f4d02e94f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a3a4ae23-2f7a-49d6-9b98-310f4d02e94f&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a3a4ae23-2f7a-49d6-9b98-310f4d02e94f--&gt;
&lt;!-- begin paragraph 3de0d85e-f0df-4ddf-95c1-61bcc3c36b8e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3de0d85e-f0df-4ddf-95c1-61bcc3c36b8e&quot;&gt;La stack de ce projet (Scala + Notion + zio-notion) nous donne un ensemble de garanties : &lt;/p&gt;
&lt;!-- end paragraph 3de0d85e-f0df-4ddf-95c1-61bcc3c36b8e--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Vous conservez la modularité de Notion pour organiser votre contenu ;&lt;/li&gt;&lt;li&gt;Zio-notion vous assure d’interagir avec notion d’une manière pratique, idiomatique de Scala ;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph ebe87d98-1417-4a8f-9451-05b504ac95a3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ebe87d98-1417-4a8f-9451-05b504ac95a3&quot;&gt;Les solutions no-code restent des solutions viables dans pas mal de cas, mais il est intéressant de prendre en compte dans votre choix d’outil que vous ne serez pas coupé dans votre élan. &lt;/p&gt;
&lt;!-- end paragraph ebe87d98-1417-4a8f-9451-05b504ac95a3--&gt;
&lt;!-- begin paragraph 6d8eec1f-37b3-428a-b9ac-f9ad6038d72a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d8eec1f-37b3-428a-b9ac-f9ad6038d72a&quot;&gt;La garantie de pouvoir interagir avec un outil no-code/low-code par le biais d’un SDK ou d’une API publique &lt;b&gt;de qualité&lt;/b&gt; est un élément essentiel à prendre en compte dans son choix d’outillage.&lt;/p&gt;
&lt;!-- end paragraph 6d8eec1f-37b3-428a-b9ac-f9ad6038d72a--&gt;
&lt;!-- begin paragraph 05a0b39b-abdb-409d-a6f0-e72ba6027aa7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-05a0b39b-abdb-409d-a6f0-e72ba6027aa7&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 05a0b39b-abdb-409d-a6f0-e72ba6027aa7--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:e9c7aa32ab1046e6ae55d94223fc2008</id>
    <title>Scala : Sandwich de versions et rétrocompatibilité</title>
    <updated>2022-10-17T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/scala-sandwich-de-versions-et-retrocompatibilite.html"/>
    <!--summary Un article sur les différents mix entre Scala 2 et 3-->    <author>
      <name>Bastien Guihard</name>
      <uri>https://univalence.io/blog/auteurs/bastien-guihard.html</uri>
    </author>        <category term="Scala"></category>    <category term="Scala 3"></category>    <category term="Magnolia"></category>    <category term="SBT"></category>    <content type="html">
&lt;!-- begin heading_2 c5978696-88db-4b44-a931-281ebd3b109b--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c5978696-88db-4b44-a931-281ebd3b109b&quot;&gt;Introduction&lt;/h3&gt;
&lt;!-- end heading_2 c5978696-88db-4b44-a931-281ebd3b109b--&gt;
&lt;!-- begin paragraph 026cb20a-14ac-463c-87cc-8b64f155efbc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-026cb20a-14ac-463c-87cc-8b64f155efbc&quot;&gt;Dans son article &lt;a href=&quot;https://univalence.io/blog/articles/scala-3-un-nouveau-langage/&quot;&gt;https://univalence.io/blog/articles/scala-3-un-nouveau-langage/&lt;/a&gt; François nous présentait les nouveautés de Scala 3 et nous donnait des conseils vis-à-vis de la migration de projets en Scala 2 vers Scala 3.&lt;/p&gt;
&lt;!-- end paragraph 026cb20a-14ac-463c-87cc-8b64f155efbc--&gt;
&lt;!-- begin paragraph 606e2ca2-3f26-4d98-a9a0-56327b219221--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-606e2ca2-3f26-4d98-a9a0-56327b219221&quot;&gt;Dans cet article, nous parlerons un peu plus en détail des problèmes liés aux migrations et cohabitations entre Scala 2 et Scala 3.&lt;/p&gt;
&lt;!-- end paragraph 606e2ca2-3f26-4d98-a9a0-56327b219221--&gt;
&lt;!-- begin heading_2 4c83a67c-3de1-4404-be4b-b8a714e4d257--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4c83a67c-3de1-4404-be4b-b8a714e4d257&quot;&gt;Prérequis &lt;/h3&gt;
&lt;!-- end heading_2 4c83a67c-3de1-4404-be4b-b8a714e4d257--&gt;
&lt;!-- begin paragraph 42f35a88-b6bd-4922-ae73-de61ca85f7ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42f35a88-b6bd-4922-ae73-de61ca85f7ac&quot;&gt;Avant de commencer, débutons par les mauvaises nouvelles.&lt;/p&gt;
&lt;!-- end paragraph 42f35a88-b6bd-4922-ae73-de61ca85f7ac--&gt;
&lt;!-- begin paragraph 8e18f6c1-0732-450f-8201-f914605d4b67--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e18f6c1-0732-450f-8201-f914605d4b67&quot;&gt;Vous allez avoir des problèmes pour migrer votre code base si : &lt;/p&gt;
&lt;!-- end paragraph 8e18f6c1-0732-450f-8201-f914605d4b67--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Vous dépendez d’une bibliothèque logicielle qui fait usage de macro Scala 2&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Si c’est le cas, vous pouvez aller voir &lt;a href=&quot;https://scalacenter.github.io/scala-3-migration-guide/docs/macros/macro-libraries.html&quot;&gt;ici&lt;/a&gt; l’état de la migration des dépendances populaires. &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Votre projet dépend d’un plugin &lt;code&gt;sbt&lt;/code&gt; qui n’a pas d’équivalent pour Scala 3&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Si c’est le cas, allez jeter un œil aux &lt;a href=&quot;https://docs.scala-lang.org/scala3/guides/migration/tutorial-prerequisites.html&quot;&gt;alternatives&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Votre projet dépend de &lt;code&gt;scala-reflect&lt;/code&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Si c’est le cas… Je n’ai pas encore de solution pour vous.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 db9ee68a-4970-4b18-957b-b5618ee999a7--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-db9ee68a-4970-4b18-957b-b5618ee999a7&quot;&gt;Jar Hell&lt;/h3&gt;
&lt;!-- end heading_2 db9ee68a-4970-4b18-957b-b5618ee999a7--&gt;
&lt;!-- begin paragraph 168ccf47-44c4-45dc-a3b3-027c842efdf6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-168ccf47-44c4-45dc-a3b3-027c842efdf6&quot;&gt;Toujours avant de commencer, il est important d’avoir une vue rapide sur ce que l&amp;#39;on appelle le Jar Hell. Le &lt;b&gt;Jar Hell&lt;/b&gt; c’est ce qui qualifie les problèmes que l&amp;#39;on peut rencontrer avec les mécanismes de classloading de la JVM.&lt;/p&gt;
&lt;!-- end paragraph 168ccf47-44c4-45dc-a3b3-027c842efdf6--&gt;
&lt;!-- begin paragraph 174b8883-8326-4b41-bb83-06b20242afee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-174b8883-8326-4b41-bb83-06b20242afee&quot;&gt;Voilà une liste non exhaustive de ces problèmes :&lt;/p&gt;
&lt;!-- end paragraph 174b8883-8326-4b41-bb83-06b20242afee--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Unexpressed deps&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Un jar n&amp;#39;indique pas de quels autres jars, il dépend. Dès lors, c&amp;#39;est au développeur de résoudre lui-même certaines dépendances.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Dépendances transitives&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Certaines bibliothèques logicielles dépendent de certaines autres bibliothèques (qui dépendent elles-mêmes d’autres bibliothèques, etc.). C’est, là encore, au développeur de récupérer les sous-dépendances.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Shadowing&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Un projet peut dépendre de deux versions différentes d’une même bibliothèque. Cela arrive, par exemple, lorsque deux des dépendances d’un projet dépendent d’une même bibliothèque, mais pour des versions différentes. Dans le meilleur des cas, il est possible de ne conserver que la version la plus récente. Mais il peut arriver que les deux versions soient incompatibles (par exemple, une fonction est disponible dans les deux versions, mais la signature a changé avec des paramètres différents et/ou un type de retour différent).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Conflit de versions&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Deux bibliothèques dépendent de deux bibliothèques incompatibles.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 6cbaed4a-78d9-4eb7-8a6d-c520490cedff--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6cbaed4a-78d9-4eb7-8a6d-c520490cedff&quot;&gt;TASTy ?&lt;/h3&gt;
&lt;!-- end heading_2 6cbaed4a-78d9-4eb7-8a6d-c520490cedff--&gt;
&lt;!-- begin paragraph adde55db-be80-4673-bbf9-d79f2c465e56--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-adde55db-be80-4673-bbf9-d79f2c465e56&quot;&gt;Si vous êtes intéressé par Scala 3, vous avez surement déjà dû entendre parler de TASTy.&lt;/p&gt;
&lt;!-- end paragraph adde55db-be80-4673-bbf9-d79f2c465e56--&gt;
&lt;!-- begin paragraph 96327326-1df7-4790-8d8d-8a165d2edfbc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-96327326-1df7-4790-8d8d-8a165d2edfbc&quot;&gt;TASTy (Typed Abstract Syntax Trees) est un format d’échange entre votre code et votre code compilé (&lt;i&gt;.class file&lt;/i&gt;).&lt;/p&gt;
&lt;!-- end paragraph 96327326-1df7-4790-8d8d-8a165d2edfbc--&gt;
&lt;!-- begin paragraph 75032dc3-a5f7-4de6-b10d-0bc95fe79ff4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75032dc3-a5f7-4de6-b10d-0bc95fe79ff4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 75032dc3-a5f7-4de6-b10d-0bc95fe79ff4--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/e9c7aa32-ab10-46e6-ae55-d94223fc2008/rsz12i8m40ngvkxnbn0f82b5xozwlf2ihkmdjz3jg39tzfqsmoakrevtft-gh3xfsbcnzosovp23p-zwk_tfrpxnewlfehal1vovevmikrixcmog9rn5lnf8n5ylzdz9c12aenrmecpsxpwg9q7amzukcspycpimoxbgbd1_fan5gxcyzwk0.svg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 98fa520a-2bae-4955-9082-8035ccf6cac0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-98fa520a-2bae-4955-9082-8035ccf6cac0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 98fa520a-2bae-4955-9082-8035ccf6cac0--&gt;
&lt;!-- begin paragraph 228232b2-7c0a-444d-99d3-d48fdf1429dd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-228232b2-7c0a-444d-99d3-d48fdf1429dd&quot;&gt;Concrètement, ce sont des fichiers qui contiennent toute l’information utile sur le code. &lt;/p&gt;
&lt;!-- end paragraph 228232b2-7c0a-444d-99d3-d48fdf1429dd--&gt;
&lt;!-- begin paragraph 1f6a44ea-6eec-4e1a-ab52-49e69f97c8a4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f6a44ea-6eec-4e1a-ab52-49e69f97c8a4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1f6a44ea-6eec-4e1a-ab52-49e69f97c8a4--&gt;
&lt;!-- begin paragraph fd327d99-497a-4341-8815-b6fc3f95b84d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fd327d99-497a-4341-8815-b6fc3f95b84d&quot;&gt;Le format TASTy permet de palier à certains désavantages de la compilation en bytecode, les fameux &lt;code&gt;.class&lt;/code&gt;, dans la mesure où ces derniers contiennent une représentation incomplète du code.&lt;/p&gt;
&lt;!-- end paragraph fd327d99-497a-4341-8815-b6fc3f95b84d--&gt;
&lt;!-- begin paragraph efc5ca5d-abee-42f0-a57c-351d040b3256--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-efc5ca5d-abee-42f0-a57c-351d040b3256&quot;&gt;Par exemple, une fois compilé en bytecode (&lt;code&gt;.class&lt;/code&gt;), les types génériques subissent un effacement de type (&lt;i&gt;type erasure&lt;/i&gt;). &lt;br /&gt;
Le code Scala suivant :&lt;/p&gt;
&lt;!-- end paragraph efc5ca5d-abee-42f0-a57c-351d040b3256--&gt;
&lt;!-- begin paragraph f427813a-cb00-4d90-a0ff-2eb4e0ce7655--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f427813a-cb00-4d90-a0ff-2eb4e0ce7655&quot;&gt;&lt;code&gt;val xs: List[Int] = List(1, 2, 3)&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph f427813a-cb00-4d90-a0ff-2eb4e0ce7655--&gt;
&lt;!-- begin paragraph c2757d6b-9ac4-4f5f-be7d-adc21b85c8a8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c2757d6b-9ac4-4f5f-be7d-adc21b85c8a8&quot;&gt;sera converti en&lt;/p&gt;
&lt;!-- end paragraph c2757d6b-9ac4-4f5f-be7d-adc21b85c8a8--&gt;
&lt;!-- begin paragraph e27664f5-9e29-45f6-b0a0-ea12725e1043--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e27664f5-9e29-45f6-b0a0-ea12725e1043&quot;&gt;&lt;code&gt;public scala.collection.immutable.List&amp;lt;java.lang.Object&amp;gt; xs();&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph e27664f5-9e29-45f6-b0a0-ea12725e1043--&gt;
&lt;!-- begin paragraph 56a59693-e2fb-41a4-8af8-31f5ccd76c6d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-56a59693-e2fb-41a4-8af8-31f5ccd76c6d&quot;&gt;Cela assure une compatibilité du code source et du code binaire entre les versions de la JVM, pour des raisons historiques, puisque les premières versions de Java ne disposaient pas de types génériques (implémentés dans Java 1.5 par Martin Ordersky). Cela vous évite entre autres de recompiler vos applications lorsque vous migrez vers des versions plus récentes de la JVM.&lt;/p&gt;
&lt;!-- end paragraph 56a59693-e2fb-41a4-8af8-31f5ccd76c6d--&gt;
&lt;!-- begin paragraph d3ee93cc-16f1-4fd8-a4a1-0e484aba05e5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3ee93cc-16f1-4fd8-a4a1-0e484aba05e5&quot;&gt;Les avantages d’avoir un format tel que TASTy sont multiples : &lt;/p&gt;
&lt;!-- end paragraph d3ee93cc-16f1-4fd8-a4a1-0e484aba05e5--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Un outil de build peut s’en servir pour construire le projet pour plusieurs versions de la JVM.&lt;/li&gt;&lt;li&gt;Permet une inspection du code profonde par des outils de métaprogrammation et permet de générer du code dans des cas avancés.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph ba319fbf-ac07-4b26-bf45-8558d35e8996--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba319fbf-ac07-4b26-bf45-8558d35e8996&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ba319fbf-ac07-4b26-bf45-8558d35e8996--&gt;
&lt;!-- begin heading_2 6a986652-38a0-4d32-a25f-32d34286b9b3--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6a986652-38a0-4d32-a25f-32d34286b9b3&quot;&gt;Qu’est-ce que cela autorise à faire de base ? &lt;/h3&gt;
&lt;!-- end heading_2 6a986652-38a0-4d32-a25f-32d34286b9b3--&gt;
&lt;!-- begin paragraph 4c6a8a47-c46c-4143-bf9e-e86fd807cd28--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4c6a8a47-c46c-4143-bf9e-e86fd807cd28&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4c6a8a47-c46c-4143-bf9e-e86fd807cd28--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/e9c7aa32-ab10-46e6-ae55-d94223fc2008/plantuml.svg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;La lib ne doit pas dépendre de &lt;code&gt;scala.reflect&lt;/code&gt; (donc pas de &lt;code&gt;macro&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/e9c7aa32-ab10-46e6-ae55-d94223fc2008/plantuml(1).svg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;La lib ne doit pas dépendre du nouveau type de métaprogrammation (&lt;code&gt;inline&lt;/code&gt; statements…).&lt;/li&gt;&lt;li&gt;Inclure l’option de compilateur &lt;code&gt;-Ytasty-reader&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph cd17fb28-327b-4df3-a171-e32eb692c1f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd17fb28-327b-4df3-a171-e32eb692c1f0&quot;&gt;Notez que si vous souhaitez utiliser une bibliothèque Scala 3 dans votre projet Scala 2, vous allez soit : &lt;/p&gt;
&lt;!-- end paragraph cd17fb28-327b-4df3-a171-e32eb692c1f0--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Devoir écrire manuellement que votre lib est en Scala 3.&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;code&gt;&amp;quot;org.legogroup&amp;quot; %% &amp;quot;woof-core&amp;quot;  % &amp;quot;0.2.0&amp;quot;&lt;br /&gt;
&lt;/code&gt;Deviendra dans un build en Scala 2&lt;br /&gt;
&lt;code&gt;&amp;quot;org.legogroup&amp;quot; % &amp;quot;woof-core_3&amp;quot; % &amp;quot;0.2.0&amp;quot;&lt;/code&gt;
&lt;!-- begin code 04f1ef46-3cf7-4458-93d4-1c6bcf7ef426--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-04f1ef46-3cf7-4458-93d4-1c6bcf7ef426&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;lazy val main = project.in(file(&amp;quot;main&amp;quot;))
  .settings(
		scalaVersion:= scala2,
		scalacOptions+= &amp;quot;-Ytasty-reader&amp;quot;,
		libraryDependencies++=Seq(
      &amp;quot;org.legogroup&amp;quot; % &amp;quot;woof-core_3&amp;quot;   % &amp;quot;0.2.0&amp;quot;
     )
  )
  .dependsOn(middle)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 04f1ef46-3cf7-4458-93d4-1c6bcf7ef426--&gt;&lt;/li&gt;&lt;li&gt;Soit utiliser l’opérateur &lt;code&gt;cross&lt;/code&gt;
&lt;!-- begin code 2c6c2aff-1740-4cdd-93b7-223973b21cf8--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-2c6c2aff-1740-4cdd-93b7-223973b21cf8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;lazy val main = project.in(file(&amp;quot;main&amp;quot;))
  .settings(
		scalaVersion:= scala2,
		scalacOptions+= &amp;quot;-Ytasty-reader&amp;quot;,
		libraryDependencies++=Seq(
      &amp;quot;org.legogroup&amp;quot; %% &amp;quot;woof-core&amp;quot; % &amp;quot;0.2.0&amp;quot; cross CrossVersion.for2_13Use3
     )
  )
  .dependsOn(middle)
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2c6c2aff-1740-4cdd-93b7-223973b21cf8--&gt;
&lt;!-- begin bookmark 5fac6314-8f35-44f7-a56e-5a2e16af826a--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-5fac6314-8f35-44f7-a56e-5a2e16af826a&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://www.scala-sbt.org/1.x/docs/Cross-Build.html#Scala+3+specific+cross-versions&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;sbt Reference Manual &amp;#x2014; Cross-building&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;&quot;&gt; https://www.scala-sbt.org/1.x/docs/Cross-Build.html#Scala+3+specific+cross-versions&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 5fac6314-8f35-44f7-a56e-5a2e16af826a--&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 0f91735b-c259-4f58-a355-73bb3e9a14da--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-0f91735b-c259-4f58-a355-73bb3e9a14da&quot;&gt;Étude de cas&lt;/h3&gt;
&lt;!-- end heading_2 0f91735b-c259-4f58-a355-73bb3e9a14da--&gt;
&lt;!-- begin paragraph 3b83c32b-4cd7-4d5a-a3b3-ea31bc083b43--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b83c32b-4cd7-4d5a-a3b3-ea31bc083b43&quot;&gt;Nous allons étudier la compatibilité de Magnolia au sein de projets composants Scala 2 et Scala 3.&lt;/p&gt;
&lt;!-- end paragraph 3b83c32b-4cd7-4d5a-a3b3-ea31bc083b43--&gt;
&lt;!-- begin paragraph 2f8978a2-f921-4dd8-9ea8-d429414133bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2f8978a2-f921-4dd8-9ea8-d429414133bc&quot;&gt;Rappelons que les versions Scala 2 et 3 de Magnolia tirent parti respectivement des macros et des nouvelles fonctionnalités de métaprogrammation.&lt;/p&gt;
&lt;!-- end paragraph 2f8978a2-f921-4dd8-9ea8-d429414133bc--&gt;
&lt;!-- begin paragraph 6e339b62-acc4-4999-a377-9fb7ac2095e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e339b62-acc4-4999-a377-9fb7ac2095e4&quot;&gt;Nous allons tenter de répondre aux questions suivantes : &lt;/p&gt;
&lt;!-- end paragraph 6e339b62-acc4-4999-a377-9fb7ac2095e4--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Peut-on utiliser Magnolia pour Scala 2 au sein d’un projet Scala 3 ?&lt;/li&gt;&lt;li&gt;Peut-on utiliser Magnolia 3 au sein d’un projet Scala 2 ?&lt;/li&gt;&lt;li&gt;Est-ce qu’un projet Scala 2 peut dépendre d’un projet Scala 3 qui utilise Magnolia 3 ?&lt;/li&gt;&lt;li&gt;Est-ce qu’un projet Scala 3 peut dépendre d’un projet Scala 2 qui utilise Magnolia 2 ?&lt;/li&gt;&lt;li&gt;Est-ce que les ADT et Typeclass de Scala 3 peuvent être dérivés par Magnolia 2 ?&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_3 ef906958-c9cd-4ea5-8129-0a1dcd1a6486--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-ef906958-c9cd-4ea5-8129-0a1dcd1a6486&quot;&gt;Note sur IntelliJ&lt;/h4&gt;
&lt;!-- end heading_3 ef906958-c9cd-4ea5-8129-0a1dcd1a6486--&gt;
&lt;!-- begin paragraph 6b7b8283-0a10-48dd-9bbe-68b10f3b39fd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6b7b8283-0a10-48dd-9bbe-68b10f3b39fd&quot;&gt;L’outillage de IntelliJ vis-à-vis de Scala 3 étant encore en développement, nous vous conseillons d’utiliser les commandes &lt;code&gt;sbt build&lt;/code&gt;, &lt;code&gt;sbt run&lt;/code&gt; depuis un terminal directement.&lt;/p&gt;
&lt;!-- end paragraph 6b7b8283-0a10-48dd-9bbe-68b10f3b39fd--&gt;
&lt;!-- begin paragraph 583f9e9a-0332-4702-9cfb-f93aa8625d86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-583f9e9a-0332-4702-9cfb-f93aa8625d86&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 583f9e9a-0332-4702-9cfb-f93aa8625d86--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/e9c7aa32-ab10-46e6-ae55-d94223fc2008/copy_of_blank_diagram(1).png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Schéma d’une application mélangeant Scala 2, Scala 3 et Magnolia pour Scala 2 et 3.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph fdd47092-3aaf-4d6b-b24f-6dababdd410c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fdd47092-3aaf-4d6b-b24f-6dababdd410c&quot;&gt;Une précision sur le schéma :&lt;/p&gt;
&lt;!-- end paragraph fdd47092-3aaf-4d6b-b24f-6dababdd410c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;genX[TcY[ADTZ]]&lt;/code&gt; signifie que la classe expose une dérivation via magnolia pour Scala &lt;b&gt;X&lt;/b&gt;, sur des typeclass écrites en Scala &lt;b&gt;Y&lt;/b&gt; paramétrés par des ADT Scala &lt;b&gt;Z &lt;/b&gt;avec X, Y et Z pouvant être égal à 2 ou 3.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph d5ce2c14-e8de-4d5c-8814-28b0ed9efcde--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d5ce2c14-e8de-4d5c-8814-28b0ed9efcde&quot;&gt;Notez que la topologie suivante ne marche pas :&lt;/p&gt;
&lt;!-- end paragraph d5ce2c14-e8de-4d5c-8814-28b0ed9efcde--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/e9c7aa32-ab10-46e6-ae55-d94223fc2008/sowkiimgastduuaa3avep4bkc58eachfiaqkylngqxdjyfpdjc_bpybcl4zbbr8exyy9lr1ia2reborfclr8pkyfpkd5hskohwmnfuwcmpgxfeqb0dq70000.svg&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;💩 cette topologie ne marche pas&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 180027d2-7a53-4d18-a526-ddc60d545571--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-180027d2-7a53-4d18-a526-ddc60d545571&quot;&gt;✅ Cette topologie fonctionne si : &lt;/p&gt;
&lt;!-- end paragraph 180027d2-7a53-4d18-a526-ddc60d545571--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;On garde qu’une seule version de Magnolia&lt;/li&gt;&lt;li&gt;Et l&amp;#39;on dérive les typeclasses avec le bon compilateur :&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Dans le &lt;code&gt;Scala 3 module&lt;/code&gt; si on utilise &lt;code&gt;magniola pour Scala 3&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Dans le &lt;code&gt;Scala 2 project&lt;/code&gt; si on utilise &lt;code&gt;magniola pour Scala 2&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 77ef4fd9-2cd3-4392-8e9b-3ee18a5d88d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77ef4fd9-2cd3-4392-8e9b-3ee18a5d88d7&quot;&gt;Répondons aux questions précédemment posées :&lt;/p&gt;
&lt;!-- end paragraph 77ef4fd9-2cd3-4392-8e9b-3ee18a5d88d7--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Peut-on utiliser Magnolia 2 au sein d’un projet Scala 3 ?&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Non. En effet, générer une typeclass via Magnolia 2 fait usage de &lt;code&gt;scala.reflect.macro&lt;/code&gt;, ce qui est impossible à faire avec le compilateur de Scala 3 (ces fonctionnalités ont été abandonnées).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Peut-on utiliser Magnolia 3 au sein d’un projet Scala 2 ?&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Non plus, ici ce sont les nouvelles fonctionnalités de métaprogrammation qui ne sont pas supportées par le compilateur de Scala 2.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Est-ce qu’un projet Scala 2 peut dépendre d’un projet Scala 3 qui utilise Magnolia 3 ?&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Oui, car on dépend du bon compilateur.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Est-ce qu’un projet Scala 3 peut dépendre d’un projet Scala 2 qui utilise Magnolia 2 ?&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Oui aussi, pour les mêmes raisons que précédemment.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Est-ce que les ADT et Typeclass de Scala 3 peuvent être dérivés par Magnolia 2&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Les enums de Scala 3 semblent poser un problème lorsqu’on tente de les dériver en Scala 2. En revanche, les ADT et typeclass de Scala 3, même avec leurs nouvelles features, paraissent ne pas poser problème, ni à l’utilisation, ni à la dérivation.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph bd0cf1d2-ee9a-48ed-aa41-b29610ca6e58--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd0cf1d2-ee9a-48ed-aa41-b29610ca6e58&quot;&gt;Si vous souhaitez expérimenter un peu avec les sandwiches, vous pouvez démarrer avec le code de cet article : &lt;a href=&quot;https://github.com/univalence/scala-sandwich&quot;&gt;https://github.com/univalence/scala-sandwich&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph bd0cf1d2-ee9a-48ed-aa41-b29610ca6e58--&gt;
&lt;!-- begin heading_2 f3a942d0-dd25-4c02-bd8b-b013e30b338b--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f3a942d0-dd25-4c02-bd8b-b013e30b338b&quot;&gt;Les outils pour aider à la migration&lt;/h3&gt;
&lt;!-- end heading_2 f3a942d0-dd25-4c02-bd8b-b013e30b338b--&gt;
&lt;!-- begin paragraph 116c214d-23e7-46c7-9416-782551c2f0ab--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-116c214d-23e7-46c7-9416-782551c2f0ab&quot;&gt;sbt est livré avec un outil de migration &lt;code&gt;sbt migration plugin&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 116c214d-23e7-46c7-9416-782551c2f0ab--&gt;
&lt;!-- begin paragraph bb2c3fbd-e30d-486d-aaf4-2f62e8e4481e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bb2c3fbd-e30d-486d-aaf4-2f62e8e4481e&quot;&gt;Il permet de :&lt;/p&gt;
&lt;!-- end paragraph bb2c3fbd-e30d-486d-aaf4-2f62e8e4481e--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Migrer les dépendances disponibles en Scala 3.&lt;/li&gt;&lt;li&gt;Migrer les options de compilation vers leur équivalent en Scala 3.&lt;/li&gt;&lt;li&gt;Aider à migrer la syntaxe.&lt;/li&gt;&lt;li&gt;Repérer les types qu’il sera impératif d’annoter manuellement du fait que le mécanisme d’inférence de type change en Scala 3.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 89b7ca0d-5407-4e8d-8d78-d85e345491d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89b7ca0d-5407-4e8d-8d78-d85e345491d1&quot;&gt;Si vous avez des dépendances de versions de Scala différentes, vous pouvez envisager d’utiliser &lt;a href=&quot;https://github.com/lightbend/mima&quot;&gt;Mima&lt;/a&gt; pour prévenir les &lt;a href=&quot;https://docs.scala-lang.org/overviews/core/binary-compatibility-for-library-authors.html&quot;&gt;incompatibilités binaires&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 89b7ca0d-5407-4e8d-8d78-d85e345491d1--&gt;
&lt;!-- begin heading_2 bf84cd9a-09ad-4313-86cd-c713178d9370--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-bf84cd9a-09ad-4313-86cd-c713178d9370&quot;&gt;Conseils&lt;/h3&gt;
&lt;!-- end heading_2 bf84cd9a-09ad-4313-86cd-c713178d9370--&gt;
&lt;!-- begin paragraph e0c9f650-3679-41c8-b238-42c972002719--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e0c9f650-3679-41c8-b238-42c972002719&quot;&gt;Un jour viendra où il faudra impérativement arrêter d’utiliser des libs en Scala 2 qui ne seront plus maintenues notamment pour des raisons de sécurité. En effet, être compatible avec Scala 3 ne signifie pas que vous n’aurez jamais à migrer.&lt;/p&gt;
&lt;!-- end paragraph e0c9f650-3679-41c8-b238-42c972002719--&gt;
&lt;!-- begin paragraph 868c23e7-857d-4530-9e41-a3337f9f4f86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-868c23e7-857d-4530-9e41-a3337f9f4f86&quot;&gt;Voici quelques conseils pour préparer l’échéance : &lt;/p&gt;
&lt;!-- end paragraph 868c23e7-857d-4530-9e41-a3337f9f4f86--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Effectuer la migration progressivement pour fixer les problèmes petit à petit.&lt;/li&gt;&lt;li&gt;Arrêter le support des applications en Scala 2.12 et antérieures pour faciliter le passage a Scala 3.&lt;/li&gt;&lt;li&gt;Faites des tests de non-régression avec une bonne couverture de test (un couverture autour de 80% semble être une bonne recommandation).&lt;/li&gt;&lt;li&gt;Lisez &lt;a href=&quot;https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html&quot;&gt;la doc de Scala&lt;/a&gt; à propos de la migration.&lt;/li&gt;&lt;li&gt;Regardez l’état de la migration des libs dont vous dépendez.&lt;/li&gt;&lt;li&gt;Arrêtez d’utiliser les technologies/features dont vous savez à l’avance qu’elles ne seront pas portées en Scala 3 (les macros, scala reflect).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin divider ba61b84e-dfe5-4984-b084-c9de71644f92--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-ba61b84e-dfe5-4984-b084-c9de71644f92&quot;&gt;
&lt;!-- end divider ba61b84e-dfe5-4984-b084-c9de71644f92--&gt;
&lt;!-- begin heading_1 077258a0-ade5-4eb4-bcce-127cff75a353--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-077258a0-ade5-4eb4-bcce-127cff75a353&quot;&gt;Erreurs &lt;/h2&gt;
&lt;!-- end heading_1 077258a0-ade5-4eb4-bcce-127cff75a353--&gt;
&lt;!-- begin heading_2 69074bfd-d78c-4ff8-9654-a5df41c9c690--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-69074bfd-d78c-4ff8-9654-a5df41c9c690&quot;&gt;signature has wrong version&lt;/h3&gt;
&lt;!-- end heading_2 69074bfd-d78c-4ff8-9654-a5df41c9c690--&gt;
&lt;!-- begin code f85204f5-4dd3-49d5-a3ea-941d7321cbde--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f85204f5-4dd3-49d5-a3ea-941d7321cbde&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;[error] (class scala.tools.tasty.UnpickleException/TASTy signature has wrong version.
[error]  expected: {majorVersion: 28, minorVersion: 1}
[error]  found   : {majorVersion: 28, minorVersion: 2}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f85204f5-4dd3-49d5-a3ea-941d7321cbde--&gt;
&lt;!-- begin paragraph 84328058-838e-4226-84c8-2f24fe66e18c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-84328058-838e-4226-84c8-2f24fe66e18c&quot;&gt;Cela arrive quand les versions de scala 2.13 et scala 3 ne sont pas compatible, il faut en général monter la version de scala 2.13 (la dernière c’est 2.13.9).&lt;/p&gt;
&lt;!-- end paragraph 84328058-838e-4226-84c8-2f24fe66e18c--&gt;
&lt;!-- begin heading_2 8eb31089-ffbc-4a96-9545-5344c91c4923--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8eb31089-ffbc-4a96-9545-5344c91c4923&quot;&gt;inline in scala 2&lt;/h3&gt;
&lt;!-- end heading_2 8eb31089-ffbc-4a96-9545-5344c91c4923--&gt;
&lt;!-- begin code 31f32706-47d5-4ebe-8491-22447014a6ae--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-31f32706-47d5-4ebe-8491-22447014a6ae&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;[error] scala.reflect.internal.Types$TypeError: Unsupported Scala 3 inline method deriveSubtype; found in trait magnolia1.Derivation.


[error] stack trace is suppressed; run last c / Compile / compileIncremental for the full output
[error] (c / Compile / compileIncremental) scala.reflect.internal.Types$TypeError: Unsupported Scala 3 inline method deriveSubtype; found in trait magnolia1.Derivation.&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 31f32706-47d5-4ebe-8491-22447014a6ae--&gt;
&lt;!-- begin heading_2 89012287-35c7-4fe5-b52b-5bd667483930--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-89012287-35c7-4fe5-b52b-5bd667483930&quot;&gt;WeakTypeTag evidences&lt;/h3&gt;
&lt;!-- end heading_2 89012287-35c7-4fe5-b52b-5bd667483930--&gt;
&lt;!-- begin code 3776d175-d63a-46ba-afb0-61edf343c364--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3776d175-d63a-46ba-afb0-61edf343c364&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;[error][...] scala:26:51: macro implementations cannot have implicit parameters other than WeakTypeTag evidences
[error]implicit def gen[T]: Show[T] = macro Magnolia.gen[T]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3776d175-d63a-46ba-afb0-61edf343c364--&gt;
&lt;!-- begin paragraph 80151f56-ad9a-4db2-86ae-6c6134234045--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-80151f56-ad9a-4db2-86ae-6c6134234045&quot;&gt;Cela arrive si &lt;code&gt;scala-reflect&lt;/code&gt; est manquant dans un projet magnolia - Scala 2.&lt;/p&gt;
&lt;!-- end paragraph 80151f56-ad9a-4db2-86ae-6c6134234045--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:0c7a8332e89c4408aba666d84b1e0762</id>
    <title>Magnolia (chopped and screwed) </title>
    <updated>2022-09-15T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/magnolia-chopped-and-screwed.html"/>
    <!--summary Une introduction tranquille à magnolia -->    <author>
      <name>Bastien Guihard</name>
      <uri>https://univalence.io/blog/auteurs/bastien-guihard.html</uri>
    </author>        <category term="Scala"></category>    <category term="Magnolia"></category>    <content type="html">
&lt;!-- begin heading_2 d9c32553-f105-42f4-815e-83b05f234901--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d9c32553-f105-42f4-815e-83b05f234901&quot;&gt;Introduction&lt;/h3&gt;
&lt;!-- end heading_2 d9c32553-f105-42f4-815e-83b05f234901--&gt;
&lt;!-- begin paragraph 312280d2-a084-4148-8800-31a899386f7c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-312280d2-a084-4148-8800-31a899386f7c&quot;&gt;Vous l’aurez surement remarqué, écrire des &lt;code&gt;typeclass&lt;/code&gt; peut être assez mécanique et répétitif. &lt;/p&gt;
&lt;!-- end paragraph 312280d2-a084-4148-8800-31a899386f7c--&gt;
&lt;!-- begin paragraph 21a6d370-8d48-4aad-8cff-ec246bd6e27c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-21a6d370-8d48-4aad-8cff-ec246bd6e27c&quot;&gt;Magnolia est une bibliothèque logicielle qui permet de faire de la dérivation de &lt;code&gt;typeclass&lt;/code&gt;, c&amp;#39;est-à-dire, d’écrire des &lt;code&gt;typeclass&lt;/code&gt; pour nous.&lt;/p&gt;
&lt;!-- end paragraph 21a6d370-8d48-4aad-8cff-ec246bd6e27c--&gt;
&lt;!-- begin paragraph 0d7ac7e3-bdbe-40d1-99df-ed7fa7ead463--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0d7ac7e3-bdbe-40d1-99df-ed7fa7ead463&quot;&gt;La littérature technique à propos de magnolia n’est pas simple à comprendre. &lt;/p&gt;
&lt;!-- end paragraph 0d7ac7e3-bdbe-40d1-99df-ed7fa7ead463--&gt;
&lt;!-- begin paragraph 4a5937ff-50a2-4f9a-b398-345bb75cb14e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a5937ff-50a2-4f9a-b398-345bb75cb14e&quot;&gt;Dans cet article, nous allons tâcher de comprendre, par étape, comment fonctionne magnolia par la pratique.&lt;/p&gt;
&lt;!-- end paragraph 4a5937ff-50a2-4f9a-b398-345bb75cb14e--&gt;
&lt;!-- begin paragraph 50b992e7-bed9-4c59-bf38-edb113be878b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50b992e7-bed9-4c59-bf38-edb113be878b&quot;&gt;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Chopped_and_screwed&quot;&gt;https://fr.wikipedia.org/wiki/Chopped_and_screwed&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 50b992e7-bed9-4c59-bf38-edb113be878b--&gt;
&lt;!-- begin heading_2 8d663570-c030-4fc9-80f4-552740ba9e03--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8d663570-c030-4fc9-80f4-552740ba9e03&quot;&gt;Qu’est-ce qu’on bricole ?&lt;/h3&gt;
&lt;!-- end heading_2 8d663570-c030-4fc9-80f4-552740ba9e03--&gt;
&lt;!-- begin paragraph 504190da-4be2-4fcf-8676-958237120be7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-504190da-4be2-4fcf-8676-958237120be7&quot;&gt;Imaginons un ensemble de &lt;code&gt;case class&lt;/code&gt; assez différentes les unes des autres : &lt;/p&gt;
&lt;!-- end paragraph 504190da-4be2-4fcf-8676-958237120be7--&gt;
&lt;!-- begin code 07991d93-406e-432b-888a-8bfa14b5a39c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-07991d93-406e-432b-888a-8bfa14b5a39c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  sealed trait Race
  case class Human(firstName: String, sound: String, socialSecurityNumber: Int) extends Race
  case class Dwarf(name: String, sound: String, goldAmount: Int)                extends Race
  case class Elf(name: String, skincareRoutine: String)                         extends Race&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 07991d93-406e-432b-888a-8bfa14b5a39c--&gt;
&lt;!-- begin paragraph 82f481a8-a63e-47c2-abd2-e4194b85f0ec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-82f481a8-a63e-47c2-abd2-e4194b85f0ec&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 82f481a8-a63e-47c2-abd2-e4194b85f0ec--&gt;
&lt;!-- begin paragraph cfeac9b0-4d85-4df9-b3b1-ba20b36c81dd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cfeac9b0-4d85-4df9-b3b1-ba20b36c81dd&quot;&gt;Pour chacune de ces &lt;code&gt;case class&lt;/code&gt;, on souhaite pouvoir écrire le code suivant : &lt;/p&gt;
&lt;!-- end paragraph cfeac9b0-4d85-4df9-b3b1-ba20b36c81dd--&gt;
&lt;!-- begin code 6150fb91-6417-48b1-9365-5d7127d30245--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6150fb91-6417-48b1-9365-5d7127d30245&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val human = Human(firstName = &amp;quot;John&amp;quot;, lastName = &amp;quot;Doe&amp;quot;, sound = &amp;quot;Hello&amp;quot;, socialSecurityNumber = 123)

println(human.shout)            
println(human.epicDescription)  &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6150fb91-6417-48b1-9365-5d7127d30245--&gt;
&lt;!-- begin paragraph 7fbc73e2-e941-4516-b2d1-c349756ab806--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7fbc73e2-e941-4516-b2d1-c349756ab806&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7fbc73e2-e941-4516-b2d1-c349756ab806--&gt;
&lt;!-- begin paragraph 52e0c690-b0bc-4650-840f-6bf2057bf139--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-52e0c690-b0bc-4650-840f-6bf2057bf139&quot;&gt;Et obtenir une super sortie épique sur notre terminal : &lt;/p&gt;
&lt;!-- end paragraph 52e0c690-b0bc-4650-840f-6bf2057bf139--&gt;
&lt;!-- begin code 4995c8f3-7887-4838-ae77-e78b239525d7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4995c8f3-7887-4838-ae77-e78b239525d7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;John Doe shouts: HELLO

FIRSTNAME              : John
LASTNAME               : Doe
SOUND                  : Hello
SOCIAL SECURITY NUMBER : 123&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4995c8f3-7887-4838-ae77-e78b239525d7--&gt;
&lt;!-- begin heading_2 b9b05982-49eb-4e4d-9cea-d9e2489663d2--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b9b05982-49eb-4e4d-9cea-d9e2489663d2&quot;&gt;Approches naïves : Pourquoi utiliser la dérivation de typeclass ?&lt;/h3&gt;
&lt;!-- end heading_2 b9b05982-49eb-4e4d-9cea-d9e2489663d2--&gt;
&lt;!-- begin paragraph f9af4d52-a92b-474a-866a-85a410221ed4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9af4d52-a92b-474a-866a-85a410221ed4&quot;&gt;Afin d’apprécier les bénéfices d’un système de dérivation de typeclass tel que magnolia, intéressons-nous à ce que serait la vie sans magnolia.&lt;/p&gt;
&lt;!-- end paragraph f9af4d52-a92b-474a-866a-85a410221ed4--&gt;
&lt;!-- begin heading_3 21a86cfd-4e96-4a12-9694-24bcf5019e86--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-21a86cfd-4e96-4a12-9694-24bcf5019e86&quot;&gt;Méthode à la mano&lt;/h4&gt;
&lt;!-- end heading_3 21a86cfd-4e96-4a12-9694-24bcf5019e86--&gt;
&lt;!-- begin paragraph 56cfb0e0-303b-4492-9f12-e359f5faf71a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-56cfb0e0-303b-4492-9f12-e359f5faf71a&quot;&gt;On va commencer par écrire une &lt;code&gt;typeclass&lt;/code&gt; qui représente notre comportement.&lt;/p&gt;
&lt;!-- end paragraph 56cfb0e0-303b-4492-9f12-e359f5faf71a--&gt;
&lt;!-- begin paragraph ffe9f8e8-9ce6-491a-a346-3480bc5634e2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ffe9f8e8-9ce6-491a-a346-3480bc5634e2&quot;&gt;On veut que nos personnages soient en mesure de pousser un cri et de décrire leurs attributs :&lt;/p&gt;
&lt;!-- end paragraph ffe9f8e8-9ce6-491a-a346-3480bc5634e2--&gt;
&lt;!-- begin code 01022575-8bd6-45be-b300-c90f28a335f5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-01022575-8bd6-45be-b300-c90f28a335f5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  sealed trait Shout[A] {
    def epicDescription(a: A): String
    def shout(a: A): String
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 01022575-8bd6-45be-b300-c90f28a335f5--&gt;
&lt;!-- begin paragraph dd8ed801-043f-4321-8264-c16d811a3699--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dd8ed801-043f-4321-8264-c16d811a3699&quot;&gt;Commençons par implémenter ce comportement pour un &lt;code&gt;Human&lt;/code&gt; et un &lt;code&gt;Dwarf&lt;/code&gt;:&lt;/p&gt;
&lt;!-- end paragraph dd8ed801-043f-4321-8264-c16d811a3699--&gt;
&lt;!-- begin code bcd5b3a9-5b7f-4cee-bab1-92d75e719249--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bcd5b3a9-5b7f-4cee-bab1-92d75e719249&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  val shoutingHuman: Shout[Human] =
    new Shout[Human] {
      override def epicDescription(a: Human): String =
        s&amp;quot;&amp;quot;&amp;quot;
           |FIRSTNAME              : ${a.firstName}
           |LASTNAME               : ${a.lastName}
           |SOUND                  : ${a.sound}
           |SOCIAL SECURITY NUMBER : ${a.socialSecurityNumber}
           |&amp;quot;&amp;quot;&amp;quot;.stripMargin
      override def shout(a: Human): String = a.sound.toUpperCase
    }

  val shoutingDwarf: Shout[Dwarf] =
    new Shout[Dwarf] {
      override def epicDescription(a: Dwarf): String =
        s&amp;quot;&amp;quot;&amp;quot;
           |NAME  : ${a.name}
           |SOUND : ${a.sound}
           |GOLD  : ${a.goldAmount}
           |&amp;quot;&amp;quot;&amp;quot;.stripMargin
      override def shout(a: Dwarf): String = a.sound.toUpperCase
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bcd5b3a9-5b7f-4cee-bab1-92d75e719249--&gt;
&lt;!-- begin paragraph 7c305429-dabb-47e0-817b-859d006348d0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7c305429-dabb-47e0-817b-859d006348d0&quot;&gt;On se rajoute une petite classe implicite afin de faciliter l’écriture de notre comportement :&lt;/p&gt;
&lt;!-- end paragraph 7c305429-dabb-47e0-817b-859d006348d0--&gt;
&lt;!-- begin code d36d93db-65f9-4564-9efd-cfc39b888b8e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d36d93db-65f9-4564-9efd-cfc39b888b8e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  implicit class HumanOps(human: Human) {
    def shout: String           = shoutingHuman.shout(human)
    def epicDescription: String = shoutingHuman.epicDescription(human)
  }

  implicit class DwarfOps(dwarf: Dwarf) {
    def shout: String           = shoutingDwarf.shout(dwarf)
    def epicDescription: String = shoutingDwarf.epicDescription(dwarf)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d36d93db-65f9-4564-9efd-cfc39b888b8e--&gt;
&lt;!-- begin paragraph 5e229f6a-520c-4db6-bfa5-107bbc4d63af--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e229f6a-520c-4db6-bfa5-107bbc4d63af&quot;&gt;Et voilà le travail : &lt;/p&gt;
&lt;!-- end paragraph 5e229f6a-520c-4db6-bfa5-107bbc4d63af--&gt;
&lt;!-- begin code ca07b96e-413e-468a-9cf1-4907e2898096--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ca07b96e-413e-468a-9cf1-4907e2898096&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val johnDoe = Human(firstName = &amp;quot;John&amp;quot;, lastName = &amp;quot;Doe&amp;quot;, sound = &amp;quot;Hello&amp;quot;, socialSecurityNumber = 123)

println(johnDoe.shout)
println(johnDoe.epicDescription)

//  HELLO
//
//  FIRSTNAME              : John
//  LASTNAME               : Doe
//  SOUND                  : Hello
//  SOCIAL SECURITY NUMBER : 123&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ca07b96e-413e-468a-9cf1-4907e2898096--&gt;
&lt;!-- begin paragraph f2b03296-3142-4249-9185-6c8e902f7b6a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f2b03296-3142-4249-9185-6c8e902f7b6a&quot;&gt;Vous pouvez trouver le code de cette implémentation &lt;a href=&quot;https://github.com/univalence/magnolia-chopped-and-screwed/blob/master/core/src/main/scala/io/univalence/magnolia_chopped_and_screwed/00_Naive.scala&quot;&gt;ici&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph f2b03296-3142-4249-9185-6c8e902f7b6a--&gt;
&lt;!-- begin paragraph ee651b3c-1fa8-404b-9809-8c0f1934dce4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ee651b3c-1fa8-404b-9809-8c0f1934dce4&quot;&gt;Allez courage, il en reste encore un comme ça… pour le moment&lt;/p&gt;
&lt;!-- end paragraph ee651b3c-1fa8-404b-9809-8c0f1934dce4--&gt;
&lt;!-- begin paragraph 825b0d94-f451-4e03-ae21-acc62d98c652--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-825b0d94-f451-4e03-ae21-acc62d98c652&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 825b0d94-f451-4e03-ae21-acc62d98c652--&gt;
&lt;!-- begin paragraph 79154615-306d-4b04-8493-5bce01e5a6a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79154615-306d-4b04-8493-5bce01e5a6a7&quot;&gt;On remarque que le code de &lt;code&gt;shoutingDwarf&lt;/code&gt; et &lt;code&gt;shoutingHuman&lt;/code&gt; est sensiblement le même, bien que l’on ne puisse pas le généraliser “facilement”. &lt;/p&gt;
&lt;!-- end paragraph 79154615-306d-4b04-8493-5bce01e5a6a7--&gt;
&lt;!-- begin paragraph 5d268a72-5b7f-4ddb-b4d2-81607e4455b3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d268a72-5b7f-4ddb-b4d2-81607e4455b3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5d268a72-5b7f-4ddb-b4d2-81607e4455b3--&gt;
&lt;!-- begin paragraph ce34044a-5044-49a9-8e58-04aa0c69ef76--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce34044a-5044-49a9-8e58-04aa0c69ef76&quot;&gt;Vous l’aurez compris, on va pouvoir se passer du processus répétitif d’implanter, plus ou moins, la même &lt;code&gt;typeclass&lt;/code&gt; plusieurs fois.&lt;/p&gt;
&lt;!-- end paragraph ce34044a-5044-49a9-8e58-04aa0c69ef76--&gt;
&lt;!-- begin paragraph 1d386eb9-bd2c-42f0-98d5-e2f909f789d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d386eb9-bd2c-42f0-98d5-e2f909f789d2&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1d386eb9-bd2c-42f0-98d5-e2f909f789d2--&gt;
&lt;!-- begin paragraph 116c4997-4a15-4d05-8b40-1578f1f64f42--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-116c4997-4a15-4d05-8b40-1578f1f64f42&quot;&gt;Pour nous permettre d’être plus efficace, on va avoir une &lt;b&gt;approche réflexive&lt;/b&gt;, autrement dit que l’on va chercher à écrire un &lt;b&gt;programme capable de s’inspecter lui-même&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 116c4997-4a15-4d05-8b40-1578f1f64f42--&gt;
&lt;!-- begin paragraph e0bb5ac3-a00e-4dfe-a4cf-f9149ed77943--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e0bb5ac3-a00e-4dfe-a4cf-f9149ed77943&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e0bb5ac3-a00e-4dfe-a4cf-f9149ed77943--&gt;
&lt;!-- begin paragraph 00b025ea-0fbc-45d9-bf6b-a146b6f70939--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-00b025ea-0fbc-45d9-bf6b-a146b6f70939&quot;&gt;Il existe deux types de réflexion : &lt;/p&gt;
&lt;!-- end paragraph 00b025ea-0fbc-45d9-bf6b-a146b6f70939--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;la réflexion au &lt;code&gt;runtime&lt;/code&gt; : &lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Elle permet d’inspecter les types des objets y compris génériques (après réification)&lt;/li&gt;&lt;li&gt;D’instancier de nouveau objets&lt;/li&gt;&lt;li&gt;D’appeler des méthodes d’un objet&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;la réflexion au &lt;code&gt;compiletime&lt;/code&gt; : &lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Permet de faire de la métaprogrammation, c&amp;#39;est-à-dire du code qui va explorer le code (en particulier pour magnolia, la structure des types) via certaines instructions spéciales et générer du code&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 6601b5b8-082f-4e2a-af5e-00f44e062ed0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6601b5b8-082f-4e2a-af5e-00f44e062ed0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6601b5b8-082f-4e2a-af5e-00f44e062ed0--&gt;
&lt;!-- begin paragraph 0780289e-ef63-40c9-a914-39d3b6bca1cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0780289e-ef63-40c9-a914-39d3b6bca1cf&quot;&gt;C’est la deuxième approche qui nous intéresse.&lt;/p&gt;
&lt;!-- end paragraph 0780289e-ef63-40c9-a914-39d3b6bca1cf--&gt;
&lt;!-- begin paragraph c9261487-7fcd-4540-8be4-958eaef206b2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c9261487-7fcd-4540-8be4-958eaef206b2&quot;&gt;En effet, &lt;b&gt;on veut que notre compilateur “écrive” les &lt;/b&gt;&lt;code&gt;&lt;b&gt;typeclasses&lt;/b&gt;&lt;/code&gt;&lt;b&gt; pour nous.&lt;/b&gt; &lt;/p&gt;
&lt;!-- end paragraph c9261487-7fcd-4540-8be4-958eaef206b2--&gt;
&lt;!-- begin paragraph 570eb2c8-580c-4632-b3c3-c52899bf2c55--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-570eb2c8-580c-4632-b3c3-c52899bf2c55&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 570eb2c8-580c-4632-b3c3-c52899bf2c55--&gt;
&lt;!-- begin paragraph a9446c9d-17ae-40cd-93c6-f9a216d93069--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a9446c9d-17ae-40cd-93c6-f9a216d93069&quot;&gt;Un des avantages de la réflexion au &lt;code&gt;compiletime&lt;/code&gt; est que le compilateur vérifie que le code généré est correct. À l’instar de la réflexion au runtime qui peut planter… au runtime&lt;/p&gt;
&lt;!-- end paragraph a9446c9d-17ae-40cd-93c6-f9a216d93069--&gt;
&lt;!-- begin heading_2 a4e9bfb8-1126-45f0-bfe4-f8dc64c1f3ce--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-a4e9bfb8-1126-45f0-bfe4-f8dc64c1f3ce&quot;&gt;C’est là que magnolia intervient&lt;/h3&gt;
&lt;!-- end heading_2 a4e9bfb8-1126-45f0-bfe4-f8dc64c1f3ce--&gt;
&lt;!-- begin heading_3 74a22553-32d4-45bf-8626-3be56848764e--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-74a22553-32d4-45bf-8626-3be56848764e&quot;&gt;Niveau 1 : &lt;code&gt;Join&lt;/code&gt;&lt;/h4&gt;
&lt;!-- end heading_3 74a22553-32d4-45bf-8626-3be56848764e--&gt;
&lt;!-- begin paragraph 59def6b4-d611-44f0-ba06-cc8765f712cd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-59def6b4-d611-44f0-ba06-cc8765f712cd&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 59def6b4-d611-44f0-ba06-cc8765f712cd--&gt;
&lt;!-- begin paragraph f7197d9a-0525-4019-8fa0-d6ded48966e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f7197d9a-0525-4019-8fa0-d6ded48966e1&quot;&gt;Dans un premier temps, nous allons chercher à faire en sorte que le comportement de notre &lt;code&gt;typeclass&lt;/code&gt; soit disponible pour n’importe quelle &lt;code&gt;case class&lt;/code&gt;  &lt;/p&gt;
&lt;!-- end paragraph f7197d9a-0525-4019-8fa0-d6ded48966e1--&gt;
&lt;!-- begin paragraph b32d5b57-88a8-4425-8f8e-bb7874bfbc01--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b32d5b57-88a8-4425-8f8e-bb7874bfbc01&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b32d5b57-88a8-4425-8f8e-bb7874bfbc01--&gt;
&lt;!-- begin paragraph 5d327463-6c28-451e-96ab-72072f9a5692--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d327463-6c28-451e-96ab-72072f9a5692&quot;&gt;C’est la fonction &lt;code&gt;join&lt;/code&gt; qui permet de faire ça. On trouve aussi cette fonction sous le nom &lt;code&gt;combine&lt;/code&gt; &lt;/p&gt;
&lt;!-- end paragraph 5d327463-6c28-451e-96ab-72072f9a5692--&gt;
&lt;!-- begin paragraph 6597b0d4-0a3c-41e3-91dc-53ad0e86677f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6597b0d4-0a3c-41e3-91dc-53ad0e86677f&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6597b0d4-0a3c-41e3-91dc-53ad0e86677f--&gt;
&lt;!-- begin paragraph a5c3c119-7c51-4fb7-ad32-d119eebc5818--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a5c3c119-7c51-4fb7-ad32-d119eebc5818&quot;&gt;Ci-dessous, la structure de base d’une dérivation de &lt;code&gt;typeclass&lt;/code&gt; : &lt;/p&gt;
&lt;!-- end paragraph a5c3c119-7c51-4fb7-ad32-d119eebc5818--&gt;
&lt;!-- begin code 8cce5bd3-8be0-48df-a1d7-abaed9b30eae--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8cce5bd3-8be0-48df-a1d7-abaed9b30eae&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object ShoutDerivation {

  // Alias de type
  type Typeclass[T] = Shout[T]

  // Fonction qui va permettre de dériver notre comportement
  def join[T](ctx: CaseClass[Shout, T]): Shout[T] =
    new Shout[T] {
      override def epicDescription(a: T): String = ???

      override def shout(a: T): String = ???
    }

	// Cette fonction nous permet de générer nos typeclass
  implicit def gen[T]: Shout[T] = macro Magnolia.gen[T]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8cce5bd3-8be0-48df-a1d7-abaed9b30eae--&gt;
&lt;!-- begin paragraph 7546a4c2-5076-4cea-a28a-f5034b42fabe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7546a4c2-5076-4cea-a28a-f5034b42fabe&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7546a4c2-5076-4cea-a28a-f5034b42fabe--&gt;
&lt;!-- begin paragraph a0d218bb-56da-4b9a-a953-c90fcd077df5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a0d218bb-56da-4b9a-a953-c90fcd077df5&quot;&gt;Commençons par dériver la méthode &lt;code&gt;shout&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph a0d218bb-56da-4b9a-a953-c90fcd077df5--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;On sait que nos &lt;code&gt;case class&lt;/code&gt; ont généralement un argument &lt;code&gt;sound&lt;/code&gt; &lt;/li&gt;&lt;li&gt;On voudrait que la sortie ressemble à &lt;code&gt;SOUND TO UPPER CASE&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Les paramètres de nos &lt;code&gt;case class&lt;/code&gt; sont accessibles via &lt;code&gt;ctx.parameters&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph deeeae72-9c77-4241-9247-f156bcd7afd4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-deeeae72-9c77-4241-9247-f156bcd7afd4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph deeeae72-9c77-4241-9247-f156bcd7afd4--&gt;
&lt;!-- begin code 16034be7-0780-4d85-8012-7cf89999aa89--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-16034be7-0780-4d85-8012-7cf89999aa89&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;override def shout(a: T): String =
        // on cherche un argument sound
        ctx.parameters.find(_.label == &amp;quot;sound&amp;quot;) match {
          // si il existe on le renvoie en capitales
          case Some(value) =&amp;gt; value.typeclass.shout(value.dereference(a)).toUpperCase
          // sinon tant pis
          case None        =&amp;gt; &amp;quot;I can&amp;#39;t shout&amp;quot;
        }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 16034be7-0780-4d85-8012-7cf89999aa89--&gt;
&lt;!-- begin paragraph 5c42ffbd-0511-42af-8e13-1738638335cd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5c42ffbd-0511-42af-8e13-1738638335cd&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5c42ffbd-0511-42af-8e13-1738638335cd--&gt;
&lt;!-- begin paragraph 4f66e9ae-2459-4a7e-85c5-8852af4af1fa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4f66e9ae-2459-4a7e-85c5-8852af4af1fa&quot;&gt;Si on essaye de compiler maintenant, voilà ce qu’il risque d’arriver : &lt;/p&gt;
&lt;!-- end paragraph 4f66e9ae-2459-4a7e-85c5-8852af4af1fa--&gt;
&lt;!-- begin code be222ce8-53db-4168-80e9-e03fa5caeca7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-be222ce8-53db-4168-80e9-e03fa5caeca7&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;magnolia: could not find Shout.Typeclass for type String
    in parameter &amp;#39;firstName&amp;#39; of product type io.univalence.magnolia_chopped_and_screwed.Race.Human

  val shoutingHuman: Shout[Human] = ShoutDerivation.gen[Human]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code be222ce8-53db-4168-80e9-e03fa5caeca7--&gt;
&lt;!-- begin paragraph b02fa2d2-ecd1-47fc-ae2c-81ffee84b361--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b02fa2d2-ecd1-47fc-ae2c-81ffee84b361&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b02fa2d2-ecd1-47fc-ae2c-81ffee84b361--&gt;
&lt;!-- begin paragraph 77cb95bb-73e7-449e-9ccf-65c4f0ab96da--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77cb95bb-73e7-449e-9ccf-65c4f0ab96da&quot;&gt;Effectivement, à ce stade, magnolia ne sait pas quoi faire des &lt;code&gt;String&lt;/code&gt; et des &lt;code&gt;Int&lt;/code&gt; qui composent nos &lt;code&gt;Race&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 77cb95bb-73e7-449e-9ccf-65c4f0ab96da--&gt;
&lt;!-- begin paragraph 35e5a09d-2936-4601-9dd0-24c4e8a24b40--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35e5a09d-2936-4601-9dd0-24c4e8a24b40&quot;&gt;Pour notre sujet, nous avons juste besoin de renvoyer leur valeur sous forme de &lt;code&gt;String&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 35e5a09d-2936-4601-9dd0-24c4e8a24b40--&gt;
&lt;!-- begin code 74c6b7be-cb89-4163-a822-a9de6aee9946--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-74c6b7be-cb89-4163-a822-a9de6aee9946&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit val shoutString: Shout[String] =
    new Shout[String] {
      override def epicDescription(a: String): String = ??? // on garde l&amp;#39;implantation d&amp;#39;epicDescription pour le niveau 2
      override def shout(a: String): String           = a
    }

implicit val shoutInt: Shout[Int] =
    new Shout[Int] {
      override def epicDescription(a: Int): String = ???
      override def shout(a: Int): String           = a.toString
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 74c6b7be-cb89-4163-a822-a9de6aee9946--&gt;
&lt;!-- begin paragraph e98b2d84-a9ca-4558-9476-a7f868206aa4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e98b2d84-a9ca-4558-9476-a7f868206aa4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e98b2d84-a9ca-4558-9476-a7f868206aa4--&gt;
&lt;!-- begin paragraph 0255dcf5-88cc-4c7b-af96-5cd19d252722--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0255dcf5-88cc-4c7b-af96-5cd19d252722&quot;&gt;Et ça marche !&lt;/p&gt;
&lt;!-- end paragraph 0255dcf5-88cc-4c7b-af96-5cd19d252722--&gt;
&lt;!-- begin code 282a379b-95fe-407d-97ce-edb9d886120c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-282a379b-95fe-407d-97ce-edb9d886120c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val shoutingHuman: Shout[Human] = ShoutDerivation.gen[Human]

val human: Human = Human(firstName = &amp;quot;John&amp;quot;, lastName = &amp;quot;Doe&amp;quot;, sound = &amp;quot;Hello world&amp;quot;, socialSecurityNumber = 123)
println(shoutingHuman.shout(human))

//HELLO WORLD&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 282a379b-95fe-407d-97ce-edb9d886120c--&gt;
&lt;!-- begin paragraph 1d34df73-f913-4682-bd28-fdecd4d3d880--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d34df73-f913-4682-bd28-fdecd4d3d880&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1d34df73-f913-4682-bd28-fdecd4d3d880--&gt;
&lt;!-- begin paragraph 8eb0307b-7c86-4268-b8ae-1964f78f8e99--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8eb0307b-7c86-4268-b8ae-1964f78f8e99&quot;&gt;&lt;b&gt;Résumé du Niveau 1 :&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 8eb0307b-7c86-4268-b8ae-1964f78f8e99--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le code du Niveau 1 est disponible &lt;a href=&quot;https://github.com/univalence/magnolia-chopped-and-screwed/blob/master/core/src/main/scala/io/univalence/magnolia_chopped_and_screwed/01_Beginner.scala&quot;&gt;ici&lt;/a&gt;&lt;/li&gt;&lt;li&gt;La méthode &lt;code&gt;join&lt;/code&gt; nous permet de dériver le comportement d’une &lt;code&gt;typeclass&lt;/code&gt; sur n’importe quelle &lt;code&gt;case class&lt;/code&gt;&lt;/li&gt;&lt;li&gt;On peut agir sur les paramètres de nos &lt;code&gt;case class&lt;/code&gt; &lt;/li&gt;&lt;li&gt;Il faut décrire le comportement des primitives telles que &lt;code&gt;String&lt;/code&gt; et &lt;code&gt;Int&lt;/code&gt; afin d’effectuer une dérivation&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph e5008d7d-468b-4bd6-9bf8-b254fe1667ba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5008d7d-468b-4bd6-9bf8-b254fe1667ba&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e5008d7d-468b-4bd6-9bf8-b254fe1667ba--&gt;
&lt;!-- begin paragraph 2f4e9f11-72b3-4e3c-8ebe-568998b74748--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2f4e9f11-72b3-4e3c-8ebe-568998b74748&quot;&gt;&lt;b&gt;Comment améliorer le Niveau 1 : &lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 2f4e9f11-72b3-4e3c-8ebe-568998b74748--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;On se rend compte que c’est un peu bizarre de devoir définir &lt;code&gt;epicDescription&lt;/code&gt; pour un &lt;code&gt;String&lt;/code&gt; ou un &lt;code&gt;Int&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Il n’est pas très ergonomique d’écrire &lt;code&gt;shoutingHuman.shout(human)&lt;/code&gt; mais &lt;code&gt;human.shout&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_3 a2ea1143-3cb9-40e3-9075-7d2ee1a8e702--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-a2ea1143-3cb9-40e3-9075-7d2ee1a8e702&quot;&gt;Niveau 2&lt;/h4&gt;
&lt;!-- end heading_3 a2ea1143-3cb9-40e3-9075-7d2ee1a8e702--&gt;
&lt;!-- begin paragraph c386242b-f9fa-42cb-91d7-c95f6b54350f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c386242b-f9fa-42cb-91d7-c95f6b54350f&quot;&gt;Dans un premier temps, nous allons commencer par découper notre &lt;code&gt;typeclass&lt;/code&gt; &lt;code&gt;Shout&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph c386242b-f9fa-42cb-91d7-c95f6b54350f--&gt;
&lt;!-- begin code 28425a02-f969-4fba-bfcc-a1a2e4e2d9ef--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-28425a02-f969-4fba-bfcc-a1a2e4e2d9ef&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  sealed trait Shout[A] {
    def shout(a: A): String
  }

  // Comportement spécifiques aux case class
  trait ShoutCC[A] extends Shout[A] {
    def epicDescription(a: A): String
  }

  // Comportement pour les valeurs (String et Int dans notre cas)
  trait ShoutValue[A] extends Shout[A]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 28425a02-f969-4fba-bfcc-a1a2e4e2d9ef--&gt;
&lt;!-- begin paragraph 9e7caab8-f786-4782-affa-f87560218edf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e7caab8-f786-4782-affa-f87560218edf&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9e7caab8-f786-4782-affa-f87560218edf--&gt;
&lt;!-- begin paragraph 371f9de1-e7ea-43eb-aa9d-f2c1d5ec3824--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-371f9de1-e7ea-43eb-aa9d-f2c1d5ec3824&quot;&gt;Les avantages de ce pattern sont multiples. &lt;/p&gt;
&lt;!-- end paragraph 371f9de1-e7ea-43eb-aa9d-f2c1d5ec3824--&gt;
&lt;!-- begin paragraph af3ce69d-78c8-40a4-847a-d8bcdda461e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-af3ce69d-78c8-40a4-847a-d8bcdda461e4&quot;&gt;Dans un premier temps, il nous permet de définir plus facilement la manière dont nos valeurs doivent se comporter &lt;/p&gt;
&lt;!-- end paragraph af3ce69d-78c8-40a4-847a-d8bcdda461e4--&gt;
&lt;!-- begin paragraph 22b8521f-1a41-4542-9abd-c3b999cfdd9b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-22b8521f-1a41-4542-9abd-c3b999cfdd9b&quot;&gt;On verra un autre intérêt lorsqu’on s’attaquera au code de &lt;code&gt;join&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 22b8521f-1a41-4542-9abd-c3b999cfdd9b--&gt;
&lt;!-- begin paragraph b5913e2d-f74f-4239-942e-c46f688c51e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b5913e2d-f74f-4239-942e-c46f688c51e0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b5913e2d-f74f-4239-942e-c46f688c51e0--&gt;
&lt;!-- begin code 8979f117-35bd-4b5b-9ef0-813e2307fbba--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8979f117-35bd-4b5b-9ef0-813e2307fbba&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit val shoutString: ShoutValue[String] = str =&amp;gt; str
implicit val shoutInt: ShoutValue[Int]       = i =&amp;gt; i.toString // plus besoin de définir epicDescription pour un String ou un Int&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8979f117-35bd-4b5b-9ef0-813e2307fbba--&gt;
&lt;!-- begin paragraph acf4230d-6786-4476-ad30-78e787d7a3f1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-acf4230d-6786-4476-ad30-78e787d7a3f1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph acf4230d-6786-4476-ad30-78e787d7a3f1--&gt;
&lt;!-- begin paragraph bef8a10e-a8cf-42ed-800b-859809a74b61--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bef8a10e-a8cf-42ed-800b-859809a74b61&quot;&gt;Aussi, on peut ajouter une &lt;code&gt;implicit class&lt;/code&gt; qui va nous permettre d’écrire &lt;code&gt;human.shout&lt;/code&gt; &lt;/p&gt;
&lt;!-- end paragraph bef8a10e-a8cf-42ed-800b-859809a74b61--&gt;
&lt;!-- begin code f5cf0339-15bb-4451-945e-ea1654f1c4c4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f5cf0339-15bb-4451-945e-ea1654f1c4c4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// On prend n&amp;#39;importe quelle case class et, si disponible, son implantation de Shout
implicit class ShoutCCOps[A &amp;lt;: AnyRef with Product](a: A)(implicit shoutCC: ShoutCC[A]) {
    // on expose pour les case class shout et epicDescription
    def shout: String           = shoutCC.shout(a)
    def epicDescription: String = shoutCC.epicDescription(a)
  }

val human = Human(firstName = &amp;quot;John&amp;quot;, lastName = &amp;quot;Doe&amp;quot;, sound = &amp;quot;Hello world&amp;quot;, socialSecurityNumber = 123)

// Grace à l&amp;#39;implicit class précédente, on peut écrire :
println(human.shout) //HELLO WORLD&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f5cf0339-15bb-4451-945e-ea1654f1c4c4--&gt;
&lt;!-- begin paragraph 34ead289-9dfb-41a8-b219-ca8fbae4ed15--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-34ead289-9dfb-41a8-b219-ca8fbae4ed15&quot;&gt;Intéressons-nous maintenant au code de &lt;code&gt;join&lt;/code&gt; pour &lt;code&gt;epicDescritpion&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 34ead289-9dfb-41a8-b219-ca8fbae4ed15--&gt;
&lt;!-- begin paragraph 7ec446c8-9f37-4174-97fa-5163c66bdaaa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7ec446c8-9f37-4174-97fa-5163c66bdaaa&quot;&gt;C’est un cas classique, &lt;code&gt;ctx&lt;/code&gt; nous permet d’obtenir le nom des paramètres et le déréférencement de la &lt;code&gt;case class&lt;/code&gt; nous permet d’obtenir les valeurs associées. &lt;/p&gt;
&lt;!-- end paragraph 7ec446c8-9f37-4174-97fa-5163c66bdaaa--&gt;
&lt;!-- begin code 091a89a0-fdca-4d89-a5b9-66a9e86acaef--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-091a89a0-fdca-4d89-a5b9-66a9e86acaef&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def join[T](ctx: CaseClass[Shout, T]): ShoutCC[T] =
    new ShoutCC[T] {
      override def epicDescription(a: T): String =
        ctx.parameters
          .map(param =&amp;gt; s&amp;quot;\n \t${param.label.toUpperCase} = ${param.dereference(a)}&amp;quot;)
          .mkString&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 091a89a0-fdca-4d89-a5b9-66a9e86acaef--&gt;
&lt;!-- begin paragraph d11a07f5-ef84-4b78-a440-be78df3c0265--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d11a07f5-ef84-4b78-a440-be78df3c0265&quot;&gt;Jusqu’ici tout va bien :&lt;/p&gt;
&lt;!-- end paragraph d11a07f5-ef84-4b78-a440-be78df3c0265--&gt;
&lt;!-- begin code 7e4db729-716a-46fd-a090-645092ed70c0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7e4db729-716a-46fd-a090-645092ed70c0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  FIRSTNAME = John
 	LASTNAME = Doe
 	SOUND = Hello world
 	SOCIALSECURITYNUMBER = 123&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7e4db729-716a-46fd-a090-645092ed70c0--&gt;
&lt;!-- begin paragraph 5f41a63c-319c-4ac5-809f-8fec1fcb4880--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f41a63c-319c-4ac5-809f-8fec1fcb4880&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5f41a63c-319c-4ac5-809f-8fec1fcb4880--&gt;
&lt;!-- begin paragraph f3e6b57f-f060-4e2a-8104-702ad134cb4c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f3e6b57f-f060-4e2a-8104-702ad134cb4c&quot;&gt;Mais imaginons maintenant que l’on décide d’ajouter une nouvelle case classe, composée de deux autres &lt;/p&gt;
&lt;!-- end paragraph f3e6b57f-f060-4e2a-8104-702ad134cb4c--&gt;
&lt;!-- begin paragraph e27da8b3-6de0-4a7a-8176-25c3454e9bbb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e27da8b3-6de0-4a7a-8176-25c3454e9bbb&quot;&gt;Par exemple, une petite personne qui serait le produit d’un humain et d’un nain&lt;/p&gt;
&lt;!-- end paragraph e27da8b3-6de0-4a7a-8176-25c3454e9bbb--&gt;
&lt;!-- begin paragraph 8c2dbde6-a9c7-49f1-8ba5-538e43e0f7d0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c2dbde6-a9c7-49f1-8ba5-538e43e0f7d0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8c2dbde6-a9c7-49f1-8ba5-538e43e0f7d0--&gt;
&lt;!-- begin code 5ee26264-12cd-4720-925c-ccfbe03872aa--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5ee26264-12cd-4720-925c-ccfbe03872aa&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class LittlePerson(human: Human, dwarf: Dwarf)extends Race

val human        = Human(firstName = &amp;quot;John&amp;quot;, lastName = &amp;quot;Doe&amp;quot;, sound = &amp;quot;Hello world&amp;quot;, socialSecurityNumber = 123)
val dwarf        = Dwarf(name = &amp;quot;gimli&amp;quot;, sound = &amp;quot;uh&amp;quot;, goldAmount = 1000)

val littlePerson = LittlePerson(human, dwarf)
println(littlePerson.epicDescription)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5ee26264-12cd-4720-925c-ccfbe03872aa--&gt;
&lt;!-- begin paragraph 0f9ec1fb-79ca-42f4-aa4f-cef07baa4d27--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0f9ec1fb-79ca-42f4-aa4f-cef07baa4d27&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0f9ec1fb-79ca-42f4-aa4f-cef07baa4d27--&gt;
&lt;!-- begin paragraph 6c307c13-fd2a-4bbc-b50a-3e4d7e4076f5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6c307c13-fd2a-4bbc-b50a-3e4d7e4076f5&quot;&gt;On peut facilement imaginer que la sortie ne va pas ressembler à notre super formatage avec des sauts de lignes et des tabulations 🥲&lt;/p&gt;
&lt;!-- end paragraph 6c307c13-fd2a-4bbc-b50a-3e4d7e4076f5--&gt;
&lt;!-- begin code 9ea85f56-9223-4eb1-9a43-f46c3efa0ff2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9ea85f56-9223-4eb1-9a43-f46c3efa0ff2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;HUMAN = Human(John,Doe,Hello world,123) 	
DWARF = Dwarf(gimli,uh,1000)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9ea85f56-9223-4eb1-9a43-f46c3efa0ff2--&gt;
&lt;!-- begin paragraph 6bbcbb9a-5e9f-4f17-ae5b-2c929f536c85--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6bbcbb9a-5e9f-4f17-ae5b-2c929f536c85&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6bbcbb9a-5e9f-4f17-ae5b-2c929f536c85--&gt;
&lt;!-- begin paragraph 2280b070-01e6-4054-a67e-8f645c3907a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2280b070-01e6-4054-a67e-8f645c3907a9&quot;&gt;Effectivement rien ne va plus et c’est là que notre pattern qui visait à séparer le comportement des valeurs et des cases classes devient pratique :&lt;/p&gt;
&lt;!-- end paragraph 2280b070-01e6-4054-a67e-8f645c3907a9--&gt;
&lt;!-- begin code e495c67c-f4de-49f2-add2-ec874d5e5f9b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e495c67c-f4de-49f2-add2-ec874d5e5f9b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def join[T](ctx: CaseClass[Shout, T]): ShoutCC[T] =
    new ShoutCC[T] {
      override def epicDescription(a: T): String =
        ctx.parameters
          .map(param =&amp;gt;
            param.typeclass match {
              // On pattern match sur le type des arguments
              case cc: ShoutCC[param.PType] =&amp;gt; s&amp;quot;${param.label} part: [${cc.epicDescription(param.dereference(a))}] \n&amp;quot;
              case cc: ShoutValue[param.PType] =&amp;gt; s&amp;quot;\n \t${param.label.toUpperCase} = ${cc.shout(param.dereference(a))}&amp;quot;
            }
          )
          .mkString&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e495c67c-f4de-49f2-add2-ec874d5e5f9b--&gt;
&lt;!-- begin paragraph 1031360d-109c-47d2-9f44-8f9fff06f95b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1031360d-109c-47d2-9f44-8f9fff06f95b&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1031360d-109c-47d2-9f44-8f9fff06f95b--&gt;
&lt;!-- begin paragraph 2cc0b5d6-82e7-499f-99a3-9276c0f93f5d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2cc0b5d6-82e7-499f-99a3-9276c0f93f5d&quot;&gt;En faisant du pattern matching sur la &lt;code&gt;typeclass&lt;/code&gt; (disponible dans ctx) on peut avoir des comportements distincts pour chaque type :&lt;/p&gt;
&lt;!-- end paragraph 2cc0b5d6-82e7-499f-99a3-9276c0f93f5d--&gt;
&lt;!-- begin paragraph 49ac1085-2c2a-4cfa-b8e5-cdc1404438fe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-49ac1085-2c2a-4cfa-b8e5-cdc1404438fe&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 49ac1085-2c2a-4cfa-b8e5-cdc1404438fe--&gt;
&lt;!-- begin code 608fbce2-5a08-4304-b02a-e9df892528d9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-608fbce2-5a08-4304-b02a-e9df892528d9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;human part: [
 	FIRSTNAME = John
 	LASTNAME = Doe
 	SOUND = Hello world
 	SOCIALSECURITYNUMBER = 123] 
dwarf part: [
 	NAME = gimli
 	SOUND = uh
 	GOLDAMOUNT = 1000]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 608fbce2-5a08-4304-b02a-e9df892528d9--&gt;
&lt;!-- begin paragraph a84888c5-e6fe-4679-bd4a-b28d80216d6e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a84888c5-e6fe-4679-bd4a-b28d80216d6e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a84888c5-e6fe-4679-bd4a-b28d80216d6e--&gt;
&lt;!-- begin paragraph 068b052b-ab7f-460b-a180-c28b31e2f71a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-068b052b-ab7f-460b-a180-c28b31e2f71a&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 068b052b-ab7f-460b-a180-c28b31e2f71a--&gt;
&lt;!-- begin paragraph e51f4ced-8dce-467b-8d5d-aa360e320794--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e51f4ced-8dce-467b-8d5d-aa360e320794&quot;&gt;&lt;b&gt;Résumé du Niveau 2 :&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph e51f4ced-8dce-467b-8d5d-aa360e320794--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le code du Niveau 2 est disponible &lt;a href=&quot;https://github.com/univalence/magnolia-chopped-and-screwed/blob/master/core/src/main/scala/io/univalence/magnolia_chopped_and_screwed/02_Advanced.scala&quot;&gt;ici&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Séparer les comportements des valeurs et des case classes nous permet d’écrire du code plus concis et nous facilite l’écriture de la dérivation.&lt;/li&gt;&lt;li&gt;La dérivation de &lt;code&gt;typeclass&lt;/code&gt; ne nous empêche pas de créer une classe implicite &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 4228de0c-5fcb-4ac0-9fc3-8415392f1227--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4228de0c-5fcb-4ac0-9fc3-8415392f1227&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4228de0c-5fcb-4ac0-9fc3-8415392f1227--&gt;
&lt;!-- begin paragraph ec844adc-4f60-4486-9d90-a4136e426622--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec844adc-4f60-4486-9d90-a4136e426622&quot;&gt;&lt;b&gt;Comment améliorer le Niveau 2 : &lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph ec844adc-4f60-4486-9d90-a4136e426622--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Dans cet exemple, on ne manipule jamais Race directement, il se passe quoi si on fait ça ? &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_3 468a7cff-de01-40de-8020-ef8d4c68600b--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-468a7cff-de01-40de-8020-ef8d4c68600b&quot;&gt;Niveau 3&lt;/h4&gt;
&lt;!-- end heading_3 468a7cff-de01-40de-8020-ef8d4c68600b--&gt;
&lt;!-- begin paragraph 565f236e-c97b-4d2e-9255-4183edb7a269--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-565f236e-c97b-4d2e-9255-4183edb7a269&quot;&gt;Imaginons encore une fois une nouvelle &lt;code&gt;case class&lt;/code&gt; composée de deux &lt;code&gt;Race&lt;/code&gt; dont on ne connait pas le type. &lt;/p&gt;
&lt;!-- end paragraph 565f236e-c97b-4d2e-9255-4183edb7a269--&gt;
&lt;!-- begin code 54492822-1078-482d-b372-f563e8b9fa80--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-54492822-1078-482d-b372-f563e8b9fa80&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class GenericHybrid(race1: Race, race2: Race) extends Race&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 54492822-1078-482d-b372-f563e8b9fa80--&gt;
&lt;!-- begin paragraph 4419d7f3-e264-4050-b518-cba6d3a80ba6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4419d7f3-e264-4050-b518-cba6d3a80ba6&quot;&gt;À l’image du niveau 2, si on combine notre humain et notre nain, on aimerait avoir le même comportement que précédemment &lt;/p&gt;
&lt;!-- end paragraph 4419d7f3-e264-4050-b518-cba6d3a80ba6--&gt;
&lt;!-- begin code a86a280d-4978-4a7b-9333-146f3f24101b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a86a280d-4978-4a7b-9333-146f3f24101b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val human        = Human(firstName = &amp;quot;John&amp;quot;, lastName = &amp;quot;Doe&amp;quot;, sound = &amp;quot;Hello world&amp;quot;, socialSecurityNumber = 123)
val dwarf        = Dwarf(name = &amp;quot;gimli&amp;quot;, sound = &amp;quot;uh&amp;quot;, goldAmount = 1000)  
val hybrid       = GenericHybrid(human, dwarf)

println(AdvancedShoutDerivation.gen[GenericHybrid].shout(hybrid))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a86a280d-4978-4a7b-9333-146f3f24101b--&gt;
&lt;!-- begin paragraph 72af50d7-23d1-49da-945d-6bbdd77c39c8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-72af50d7-23d1-49da-945d-6bbdd77c39c8&quot;&gt;Sauf que :&lt;/p&gt;
&lt;!-- end paragraph 72af50d7-23d1-49da-945d-6bbdd77c39c8--&gt;
&lt;!-- begin code bd826523-2ab6-4893-9176-59a4b95d94f9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bd826523-2ab6-4893-9176-59a4b95d94f9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;magnolia: the method `split` must be defined on the derivation object AdvancedShoutDerivation to derive typeclasses for sealed traits
  println(AdvancedShoutDerivation.gen[GenericHybrid].shout(hybrid))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bd826523-2ab6-4893-9176-59a4b95d94f9--&gt;
&lt;!-- begin paragraph ed2beb8a-2257-454c-8b11-c6389722a2e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ed2beb8a-2257-454c-8b11-c6389722a2e1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ed2beb8a-2257-454c-8b11-c6389722a2e1--&gt;
&lt;!-- begin paragraph 934ba309-b6f1-4f55-a2d5-c8d1f12121b0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-934ba309-b6f1-4f55-a2d5-c8d1f12121b0&quot;&gt;On nous demande d’implémenter &lt;code&gt;split&lt;/code&gt; afin de pouvoir dériver des &lt;code&gt;typeclass&lt;/code&gt; pour des &lt;code&gt;sealed trait&lt;/code&gt;, dans notre cas Race&lt;/p&gt;
&lt;!-- end paragraph 934ba309-b6f1-4f55-a2d5-c8d1f12121b0--&gt;
&lt;!-- begin paragraph 82631cb7-cb29-4e9b-b594-7ce3066bf792--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-82631cb7-cb29-4e9b-b594-7ce3066bf792&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 82631cb7-cb29-4e9b-b594-7ce3066bf792--&gt;
&lt;!-- begin paragraph 8c1366ed-2aea-4f99-bd7f-1f63ab22f4e2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c1366ed-2aea-4f99-bd7f-1f63ab22f4e2&quot;&gt;Dans notre cas, l’implantation de &lt;code&gt;split&lt;/code&gt; consiste à :&lt;/p&gt;
&lt;!-- end paragraph 8c1366ed-2aea-4f99-bd7f-1f63ab22f4e2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Trouver pour une instance a de type &lt;code&gt;T&lt;/code&gt;, le sous-type de &lt;code&gt;Race&lt;/code&gt; auquel &lt;code&gt;T&lt;/code&gt; correspond&lt;/li&gt;&lt;li&gt;Une fois trouvé, d’invoquer &lt;code&gt;shout&lt;/code&gt; ou &lt;code&gt;epicDescription&lt;/code&gt; avec l’instance de &lt;code&gt;Race&lt;/code&gt; fournie(ici &lt;code&gt;a&lt;/code&gt;), castée dans le bon sous type de Race &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 35455491-b2b1-4219-8a72-016638f68561--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35455491-b2b1-4219-8a72-016638f68561&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 35455491-b2b1-4219-8a72-016638f68561--&gt;
&lt;!-- begin code 5bb6875b-49e6-44cf-bef6-42bb8ea5bf20--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5bb6875b-49e6-44cf-bef6-42bb8ea5bf20&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def split[T](ctx: SealedTrait[ShoutCC, T]): Shout[T] =
    new ShoutCC[T] {
      override def shout(a: T): String =
        // On cherche le bon type de `Race`
        ctx.split(a) { sub =&amp;gt;
          sub.typeclass.shout(sub.cast(a))
        }

      override def epicDescription(a: T): String =
        ctx.split(a) { sub =&amp;gt;
          sub.typeclass.epicDescription(sub.cast(a))
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5bb6875b-49e6-44cf-bef6-42bb8ea5bf20--&gt;
&lt;!-- begin paragraph e17192a3-717c-4d62-9f25-9538ae698c78--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e17192a3-717c-4d62-9f25-9538ae698c78&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e17192a3-717c-4d62-9f25-9538ae698c78--&gt;
&lt;!-- begin paragraph 0e31b2ab-09ab-4b76-83b8-df1d311cdfc5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e31b2ab-09ab-4b76-83b8-df1d311cdfc5&quot;&gt;Cette implémentation de &lt;code&gt;split&lt;/code&gt; conviendrait pour un autre &lt;code&gt;sealed trait&lt;/code&gt; que &lt;code&gt;Race&lt;/code&gt; mais malheureusement magnolia est limité quand il s’agit de dérivations récursives (des races dans des races).&lt;/p&gt;
&lt;!-- end paragraph 0e31b2ab-09ab-4b76-83b8-df1d311cdfc5--&gt;
&lt;!-- begin paragraph 439b94da-eff0-4656-8a51-7dcf4bd33156--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-439b94da-eff0-4656-8a51-7dcf4bd33156&quot;&gt;Pour palier a ce problème, on est obligé de forcer la dérivation de &lt;code&gt;Race&lt;/code&gt;:&lt;/p&gt;
&lt;!-- end paragraph 439b94da-eff0-4656-8a51-7dcf4bd33156--&gt;
&lt;!-- begin code 1b24b08d-0bbd-4042-8213-1111ab2c825c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1b24b08d-0bbd-4042-8213-1111ab2c825c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;...
// on force la dérivation de Race
implicit val shoutRace: Shout[Race] = AwesomeShoutDerivation.bugGenR[Race]
println(AwesomeShoutDerivation.gen[GenericHybrid].epicDescription(hybrid))

}
object AwesomeShoutDerivation {
  import Awesome._

  implicit def gen[T]: ShoutCC[T] = macro Magnolia.gen[T]

  def bugGenR[T]: Shout[T] = macro Magnolia.gen[T]

  type Typeclass[T] = Shout[T]

  def split[T](ctx: SealedTrait[Shout, T]): ShoutCC[T] =
    new ShoutCC[T] {
      override def shout(a: T): String =
        ctx.split(a) { sub =&amp;gt;
          sub.typeclass.asInstanceOf[ShoutCC[sub.SType]].shout(sub.cast(a))
        }

      override def epicDescription(a: T): String =
        ctx.split(a) { sub =&amp;gt;
          sub.typeclass.asInstanceOf[ShoutCC[sub.SType]].epicDescription(sub.cast(a))
        }
    }
...&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1b24b08d-0bbd-4042-8213-1111ab2c825c--&gt;
&lt;!-- begin paragraph 534aff00-2537-4708-83cb-abe8955d3edd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-534aff00-2537-4708-83cb-abe8955d3edd&quot;&gt;&lt;b&gt;Résumé du Niveau 3 :&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 534aff00-2537-4708-83cb-abe8955d3edd--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le code du Niveau 3 est disponible &lt;a href=&quot;https://github.com/univalence/magnolia-chopped-and-screwed/blob/master/core/src/main/scala/io/univalence/magnolia_chopped_and_screwed/03_Awesome.scala&quot;&gt;ici&lt;/a&gt;&lt;/li&gt;&lt;li&gt;On peut dériver des &lt;code&gt;typeclass&lt;/code&gt; pour des &lt;code&gt;sealed trait&lt;/code&gt; à l’aide de la méthode &lt;code&gt;split&lt;/code&gt;&lt;/li&gt;&lt;li&gt;On est obligé de forcer la dérivation de notre &lt;code&gt;sealed trait&lt;/code&gt; dans le cas de dérivations récursives.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 4f7100a0-8e60-450c-bd48-c4f5238ae547--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4f7100a0-8e60-450c-bd48-c4f5238ae547&quot;&gt;À suivre&lt;/h3&gt;
&lt;!-- end heading_2 4f7100a0-8e60-450c-bd48-c4f5238ae547--&gt;
&lt;!-- begin paragraph 6adfc0a8-4164-4817-9881-20ac933cf087--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6adfc0a8-4164-4817-9881-20ac933cf087&quot;&gt;Dans un prochain article, nous regarderons si Scala 3 nous permet de faire plus facilement des dérivations récursives de &lt;code&gt;typeclass&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 6adfc0a8-4164-4817-9881-20ac933cf087--&gt;
&lt;!-- begin heading_2 0640e242-f624-4ef6-bd50-73702ae20662--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-0640e242-f624-4ef6-bd50-73702ae20662&quot;&gt;Pour aller plus loin &lt;/h3&gt;
&lt;!-- end heading_2 0640e242-f624-4ef6-bd50-73702ae20662--&gt;
&lt;!-- begin paragraph a32c3d70-4a5c-4716-a726-0d90d5e8a1f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a32c3d70-4a5c-4716-a726-0d90d5e8a1f9&quot;&gt;&lt;a href=&quot;https://univalence.io/blog/articles/typeclass-derivation-faites-eclore-vos-instances-avec-magnolia/&quot;&gt;https://univalence.io/blog/articles/typeclass-derivation-faites-eclore-vos-instances-avec-magnolia/&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph a32c3d70-4a5c-4716-a726-0d90d5e8a1f9--&gt;
&lt;!-- begin paragraph 6f21d140-b1ec-486d-9f76-145ce0530ddc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6f21d140-b1ec-486d-9f76-145ce0530ddc&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6f21d140-b1ec-486d-9f76-145ce0530ddc--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:178acfd879fc409cbf9a2394d943f1b9</id>
    <title>ZIO qu&#039;est-ce que c&#039;est ?</title>
    <updated>2022-08-15T07:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/zio-qu-est-ce-que-c-est.html"/>
    <!--summary Vous faites du Scala, mais vous ne connaissez pas ZIO ? Cet article vous présente la bibliothèque, pour ceux qui ne savent pas à quoi ça sert ni par quel bout la prendre.-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Scala"></category>    <category term="ZIO"></category>    <content type="html">
&lt;!-- begin paragraph 80034519-4d72-46ce-8fe3-bf088ab5f9d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-80034519-4d72-46ce-8fe3-bf088ab5f9d7&quot;&gt;Vous faites du Scala, mais vous ne connaissez pas ZIO ? Pour vous, &lt;b&gt;ZIO n’est qu’un buzzword&lt;/b&gt; ? Vous ne comprenez pas à quoi cela sert ?&lt;/p&gt;
&lt;!-- end paragraph 80034519-4d72-46ce-8fe3-bf088ab5f9d7--&gt;
&lt;!-- begin paragraph ec6e6c1e-c553-4f26-a5a6-10c3e77338f7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec6e6c1e-c553-4f26-a5a6-10c3e77338f7&quot;&gt;Personnellement, j’ai eu du mal au début à comprendre en quoi cela pouvait m’être utile. Aujourd’hui, je vois mal comment je pourrais m’en passer. Alors, je vais essayer de vous expliquer exactement pourquoi &lt;b&gt;je pense que vous allez l’adorer&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph ec6e6c1e-c553-4f26-a5a6-10c3e77338f7--&gt;
&lt;!-- begin heading_1 0c378efd-0ac7-4e23-a31a-ce4af7fbf02e--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-0c378efd-0ac7-4e23-a31a-ce4af7fbf02e&quot;&gt;Pourquoi ZIO existe ?&lt;/h2&gt;
&lt;!-- end heading_1 0c378efd-0ac7-4e23-a31a-ce4af7fbf02e--&gt;
&lt;!-- begin paragraph b7b7623e-5c5f-46fe-b130-7f9eae2df7f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b7b7623e-5c5f-46fe-b130-7f9eae2df7f0&quot;&gt;Quand j’ai commencé à rentrer dans le monde fonctionnel, il y avait une chose que je n’arrivais vraiment pas à assimiler. &lt;/p&gt;
&lt;!-- end paragraph b7b7623e-5c5f-46fe-b130-7f9eae2df7f0--&gt;
&lt;!-- begin paragraph 5a3d52b1-7b8e-4325-aaa3-a48c4d61efc1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a3d52b1-7b8e-4325-aaa3-a48c4d61efc1&quot;&gt;&lt;b&gt;Comment gérer les effets&lt;/b&gt; ?&lt;/p&gt;
&lt;!-- end paragraph 5a3d52b1-7b8e-4325-aaa3-a48c4d61efc1--&gt;
&lt;!-- begin paragraph 85676767-0ed6-40cb-a305-70dddab13d3f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-85676767-0ed6-40cb-a305-70dddab13d3f&quot;&gt;J’arrivais à créer des programmes fonctionnels et pures. Mais, je ne pouvais rien faire d’essentiellement utile, puisque je ne savais pas comment communiquer avec une API, une base de données ou autre.&lt;/p&gt;
&lt;!-- end paragraph 85676767-0ed6-40cb-a305-70dddab13d3f--&gt;
&lt;!-- begin paragraph a1776f24-a7b0-40d9-b618-e824806150d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a1776f24-a7b0-40d9-b618-e824806150d4&quot;&gt;J’ai ensuite commencé à m’intéresser à cette question et je suis tombé sur cet adage : &lt;b&gt;“On ne peut pas faire un programme entièrement pur. L’objectif, c&amp;#39;est de retarder la gestion des effets le plus tard possible dans notre code.”&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph a1776f24-a7b0-40d9-b618-e824806150d4--&gt;
&lt;!-- begin paragraph 964f402b-49a5-481b-9a43-7ade62e27d9c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-964f402b-49a5-481b-9a43-7ade62e27d9c&quot;&gt;Pour être honnête, je n’avais pas compris comment faire avec cette phrase non plus… J’avais juste compris qu’il fallait essayer d’avoir le plus de fonctions pures puisqu’elles sont :&lt;/p&gt;
&lt;!-- end paragraph 964f402b-49a5-481b-9a43-7ade62e27d9c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;prévisibles (déterministes)&lt;/li&gt;&lt;li&gt;faciles à tester&lt;/li&gt;&lt;li&gt;faciles à réutiliser&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 d3f9c1c8-89d2-4703-b421-cd44ec5306ed--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d3f9c1c8-89d2-4703-b421-cd44ec5306ed&quot;&gt;L’IO monad&lt;/h3&gt;
&lt;!-- end heading_2 d3f9c1c8-89d2-4703-b421-cd44ec5306ed--&gt;
&lt;!-- begin paragraph 296bcf08-d0cb-48fd-8e03-11f76e5270ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-296bcf08-d0cb-48fd-8e03-11f76e5270ea&quot;&gt;J’ai compris ce que cela impliquait réellement avec cette vidéo présentant l’&lt;i&gt;IO monad&lt;/i&gt; :&lt;/p&gt;
&lt;!-- end paragraph 296bcf08-d0cb-48fd-8e03-11f76e5270ea--&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/sxudIMiOo68?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 13331989-a047-4123-a026-ac41311b6b13--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-13331989-a047-4123-a026-ac41311b6b13&quot;&gt;C’est l’origine de ZIO et d’autres bibliothèques à “effets” comme Cats Effect ou ScalaZ. Toutes ces librairies ne sont que des implémentations différentes de cette monade.&lt;/p&gt;
&lt;!-- end paragraph 13331989-a047-4123-a026-ac41311b6b13--&gt;
&lt;!-- begin paragraph adbed1a6-2741-487d-acf2-0d160b524d86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-adbed1a6-2741-487d-acf2-0d160b524d86&quot;&gt;Le nom est assez barbare, mais la logique derrière la monade est plus simple à appréhender : “&lt;b&gt;On n’exécute pas nos effets immédiatement&lt;/b&gt;, &lt;b&gt;on décrit notre programme&lt;/b&gt; &lt;b&gt;qu’on interprètera qu’à la fin de notre code.&lt;/b&gt;”&lt;/p&gt;
&lt;!-- end paragraph adbed1a6-2741-487d-acf2-0d160b524d86--&gt;
&lt;!-- begin paragraph f4009c56-957e-4bac-9d5d-9a4acba77830--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f4009c56-957e-4bac-9d5d-9a4acba77830&quot;&gt;Tout commence par cette déclaration :&lt;/p&gt;
&lt;!-- end paragraph f4009c56-957e-4bac-9d5d-9a4acba77830--&gt;
&lt;!-- begin code fbe4b2e5-2196-409e-96f5-1f1dd501f836--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fbe4b2e5-2196-409e-96f5-1f1dd501f836&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class IO[A](unsafeRun: () =&amp;gt; A) {
	def map[B](f: A =&amp;gt; B): IO[B] = ???

	def flatMap[B](f: A =&amp;gt; IO[B]): IO[B] = ???
}

val printHello = IO(() =&amp;gt; print(&amp;quot;hello&amp;quot;))
val printWorld = IO(() =&amp;gt; print(&amp;quot; world&amp;quot;))

printHello.unsafeRun() // Programme qui affiche que &amp;quot;hello&amp;quot;
printHello.flatMap(_ =&amp;gt; printWorld).unsafeRun() // Programme qui affiche &amp;quot;hello world&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fbe4b2e5-2196-409e-96f5-1f1dd501f836--&gt;
&lt;!-- begin paragraph bb70faa5-0a5a-439e-ab0c-41172a2ebde7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bb70faa5-0a5a-439e-ab0c-41172a2ebde7&quot;&gt;Ici, &lt;b&gt;printHello&lt;/b&gt; et &lt;b&gt;printWorld&lt;/b&gt; sont des valeurs qui décrivent un affichage console. Cela veut dire qu’au moment où on les déclare, rien n’est réellement lancé. On peut faire un lien avec les &lt;b&gt;transformations&lt;/b&gt; pour Spark, une transformation décrit seulement ce que les workers vont devoir faire, mais rien n’est concrètement lancé au niveau d’une transformation. Pour que cela se lance, il faut une &lt;b&gt;action&lt;/b&gt;. Ici notre action, c&amp;#39;est &lt;b&gt;unsafeRun&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph bb70faa5-0a5a-439e-ab0c-41172a2ebde7--&gt;
&lt;!-- begin heading_2 a91f1a01-4817-43fb-9366-12e7b33a3eb2--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-a91f1a01-4817-43fb-9366-12e7b33a3eb2&quot;&gt;ZIO, la monade divine&lt;/h3&gt;
&lt;!-- end heading_2 a91f1a01-4817-43fb-9366-12e7b33a3eb2--&gt;
&lt;!-- begin paragraph 4b7d74da-1189-4659-8d96-0ff5c7e12ca5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4b7d74da-1189-4659-8d96-0ff5c7e12ca5&quot;&gt;ZIO, comme on l’a dit auparavant, n’est pas la seule IO monad, mais elle se démarque des autres, car elle a transcendé son être pour devenir ce qu’on appelle une &lt;b&gt;god monad&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 4b7d74da-1189-4659-8d96-0ff5c7e12ca5--&gt;
&lt;!-- begin paragraph 4678bb4e-63ab-4685-a65f-bc9202c0600d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4678bb4e-63ab-4685-a65f-bc9202c0600d&quot;&gt;ZIO porte ce surnom parce qu&amp;#39;elle est à la fois :&lt;/p&gt;
&lt;!-- end paragraph 4678bb4e-63ab-4685-a65f-bc9202c0600d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Une IO monad&lt;/li&gt;&lt;li&gt;Une Either monad&lt;/li&gt;&lt;li&gt;Une State monad&lt;/li&gt;&lt;li&gt;Une Future monad&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 30ae938b-35d9-4be1-bdcc-a040c0cbcf91--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-30ae938b-35d9-4be1-bdcc-a040c0cbcf91&quot;&gt;Là où les autres ont parié sur la composition et sur l’utilisation massive du &lt;b&gt;tagless final&lt;/b&gt;, ZIO a préféré généraliser tous ces concepts dans un seul et même endroit, rendant son utilisation beaucoup plus simple et naturelle.&lt;/p&gt;
&lt;!-- end paragraph 30ae938b-35d9-4be1-bdcc-a040c0cbcf91--&gt;
&lt;!-- begin heading_1 4ed33e6d-371d-4d40-b8a5-3ad600b37159--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-4ed33e6d-371d-4d40-b8a5-3ad600b37159&quot;&gt;ZIO dans les grandes lignes&lt;/h2&gt;
&lt;!-- end heading_1 4ed33e6d-371d-4d40-b8a5-3ad600b37159--&gt;
&lt;!-- begin paragraph 2fda4335-27b4-4cab-bdb6-431a2e2a610b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2fda4335-27b4-4cab-bdb6-431a2e2a610b&quot;&gt;Pour gérer autant d’aspects différents, ZIO ne peut pas se contenter d’un type générique &lt;code&gt;A&lt;/code&gt; comme c’est le cas pour une monade &lt;b&gt;IO[A]&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 2fda4335-27b4-4cab-bdb6-431a2e2a610b--&gt;
&lt;!-- begin paragraph df103573-a9c0-432d-b429-050952856d5a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-df103573-a9c0-432d-b429-050952856d5a&quot;&gt;ZIO possède pas moins de trois types génériques et s’écrit donc &lt;b&gt;ZIO[R, E, A]&lt;/b&gt; :&lt;/p&gt;
&lt;!-- end paragraph df103573-a9c0-432d-b429-050952856d5a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le type &lt;code&gt;A&lt;/code&gt; décrit la valeur de retour de notre effet dans le cas nominal&lt;/li&gt;&lt;li&gt;Le type &lt;code&gt;E&lt;/code&gt; décrit la valeur de retour de notre effet en cas d’échec&lt;/li&gt;&lt;li&gt;Le type &lt;code&gt;R&lt;/code&gt; décrit les dépendances que notre fonction a besoin pour être exécutée&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph ddec1089-4d8b-4beb-b88c-4535021831e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddec1089-4d8b-4beb-b88c-4535021831e0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ddec1089-4d8b-4beb-b88c-4535021831e0--&gt;
&lt;!-- begin paragraph 17ce90a5-adaa-4532-b5b7-f5813cc9b05a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-17ce90a5-adaa-4532-b5b7-f5813cc9b05a&quot;&gt;ZIO utilise la hiérarchie des types en Scala (&lt;a href=&quot;https://docs.scala-lang.org/tour/unified-types.html&quot;&gt;https://docs.scala-lang.org/tour/unified-types.html&lt;/a&gt;) dans le cas où vous ne voulez pas de dépendances ou d’erreurs. &lt;/p&gt;
&lt;!-- end paragraph 17ce90a5-adaa-4532-b5b7-f5813cc9b05a--&gt;
&lt;!-- begin paragraph d5215aa9-ca9b-4967-9a4a-c550ea39c2cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d5215aa9-ca9b-4967-9a4a-c550ea39c2cc&quot;&gt;&lt;b&gt;Vous voulez décrire un effet qui n’a pas de dépendance ? &lt;/b&gt;Il suffit de créer un &lt;b&gt;ZIO[Any, E, A]&lt;/b&gt;. Parce que ne pas avoir besoin de dépendances signifie que notre fonction peut accepter n’importe quelle dépendance.&lt;/p&gt;
&lt;!-- end paragraph d5215aa9-ca9b-4967-9a4a-c550ea39c2cc--&gt;
&lt;!-- begin paragraph 46a4842d-b7e3-44e1-99e1-e1caf7127fbf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-46a4842d-b7e3-44e1-99e1-e1caf7127fbf&quot;&gt;&lt;b&gt;Vous voulez un effet qui n’échoue pas ?&lt;/b&gt; Il suffit de créer un &lt;b&gt;ZIO[R, Nothing, A]&lt;/b&gt;. Parce que ne pas avoir d’erreurs signifie que le type erreur ne peut pas être défini. Or, le type Nothing n’a pas de valeur associée.&lt;/p&gt;
&lt;!-- end paragraph 46a4842d-b7e3-44e1-99e1-e1caf7127fbf--&gt;
&lt;!-- begin heading_1 26b35cfa-b6ea-49bb-a35a-438bc7c3ea50--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-26b35cfa-b6ea-49bb-a35a-438bc7c3ea50&quot;&gt;Les aspects de ZIO qui vont vous faciliter la vie&lt;/h2&gt;
&lt;!-- end heading_1 26b35cfa-b6ea-49bb-a35a-438bc7c3ea50--&gt;
&lt;!-- begin paragraph d94d4975-340f-484c-a0f1-34d6a387d58a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d94d4975-340f-484c-a0f1-34d6a387d58a&quot;&gt;Comme nous l’avons expliqué auparavant, le datatype ZIO crée une description de nos effets qui ne sera interprétée qu’à la fin de notre programme. &lt;/p&gt;
&lt;!-- end paragraph d94d4975-340f-484c-a0f1-34d6a387d58a--&gt;
&lt;!-- begin callout 63d4d735-e2a8-41af-b148-fba2aba8dbc0--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-63d4d735-e2a8-41af-b148-fba2aba8dbc0&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;💡&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;On fait souvent l’analogie avec une recette de cuisine, et je trouve la comparaison très bonne. Plutôt que d’être la personne qui cuisine, avec ZIO, on est celui qui écrit la recette. On va ensuite passer cette recette au Runtime de ZIO qui va se charger de la réaliser.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout 63d4d735-e2a8-41af-b148-fba2aba8dbc0--&gt;
&lt;!-- begin paragraph fa0eb3f9-3f22-44aa-84bf-1463aa1b9332--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa0eb3f9-3f22-44aa-84bf-1463aa1b9332&quot;&gt;Ce qui est intéressant avec ce principe, c&amp;#39;est qu’&lt;b&gt;on peut très facilement augmenter notre effet et lui rajouter certaines capacités&lt;/b&gt; automatiquement et sans overhead.&lt;/p&gt;
&lt;!-- end paragraph fa0eb3f9-3f22-44aa-84bf-1463aa1b9332--&gt;
&lt;!-- begin heading_2 7ba1dc68-9317-467c-ba8e-d6d9f8e7dc8c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-7ba1dc68-9317-467c-ba8e-d6d9f8e7dc8c&quot;&gt;1. La gestion explicite des erreurs&lt;/h3&gt;
&lt;!-- end heading_2 7ba1dc68-9317-467c-ba8e-d6d9f8e7dc8c--&gt;
&lt;!-- begin paragraph a274e4ca-05d1-4696-b149-ea3be46cbf42--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a274e4ca-05d1-4696-b149-ea3be46cbf42&quot;&gt;ZIO rend explicite nos erreurs. Lorsqu’on manipule un effet, on sait exactement si notre effet peut rater et si oui, &lt;b&gt;qu’elle est l’erreur en question que l’on va devoir gérer&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph a274e4ca-05d1-4696-b149-ea3be46cbf42--&gt;
&lt;!-- begin paragraph 32f041fe-167a-452b-8428-6892b633eee7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-32f041fe-167a-452b-8428-6892b633eee7&quot;&gt;Cela nous permet de mieux appréhender les différents cas non prévus de nos applications et de &lt;b&gt;réduire considérablement le nombre de bugs en production&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 32f041fe-167a-452b-8428-6892b633eee7--&gt;
&lt;!-- begin paragraph 67f5e252-7f9e-4c5d-bd9b-69f54f7c482a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67f5e252-7f9e-4c5d-bd9b-69f54f7c482a&quot;&gt;ZIO vient avec une pléthore de fonctionnalités pour gérer les erreurs :&lt;/p&gt;
&lt;!-- end paragraph 67f5e252-7f9e-4c5d-bd9b-69f54f7c482a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;vous pouvez décider de les ignorer&lt;/li&gt;&lt;li&gt;vous pouvez décider de les gérer&lt;/li&gt;&lt;li&gt;vous pouvez décider de relancer votre fonction jusqu’à ce qu’elle réussisse&lt;/li&gt;&lt;li&gt;vous pouvez propager les erreurs&lt;/li&gt;&lt;li&gt;vous pouvez transformer les erreurs&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f58e96c7-0935-48a7-afe6-548e95503b01--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f58e96c7-0935-48a7-afe6-548e95503b01&quot;&gt;Voici, par exemple, comment gérer une erreur :&lt;/p&gt;
&lt;!-- end paragraph f58e96c7-0935-48a7-afe6-548e95503b01--&gt;
&lt;!-- begin code 87bc3538-6ed9-4582-a061-0f48f0655e41--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-87bc3538-6ed9-4582-a061-0f48f0655e41&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._

val getUser(id: Int): ZIO[Database, UserNotFound, User] = ???

val printUser: ZIO[Database, Nothing, Unit] = getUser(10).fold(
	failure = _    =&amp;gt; Console.printLine(&amp;quot;L&amp;#39;utilisateur 10 n&amp;#39;existe pas.&amp;quot;),
	success = user =&amp;gt; Console.printLine(s&amp;quot;L&amp;#39;utilisateur 10 est ${user.name}.&amp;quot;)
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 87bc3538-6ed9-4582-a061-0f48f0655e41--&gt;
&lt;!-- begin paragraph 0335cc2d-ca77-487e-8bdd-eaba174cd214--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0335cc2d-ca77-487e-8bdd-eaba174cd214&quot;&gt;Ici, on a un nouvel effet &lt;b&gt;printUser&lt;/b&gt; qui récupère l’utilisateur numéro 10, puis gère le cas où l&amp;#39;on récupère l’utilisateur de la base de données et le cas où l&amp;#39;on ne le récupère pas :&lt;/p&gt;
&lt;!-- end paragraph 0335cc2d-ca77-487e-8bdd-eaba174cd214--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Si on récupère l’utilisateur, on affiche : “L&amp;#39;utilisateur 10 est John Doe.”.&lt;/li&gt;&lt;li&gt;Si on a une erreur, on affiche : “L&amp;#39;utilisateur 10 n&amp;#39;existe pas.”.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph d3cec51e-c996-4455-9061-695b0537cb1b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3cec51e-c996-4455-9061-695b0537cb1b&quot;&gt;À noter, qu’ici, notre nouvel effet n’a plus d’erreurs (d’où le type Nothing) puisqu’on a géré le cas d’erreur via la fonction fold.&lt;/p&gt;
&lt;!-- end paragraph d3cec51e-c996-4455-9061-695b0537cb1b--&gt;
&lt;!-- begin heading_2 813c6065-dcfe-4b96-bca5-ccf0b5350328--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-813c6065-dcfe-4b96-bca5-ccf0b5350328&quot;&gt;2. La programmation concurrente&lt;/h3&gt;
&lt;!-- end heading_2 813c6065-dcfe-4b96-bca5-ccf0b5350328--&gt;
&lt;!-- begin paragraph 70d59fb5-8436-48c6-a708-bd9f272b4d7e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-70d59fb5-8436-48c6-a708-bd9f272b4d7e&quot;&gt;On peut, par exemple, lancer une liste d’effet en parallèle sans avoir à s’en soucier plus que ça. &lt;/p&gt;
&lt;!-- end paragraph 70d59fb5-8436-48c6-a708-bd9f272b4d7e--&gt;
&lt;!-- begin paragraph cbab348a-04e5-47bd-bcb7-6a72526f89a8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cbab348a-04e5-47bd-bcb7-6a72526f89a8&quot;&gt;Ces effets seront lancés dans des fibers (en quelque sorte, des greens threads gérés par ZIO). L’avantage des fibers par rapport aux threads de notre ordinateur, c’est qu’on peut en lancer des dizaines de milliers, voire des millions avec un impact faible sur les ressources de la machine ! De notre côté, on ne s’en soucie pas trop. On ne s’occupe que de la logique de notre application.&lt;/p&gt;
&lt;!-- end paragraph cbab348a-04e5-47bd-bcb7-6a72526f89a8--&gt;
&lt;!-- begin paragraph 9c592e7a-ad91-424c-8949-79176b7935dd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c592e7a-ad91-424c-8949-79176b7935dd&quot;&gt;Voici un exemple dans lequel on récupère une liste d’utilisateurs depuis une base de données de manière concurrente :&lt;/p&gt;
&lt;!-- end paragraph 9c592e7a-ad91-424c-8949-79176b7935dd--&gt;
&lt;!-- begin code f3f8f483-e6dd-4799-806d-beb2df61d2a1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f3f8f483-e6dd-4799-806d-beb2df61d2a1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._

val getUser(id: Int): ZIO[Database, UserNotFound, User] = ???

// Récupère les utilisateurs de 10 à 20
val getUsers: ZIO[Database, UserNotFound, List[User]] = 
	ZIO.foreachPar((10 to 20).toList)(getUser)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f3f8f483-e6dd-4799-806d-beb2df61d2a1--&gt;
&lt;!-- begin paragraph 5ac5a846-21d0-46ad-ae9d-ec8ecc5ddfda--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ac5a846-21d0-46ad-ae9d-ec8ecc5ddfda&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5ac5a846-21d0-46ad-ae9d-ec8ecc5ddfda--&gt;
&lt;!-- begin paragraph 88dacab2-b784-4613-bbdd-e3dcdaee0fd6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-88dacab2-b784-4613-bbdd-e3dcdaee0fd6&quot;&gt;On peut bien évidemment aller beaucoup plus loin. ZIO est rempli de structures concurrentes à la fois pratiques et suffisamment sûres pour vous aider à mener à bien vos projets. Pour plus d’informations : &lt;a href=&quot;https://zio.dev/overview/overview_basic_concurrency&quot;&gt;https://zio.dev/overview/overview_basic_concurrency&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 88dacab2-b784-4613-bbdd-e3dcdaee0fd6--&gt;
&lt;!-- begin heading_2 ab1008c8-1025-4eb1-b2e8-279a5b10b1c7--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ab1008c8-1025-4eb1-b2e8-279a5b10b1c7&quot;&gt;3. L’injection de dépendances&lt;/h3&gt;
&lt;!-- end heading_2 ab1008c8-1025-4eb1-b2e8-279a5b10b1c7--&gt;
&lt;!-- begin paragraph 43f0d936-e05a-4ced-a737-8edab6ff517a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-43f0d936-e05a-4ced-a737-8edab6ff517a&quot;&gt;Un autre point important de ZIO, c’est le système de &lt;b&gt;Layers&lt;/b&gt;. Cette feature prend tout son sens depuis la version 2.0 de ZIO, car elle est devenue plus confortable à utiliser.&lt;/p&gt;
&lt;!-- end paragraph 43f0d936-e05a-4ced-a737-8edab6ff517a--&gt;
&lt;!-- begin paragraph db4a626a-2c0b-461d-bb27-7e3831cc7c1a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-db4a626a-2c0b-461d-bb27-7e3831cc7c1a&quot;&gt;Si on reprend l’analogie de la recette de cuisine, cette dernière a souvent besoin d’ustensiles pour fonctionner qu’il va falloir préparer en amont pour la réaliser (eg. le four à faire chauffer à 200ºC et quel sera le temps de la cuisson). Pour les Layers, c’est exactement pareil. Votre programme va avoir besoin de dépendances pour fonctionner tout au long de sa durée de vie. Cela peut être :&lt;/p&gt;
&lt;!-- end paragraph db4a626a-2c0b-461d-bb27-7e3831cc7c1a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Une configuration du programme&lt;/li&gt;&lt;li&gt;Un service pour communiquer avec une API tierce&lt;/li&gt;&lt;li&gt;Une connexion à une base de données pour pouvoir la requêter&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 2f9c3007-e383-4137-abe0-618e73c6eb59--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2f9c3007-e383-4137-abe0-618e73c6eb59&quot;&gt;Tout ceci peut être fait avec des Layers et cela va grandement vous améliorer la vie. Ce sujet mérite un article de blog à part entière. Si vous voulez plus d’informations, je vous conseille : &lt;a href=&quot;https://zio.dev/references/contextual/zlayer&quot;&gt;https://zio.dev/references/contextual/zlayer&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 2f9c3007-e383-4137-abe0-618e73c6eb59--&gt;
&lt;!-- begin heading_2 61f80df2-942b-408a-b93a-27cb45579167--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-61f80df2-942b-408a-b93a-27cb45579167&quot;&gt;4. Le scheduling&lt;/h3&gt;
&lt;!-- end heading_2 61f80df2-942b-408a-b93a-27cb45579167--&gt;
&lt;!-- begin paragraph e7ce5680-7522-49b4-ad9e-acb02189a52f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e7ce5680-7522-49b4-ad9e-acb02189a52f&quot;&gt;Le dernier point qui est fréquemment mis en second plan, mais que je trouve très important, c’est la possibilité de planifier nos effets.&lt;/p&gt;
&lt;!-- end paragraph e7ce5680-7522-49b4-ad9e-acb02189a52f--&gt;
&lt;!-- begin paragraph 6e7b0dc9-b4ea-4f0d-9098-db7eebc30280--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e7b0dc9-b4ea-4f0d-9098-db7eebc30280&quot;&gt;Avec ZIO, on peut très bien :&lt;/p&gt;
&lt;!-- end paragraph 6e7b0dc9-b4ea-4f0d-9098-db7eebc30280--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;répéter un effet 10 fois&lt;/li&gt;&lt;li&gt;répéter un effet tous les mardis à 10 heures du matin&lt;/li&gt;&lt;li&gt;appliquer une stratégie pour que l’effet se relance en cas d’erreur avec entre chaque essai un délai exponentiel&lt;/li&gt;&lt;li&gt;relancer la fonction jusqu’à ce qu’elle réussisse et compter le nombre de fois qu’il a fallu pour qu’elle réussisse&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph a1e0062e-bd8b-4c7d-bcf0-81ab6131b376--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a1e0062e-bd8b-4c7d-bcf0-81ab6131b376&quot;&gt;Cette feature est très puissante et permet de remplacer, dans certains cas, des outils d’orchestration un peu trop overkill.&lt;/p&gt;
&lt;!-- end paragraph a1e0062e-bd8b-4c7d-bcf0-81ab6131b376--&gt;
&lt;!-- begin paragraph ef0c6909-c18b-46ad-a218-ae3aa19a993e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ef0c6909-c18b-46ad-a218-ae3aa19a993e&quot;&gt;Prenons le cas d’un programme en Scala qui s’assure que &lt;a href=&quot;http://github.com/&quot;&gt;github.com&lt;/a&gt; est up :&lt;/p&gt;
&lt;!-- end paragraph ef0c6909-c18b-46ad-a218-ae3aa19a993e--&gt;
&lt;!-- begin code 71aed72e-d279-4506-826f-720f0aa8d767--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-71aed72e-d279-4506-826f-720f0aa8d767&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._

def pingGithub: ZIO[Any, ConnectionError, Unit] = ???

val app = 
	pingGithub
		.retry(Schedule.exponential(1.second) &amp;amp;&amp;amp; Schedule.recurs(5))
		.schedule(Schedule.spaced(1.minute))
		.orElse(Console.printLine(&amp;quot;Github is unreachable&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 71aed72e-d279-4506-826f-720f0aa8d767--&gt;
&lt;!-- begin paragraph 7ed4aeac-fd2c-43ec-b5ef-18736e8d1412--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7ed4aeac-fd2c-43ec-b5ef-18736e8d1412&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7ed4aeac-fd2c-43ec-b5ef-18736e8d1412--&gt;
&lt;!-- begin paragraph 23fc9430-126d-4080-9997-3d71b0706722--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-23fc9430-126d-4080-9997-3d71b0706722&quot;&gt;Ici l’effet “app”, quand il sera exécuté par le Runtime de ZIO, va ping Github en suivant cette logique :&lt;/p&gt;
&lt;!-- end paragraph 23fc9430-126d-4080-9997-3d71b0706722--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Il va se lancer toutes les minutes sans interruption (&lt;b&gt;schedule&lt;/b&gt;).&lt;/li&gt;&lt;li&gt;Il va se relancer jusqu’à 5 fois si GitHub ne répond pas (&lt;b&gt;retry&lt;/b&gt;), d’abord avec 1 seconde de délais, puis avec un délai exponentiel.&lt;/li&gt;&lt;li&gt;Si au bout de 5 fois Github ne répond toujours pas alors, on notifie l’utilisateur (&lt;b&gt;orElse&lt;/b&gt;) avec un message dans la console.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph ef8e9a28-6303-4525-920b-5aeda7473344--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ef8e9a28-6303-4525-920b-5aeda7473344&quot;&gt;Personnellement, la première fois que j’ai écrit ce genre de code, j’ai été impressionné ! En si peu de lignes, on peut écrire un code entièrement fonctionnel, qui gère parfaitement les mauvais cas et qui en plus vient avec un DSL plaisant à lire.&lt;/p&gt;
&lt;!-- end paragraph ef8e9a28-6303-4525-920b-5aeda7473344--&gt;
&lt;!-- begin heading_1 9d39de7c-2e5a-4d33-9aab-169a5bdeaaed--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9d39de7c-2e5a-4d33-9aab-169a5bdeaaed&quot;&gt;À vous de jouer ! &lt;/h2&gt;
&lt;!-- end heading_1 9d39de7c-2e5a-4d33-9aab-169a5bdeaaed--&gt;
&lt;!-- begin paragraph a209d6ac-2b62-44f4-8394-128928dc84a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a209d6ac-2b62-44f4-8394-128928dc84a9&quot;&gt;ZIO c’est beaucoup plus que ce que je vous ai présenté. Il y a d’autres fonctionnalités qui sont potentiellement plus pratiques dans vos cas d’usage, comme la gestion des streams, les métriques ou la gestion de la configuration de vos services... Il définit une nouvelle manière d’écrire du code et vous accompagne tout le long du développement de votre application, sans pour autant être clivant comme peuvent l’être les grands frameworks que sont Spring ou Play par exemple.&lt;/p&gt;
&lt;!-- end paragraph a209d6ac-2b62-44f4-8394-128928dc84a9--&gt;
&lt;!-- begin paragraph 27dff4dd-c186-4403-a886-94c5f79ca4e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27dff4dd-c186-4403-a886-94c5f79ca4e7&quot;&gt;Si vous avez besoin de plus de ressources, je vous conseille la documentation de ZIO &lt;a href=&quot;https://zio.dev/&quot;&gt;https://zio.dev/&lt;/a&gt; ou encore la chaine YouTube de Ziverge &lt;a href=&quot;https://www.youtube.com/channel/UCeIg_PnAoyd1w6y8BelLdiQ&quot;&gt;https://www.youtube.com/channel/UCeIg_PnAoyd1w6y8BelLdiQ&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 27dff4dd-c186-4403-a886-94c5f79ca4e7--&gt;
&lt;!-- begin paragraph ed9c1dba-939e-4a77-898b-bab63472e06f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ed9c1dba-939e-4a77-898b-bab63472e06f&quot;&gt;Vous verrez, l’essayer c’est l’adopter ! &lt;/p&gt;
&lt;!-- end paragraph ed9c1dba-939e-4a77-898b-bab63472e06f--&gt;
&lt;!-- begin paragraph c784efb9-bcb5-48c9-84d5-7743782adac7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c784efb9-bcb5-48c9-84d5-7743782adac7&quot;&gt;Je ne suis pas responsable si vous ne pouvez plus vous en passer par la suite ;)&lt;/p&gt;
&lt;!-- end paragraph c784efb9-bcb5-48c9-84d5-7743782adac7--&gt;
&lt;!-- begin paragraph 6f141773-0ea8-47c0-8681-9b0acda50d5c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6f141773-0ea8-47c0-8681-9b0acda50d5c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6f141773-0ea8-47c0-8681-9b0acda50d5c--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:4aea8798720149c796ff5bc7f90fcb9a</id>
    <title>Log4Shell + SBT</title>
    <updated>2021-12-11T19:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/Log4Shell.html"/>
    <!--summary CVE-2021-44228-->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="SBT"></category>    <content type="html">
&lt;!-- begin paragraph 4459209d-386e-4882-ab87-01cb3336a791--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4459209d-386e-4882-ab87-01cb3336a791&quot;&gt;Si par hasard, on a un doute sur log4j (&lt;a href=&quot;https://www.cve.org/CVERecord?id=CVE-2021-44228&quot;&gt;&lt;b&gt;CVE-2021-44228&lt;/b&gt;&lt;/a&gt;&lt;b&gt;)&lt;/b&gt;, et que l&amp;#39;on utilise Play Framework (2)/sbt, on peut rapidement savoir si on est dans la semoule (une version &amp;lt; 2.15 ou pire inférieur à 2.10.0) :&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph 4459209d-386e-4882-ab87-01cb3336a791--&gt;
&lt;!-- begin code 255fde37-6066-4125-b2eb-e53ed2cf75e4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-255fde37-6066-4125-b2eb-e53ed2cf75e4&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;sbt dependencyTree | grep log4j #dependencyTree est de base dans SBT maintenant&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 255fde37-6066-4125-b2eb-e53ed2cf75e4--&gt;
&lt;!-- begin paragraph f9aa6c43-b80d-4f5e-b14d-6fa87ccc2ec3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9aa6c43-b80d-4f5e-b14d-6fa87ccc2ec3&quot;&gt;Pour en savoir un peu plus, on peut regarder avec l&amp;#39;analyse de dépendances depuis un terminal (sbt versions &amp;gt;= 0.13.10)&lt;/p&gt;
&lt;!-- end paragraph f9aa6c43-b80d-4f5e-b14d-6fa87ccc2ec3--&gt;
&lt;!-- begin code 6f45be5a-7529-40b9-83f2-73978d80b2c9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6f45be5a-7529-40b9-83f2-73978d80b2c9&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;echo &amp;#39;addSbtPlugin(&amp;quot;net.virtual-void&amp;quot; % &amp;quot;sbt-dependency-graph&amp;quot; % &amp;quot;0.10.0-RC1&amp;quot;)&amp;#39; &amp;gt;&amp;gt; project/plugins.sbt
sbt &amp;quot;whatDependsOn org.apache.logging.log4j log4j-core&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6f45be5a-7529-40b9-83f2-73978d80b2c9--&gt;
&lt;!-- begin paragraph 351535dc-d518-4686-b10d-92de378ab457--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-351535dc-d518-4686-b10d-92de378ab457&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 351535dc-d518-4686-b10d-92de378ab457--&gt;
&lt;!-- begin divider 1e1808cb-0f3e-4746-b38a-a1d2e07ee7a2--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-1e1808cb-0f3e-4746-b38a-a1d2e07ee7a2&quot;&gt;
&lt;!-- end divider 1e1808cb-0f3e-4746-b38a-a1d2e07ee7a2--&gt;
&lt;!-- begin paragraph a77b57b3-ef11-4882-971d-e3a4ded040a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a77b57b3-ef11-4882-971d-e3a4ded040a0&quot;&gt;Les fixs, le truc que j&amp;#39;ai trouvé le plus simple pour passer le week-end &amp;quot;normalement&amp;quot;, on ajoute ça aux lanceurs java du côté de la prod :&lt;/p&gt;
&lt;!-- end paragraph a77b57b3-ef11-4882-971d-e3a4ded040a0--&gt;
&lt;!-- begin code ddad7f51-7d04-468c-a8c8-9c4fef1afe96--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ddad7f51-7d04-468c-a8c8-9c4fef1afe96&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;-Dlog4j2.formatMsgNoLookups=True&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ddad7f51-7d04-468c-a8c8-9c4fef1afe96--&gt;
&lt;!-- begin paragraph 89d4be36-c518-42a1-83b3-dc3ac4832511--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89d4be36-c518-42a1-83b3-dc3ac4832511&quot;&gt;On bump la dépendance à 2.15.0 (si possible).&lt;/p&gt;
&lt;!-- end paragraph 89d4be36-c518-42a1-83b3-dc3ac4832511--&gt;
&lt;!-- begin code 13f673e9-9ddb-42ca-b2bb-2cbdc862a450--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-13f673e9-9ddb-42ca-b2bb-2cbdc862a450&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies += &amp;quot;org.apache.logging.log4j&amp;quot; % &amp;quot;log4j-core&amp;quot; % &amp;quot;2.15.0&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 13f673e9-9ddb-42ca-b2bb-2cbdc862a450--&gt;
&lt;!-- begin divider c192bcee-6783-4aa1-b7af-387af09337bc--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-c192bcee-6783-4aa1-b7af-387af09337bc&quot;&gt;
&lt;!-- end divider c192bcee-6783-4aa1-b7af-387af09337bc--&gt;
&lt;!-- begin paragraph ed5e8c09-967f-4503-a316-7b5d88e16b82--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ed5e8c09-967f-4503-a316-7b5d88e16b82&quot;&gt;Bon week-end !&lt;/p&gt;
&lt;!-- end paragraph ed5e8c09-967f-4503-a316-7b5d88e16b82--&gt;
&lt;!-- begin paragraph 794cbf4b-b107-4f63-bafe-b72d58ef0470--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-794cbf4b-b107-4f63-bafe-b72d58ef0470&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 794cbf4b-b107-4f63-bafe-b72d58ef0470--&gt;
&lt;!-- begin paragraph 12dd7291-dd2a-475c-b621-2d1f6f47d811--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-12dd7291-dd2a-475c-b621-2d1f6f47d811&quot;&gt;Plus d’infos :&lt;/p&gt;
&lt;!-- end paragraph 12dd7291-dd2a-475c-b621-2d1f6f47d811--&gt;
&lt;!-- begin bookmark 16c8a256-fa51-49b8-8a02-1a2678bcaa8c--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-16c8a256-fa51-49b8-8a02-1a2678bcaa8c&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://logging.apache.org/security.html&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Apache Logging Services&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;/blog/assets/favicon_02169a3f4fbfbe0d3be18310ff5b841d.png&quot;&gt; https://logging.apache.org/security.html&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 16c8a256-fa51-49b8-8a02-1a2678bcaa8c--&gt;
&lt;!-- begin bookmark a074f796-c1e2-4dc1-8523-9eb70395d6ae--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-a074f796-c1e2-4dc1-8523-9eb70395d6ae&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://www.cve.org/CVERecord?id=CVE-2021-44228&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://www.cve.org/CVERecord?id=CVE-2021-44228&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark a074f796-c1e2-4dc1-8523-9eb70395d6ae--&gt;
&lt;!-- begin paragraph b208747e-108d-41ea-976e-d031de28cf47--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b208747e-108d-41ea-976e-d031de28cf47&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b208747e-108d-41ea-976e-d031de28cf47--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:bfb6f8d1029f436bb5a0094bf6ee84d2</id>
    <title>Project LOOM: Les Continuations &amp; les Thread virtuels</title>
    <updated>2021-09-02T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/project-loom-les-continuations-et-les-thread-virtuels.html"/>
    <!--summary Ça vous dirait de diviser par deux le coût en mémoire de vos programmes concurrents ? Dans cet article on va étudier le projet Loom qui introduit un modèle de concurrence léger et facile d&#039;usage-->    <author>
      <name>Bastien Guihard</name>
      <uri>https://univalence.io/blog/auteurs/bastien-guihard.html</uri>
    </author>        <category term="Java"></category>    <category term="loom"></category>    <category term="Programmation concurrente"></category>    <content type="html">
&lt;!-- begin heading_2 fcd70b24-a47a-4418-baff-d7e2d68790e6--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-fcd70b24-a47a-4418-baff-d7e2d68790e6&quot;&gt;Introduction&lt;/h3&gt;
&lt;!-- end heading_2 fcd70b24-a47a-4418-baff-d7e2d68790e6--&gt;
&lt;!-- begin paragraph aeb3cec8-8f75-4756-b8c3-ad7b075b1ad1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aeb3cec8-8f75-4756-b8c3-ad7b075b1ad1&quot;&gt;Dans cet article nous allons parler du projet Loom.&lt;/p&gt;
&lt;!-- end paragraph aeb3cec8-8f75-4756-b8c3-ad7b075b1ad1--&gt;
&lt;!-- begin paragraph 77f4d5d3-f580-472c-a02b-9acf70ab9c10--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77f4d5d3-f580-472c-a02b-9acf70ab9c10&quot;&gt;Le projet Loom est une version bêta de l&amp;#39;OpenJDK qui vise à introduire un modèle de concurrence léger à Java. &lt;/p&gt;
&lt;!-- end paragraph 77f4d5d3-f580-472c-a02b-9acf70ab9c10--&gt;
&lt;!-- begin paragraph d913b4f3-4f29-4aae-bc2d-1ee4edd85d40--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d913b4f3-4f29-4aae-bc2d-1ee4edd85d40&quot;&gt;En effet, pas mal de langages proposent des implantations plus ou moins performantes de modèles qui facilitent les exécutions concurrentes.&lt;/p&gt;
&lt;!-- end paragraph d913b4f3-4f29-4aae-bc2d-1ee4edd85d40--&gt;
&lt;!-- begin paragraph 89c5e649-3923-4723-b66c-7c4fa60af63e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89c5e649-3923-4723-b66c-7c4fa60af63e&quot;&gt;Par exemple, Clojure dispose d&amp;#39;un modèle CSP-like qui permet d&amp;#39;envoyer des processus dans un thread pool relativement aisément. Go dispose de ses fameuses go-routines, inspirées des coroutines que nous retrouvons dans Kotlin. Scala propose plusieurs modèles d&amp;#39;exécution comme les acteurs et les fibers (coroutines). L&amp;#39;approche Async/Await de JavaScript…&lt;/p&gt;
&lt;!-- end paragraph 89c5e649-3923-4723-b66c-7c4fa60af63e--&gt;
&lt;!-- begin paragraph 27d7942e-0641-439c-a8c9-3fd2b1462929--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27d7942e-0641-439c-a8c9-3fd2b1462929&quot;&gt;Loom introduit deux features principales qui sont les suivantes :&lt;/p&gt;
&lt;!-- end paragraph 27d7942e-0641-439c-a8c9-3fd2b1462929--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Les Continuations :&lt;/b&gt;
&lt;!-- begin paragraph 162f8318-8ec2-4fc0-8e97-d36e85bfd9b7--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-162f8318-8ec2-4fc0-8e97-d36e85bfd9b7&quot;&gt;Les continuations sont un ensemble de fonctions capables d&amp;#39;être interrompues et stockées sur le tas puis reprises plus tard dans l’exécution du programme.&lt;/p&gt;
&lt;!-- end paragraph 162f8318-8ec2-4fc0-8e97-d36e85bfd9b7--&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Les Fibers / Threads virtuels (terme privilégié) :&lt;/b&gt;
&lt;!-- begin paragraph 3d85451e-c020-4962-aef2-e8807fc5c0d4--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-3d85451e-c020-4962-aef2-e8807fc5c0d4&quot;&gt;Les threads virtuels sont similaires aux coroutines de Kotlin ou au fameux Async/Await de JavaScript, à l&amp;#39;exception près que les fibers sont gérées directement par la VM et pas par le compilateur.&lt;/p&gt;
&lt;!-- end paragraph 3d85451e-c020-4962-aef2-e8807fc5c0d4--&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 0db5cbd1-053c-4528-a087-6f15be8d378d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-0db5cbd1-053c-4528-a087-6f15be8d378d&quot;&gt;Intérêt et cas d&amp;#39;usage&lt;/h3&gt;
&lt;!-- end heading_2 0db5cbd1-053c-4528-a087-6f15be8d378d--&gt;
&lt;!-- begin paragraph 26dcfc12-5b35-4598-9ba8-ead7fe697cc7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26dcfc12-5b35-4598-9ba8-ead7fe697cc7&quot;&gt;Pourquoi porter son attention sur ce modèle de concurrence ?&lt;/p&gt;
&lt;!-- end paragraph 26dcfc12-5b35-4598-9ba8-ead7fe697cc7--&gt;
&lt;!-- begin heading_3 8cd9c9e9-98b6-4531-8cc8-b9aa4a5d5b60--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-8cd9c9e9-98b6-4531-8cc8-b9aa4a5d5b60&quot;&gt;Meilleures performances&lt;/h4&gt;
&lt;!-- end heading_3 8cd9c9e9-98b6-4531-8cc8-b9aa4a5d5b60--&gt;
&lt;!-- begin paragraph cd481ec3-f157-451c-adfa-d210e4d0b442--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd481ec3-f157-451c-adfa-d210e4d0b442&quot;&gt;L&amp;#39;idée derrière les Threads virtuels est juste d&amp;#39;avoir un thread dont la création et le blocage sont peu couteux.&lt;/p&gt;
&lt;!-- end paragraph cd481ec3-f157-451c-adfa-d210e4d0b442--&gt;
&lt;!-- begin paragraph 19fe5c2d-4e64-4b75-b216-b5713cee6183--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-19fe5c2d-4e64-4b75-b216-b5713cee6183&quot;&gt;Pour expliquer ça, il faut comprendre deux-trois choses :&lt;/p&gt;
&lt;!-- end paragraph 19fe5c2d-4e64-4b75-b216-b5713cee6183--&gt;
&lt;!-- begin paragraph 644c964e-25b4-4fe1-a29e-7ce356ae33ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-644c964e-25b4-4fe1-a29e-7ce356ae33ea&quot;&gt;&lt;code&gt;java.lang.Thread&lt;/code&gt; est un simple wrapper sur des threads machine. Les threads machines sont assez lourds, puisqu&amp;#39;ils nécessitent : &lt;/p&gt;
&lt;!-- end paragraph 644c964e-25b4-4fe1-a29e-7ce356ae33ea--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Un support dans un maximum de langages.&lt;/li&gt;&lt;li&gt;D’avoir la possibilité d’arrêter et de reprendre l’exécution d&amp;#39;un calcul, &lt;b&gt;donc de préserver son état (pointeur, données locales)&lt;/b&gt;.&lt;/li&gt;&lt;li&gt;D’allouer une pile suffisamment grande pour pouvoir convenir à tous les langages, puisqu’un thread machine ne sait pas comment chaque langage gère la mémoire.&lt;/li&gt;&lt;li&gt;Enfin, ils doivent assigner les exécutions aux CPU disponibles.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph b3deec04-bed8-4375-89c7-f20f9f6a17a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3deec04-bed8-4375-89c7-f20f9f6a17a0&quot;&gt;Les threads virtuels diffèrent d&amp;#39;une manière assez chouette : tout le contrôle des exécutions (interrompre, reprendre un calcul…) est géré par... un objet Java !&lt;/p&gt;
&lt;!-- end paragraph b3deec04-bed8-4375-89c7-f20f9f6a17a0--&gt;
&lt;!-- begin paragraph 9600f683-93f4-42c9-ad59-4e636febe59f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9600f683-93f4-42c9-ad59-4e636febe59f&quot;&gt;Les objets Java sont mieux taillés pour modéliser l’exécution d&amp;#39;un programme Java efficacement.&lt;/p&gt;
&lt;!-- end paragraph 9600f683-93f4-42c9-ad59-4e636febe59f--&gt;
&lt;!-- begin paragraph b4dc818c-7bf1-4586-8612-10a1650f29d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b4dc818c-7bf1-4586-8612-10a1650f29d8&quot;&gt;Un autre avantage niveau performance est que l&amp;#39;on peut choisir soi-même le scheduler le mieux adapté à notre besoin (par défaut, c’est un&lt;a href=&quot;http://tutorials.jenkov.com/java-util-concurrent/java-fork-and-join-forkjoinpool.html&quot;&gt; work-stealing-fork-join-pool&lt;/a&gt;).&lt;/p&gt;
&lt;!-- end paragraph b4dc818c-7bf1-4586-8612-10a1650f29d8--&gt;
&lt;!-- begin paragraph 2e255151-5c26-49ad-ab80-b9b4d8959ecd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2e255151-5c26-49ad-ab80-b9b4d8959ecd&quot;&gt;En gros, la meilleure aisance du runtime Java avec Java par rapport aux threads machine permet de faire baisser le coût du spawning de thread drastiquement.&lt;/p&gt;
&lt;!-- end paragraph 2e255151-5c26-49ad-ab80-b9b4d8959ecd--&gt;
&lt;!-- begin heading_3 7878e089-dd41-4e45-9132-ccc9c2038239--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-7878e089-dd41-4e45-9132-ccc9c2038239&quot;&gt;Simplicité d&amp;#39;écriture de programmes concurrents&lt;/h4&gt;
&lt;!-- end heading_3 7878e089-dd41-4e45-9132-ccc9c2038239--&gt;
&lt;!-- begin paragraph 97c1c8b3-5b43-43df-8cc0-8360a6bd16f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-97c1c8b3-5b43-43df-8cc0-8360a6bd16f3&quot;&gt;Un des problèmes lié au fait d&amp;#39;utiliser des threads machines est que cette pratique permet tout au plus de spawner quelques milliers de threads simultanément actifs, bien loin des besoins réels d&amp;#39;un serveur Web supportant — par exemple — plusieurs millions de connexions concurrentes.&lt;/p&gt;
&lt;!-- end paragraph 97c1c8b3-5b43-43df-8cc0-8360a6bd16f3--&gt;
&lt;!-- begin paragraph fa01ac7d-fcbc-4ba7-ab53-94d11c680c7d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa01ac7d-fcbc-4ba7-ab53-94d11c680c7d&quot;&gt;On est alors obligé d&amp;#39;avoir recours à des stratagèmes compliqués, comme le pooling qui consiste à mutualiser plusieurs threads pour effectuer des tâches simultanément. Mais, aucun thread pool ne saurait représenter l&amp;#39;état complet du programme.&lt;/p&gt;
&lt;!-- end paragraph fa01ac7d-fcbc-4ba7-ab53-94d11c680c7d--&gt;
&lt;!-- begin paragraph d8db7a31-9c78-40c0-bb34-b7374aa7448a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d8db7a31-9c78-40c0-bb34-b7374aa7448a&quot;&gt;L&amp;#39;avantage des threads virtuels est que la JVM peut en spawner… des masses ; plusieurs millions simultanément sans problèmes.&lt;/p&gt;
&lt;!-- end paragraph d8db7a31-9c78-40c0-bb34-b7374aa7448a--&gt;
&lt;!-- begin paragraph e050c1df-623e-4fb3-92d0-45babe0b7412--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e050c1df-623e-4fb3-92d0-45babe0b7412&quot;&gt;Dès lors, plus besoin d&amp;#39;architectures compliquées. Pour chaque traitement, on spawn un thread virtuel un point, c’est tout.&lt;/p&gt;
&lt;!-- end paragraph e050c1df-623e-4fb3-92d0-45babe0b7412--&gt;
&lt;!-- begin heading_2 59813bb3-c428-4152-a070-6f2a951efd58--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-59813bb3-c428-4152-a070-6f2a951efd58&quot;&gt;Prérequis&lt;/h3&gt;
&lt;!-- end heading_2 59813bb3-c428-4152-a070-6f2a951efd58--&gt;
&lt;!-- begin heading_3 e427c2e4-9fb9-4f22-8c81-b9205a45034d--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-e427c2e4-9fb9-4f22-8c81-b9205a45034d&quot;&gt;Openjdk&lt;/h4&gt;
&lt;!-- end heading_3 e427c2e4-9fb9-4f22-8c81-b9205a45034d--&gt;
&lt;!-- begin paragraph 4a32b265-0ca3-41d9-9c5e-7bef532041a6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a32b265-0ca3-41d9-9c5e-7bef532041a6&quot;&gt;Vous pouvez trouver une version du &lt;a href=&quot;http://jdk.java.net/loom/&quot;&gt;projet loom ici&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 4a32b265-0ca3-41d9-9c5e-7bef532041a6--&gt;
&lt;!-- begin paragraph def76e1e-b058-4dff-b574-c82d4df46caa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-def76e1e-b058-4dff-b574-c82d4df46caa&quot;&gt;&lt;b&gt;Config OSX + Jenv&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph def76e1e-b058-4dff-b574-c82d4df46caa--&gt;
&lt;!-- begin paragraph 5ae57662-b48f-4859-97fa-3a7d9d603e08--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ae57662-b48f-4859-97fa-3a7d9d603e08&quot;&gt;Si vous n&amp;#39;utilisez pas déjà Jenv je vous conseille d&amp;#39;aller jeter &lt;a href=&quot;https://www.jenv.be/&quot;&gt;un oeil ici&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 5ae57662-b48f-4859-97fa-3a7d9d603e08--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/bfb6f8d1-029f-436b-b5a0-094bf6ee84d2/capture_decran_2021-06-11_a_10.51.41.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 a29d1651-fab5-4b0e-91f2-46a7fed31e99--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-a29d1651-fab5-4b0e-91f2-46a7fed31e99&quot;&gt;Setup d&amp;#39;IntelliJ&lt;/h4&gt;
&lt;!-- end heading_3 a29d1651-fab5-4b0e-91f2-46a7fed31e99--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Passer le compilateur à Java 15 &lt;/li&gt;&lt;/ol&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/bfb6f8d1-029f-436b-b5a0-094bf6ee84d2/capture_decran_2021-06-11_a_11.06.56.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph c3cdabc0-0371-48e8-9abc-7f5b2d972094--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c3cdabc0-0371-48e8-9abc-7f5b2d972094&quot;&gt;Passer le SDK a 17 avec un niveau de langage java 15 pour l&amp;#39;ensemble du projet:&lt;/p&gt;
&lt;!-- end paragraph c3cdabc0-0371-48e8-9abc-7f5b2d972094--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/bfb6f8d1-029f-436b-b5a0-094bf6ee84d2/capture_decran_2021-05-31_a_11.09.41.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/bfb6f8d1-029f-436b-b5a0-094bf6ee84d2/capture_decran_2021-06-11_a_11.07.17.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/bfb6f8d1-029f-436b-b5a0-094bf6ee84d2/capture_decran_2021-06-11_a_11.07.34.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/bfb6f8d1-029f-436b-b5a0-094bf6ee84d2/capture_decran_2021-06-11_a_11.07.31.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 41f566b2-eecb-431c-a5ab-80e0cced821d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-41f566b2-eecb-431c-a5ab-80e0cced821d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 41f566b2-eecb-431c-a5ab-80e0cced821d--&gt;
&lt;!-- begin heading_2 fa857ae0-0518-4d28-bbe1-3b888fa64b11--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-fa857ae0-0518-4d28-bbe1-3b888fa64b11&quot;&gt;Les fibers / Threads virtuels&lt;/h3&gt;
&lt;!-- end heading_2 fa857ae0-0518-4d28-bbe1-3b888fa64b11--&gt;
&lt;!-- begin heading_3 bc72445e-43bb-476f-8cc9-a96a8469f6e8--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-bc72445e-43bb-476f-8cc9-a96a8469f6e8&quot;&gt;Prérequis:&lt;/h4&gt;
&lt;!-- end heading_3 bc72445e-43bb-476f-8cc9-a96a8469f6e8--&gt;
&lt;!-- begin paragraph b957ccc0-cf6c-4aef-acf5-7513caa4defb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b957ccc0-cf6c-4aef-acf5-7513caa4defb&quot;&gt;Si vous avez déjà fait des algos concurrents en Java alors vous n&amp;#39;avez pas grand-chose d&amp;#39;autre à savoir. Pensez juste que désormais, spawner un thread ne coute pas grand-chose &lt;/p&gt;
&lt;!-- end paragraph b957ccc0-cf6c-4aef-acf5-7513caa4defb--&gt;
&lt;!-- begin heading_3 12b83ec1-e575-4ed0-8c8f-d224ae639326--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-12b83ec1-e575-4ed0-8c8f-d224ae639326&quot;&gt;Exemple&lt;/h4&gt;
&lt;!-- end heading_3 12b83ec1-e575-4ed0-8c8f-d224ae639326--&gt;
&lt;!-- begin code 624dcb7f-aab9-4023-90d4-f1ba80a98141--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-624dcb7f-aab9-4023-90d4-f1ba80a98141&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;package fr.guihardbastien.boilerplate;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

public class Main {

    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;

    public void syncAdd() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public void fibersHelloWorld() throws InterruptedException {
        var a = new ArrayList&amp;lt;Thread&amp;gt;();
        for (int i = 0; i &amp;lt; 1_000_000; i++) {
            var t = Thread.ofVirtual().start(() -&amp;gt; {
                try {
                    Thread.sleep(Duration.ofSeconds(1));
                    this.syncAdd();
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + &amp;quot;-- Interrupted&amp;quot;);
                }
            });
            a.add(t);
        }

        for (var t : a) {
            t.join();
        }
        System.out.println(this.counter);
    }

    public static void main(String[] args) throws InterruptedException {
        var m = new Main();
        m.fibersHelloWorld();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 624dcb7f-aab9-4023-90d4-f1ba80a98141--&gt;
&lt;!-- begin paragraph 905ee0ee-dc42-439b-9ab2-6f33a04d0b34--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-905ee0ee-dc42-439b-9ab2-6f33a04d0b34&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 905ee0ee-dc42-439b-9ab2-6f33a04d0b34--&gt;
&lt;!-- begin heading_2 47123d20-f54f-411d-88c0-a0848ee034b2--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-47123d20-f54f-411d-88c0-a0848ee034b2&quot;&gt;Les continuations&lt;/h3&gt;
&lt;!-- end heading_2 47123d20-f54f-411d-88c0-a0848ee034b2--&gt;
&lt;!-- begin heading_3 c8a2bd45-e987-4732-8aaf-a2f95b9c6e6a--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-c8a2bd45-e987-4732-8aaf-a2f95b9c6e6a&quot;&gt;Prérequis&lt;/h4&gt;
&lt;!-- end heading_3 c8a2bd45-e987-4732-8aaf-a2f95b9c6e6a--&gt;
&lt;!-- begin paragraph 5afa2fe0-5c15-46b7-95a5-e9aa03de57ba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5afa2fe0-5c15-46b7-95a5-e9aa03de57ba&quot;&gt;Pour rappel les continuations sont des fonctions pouvant être interrompues et stockées sur le tas facilement. Il y a 5 choses à savoir pour espérer utiliser des continuations:&lt;/p&gt;
&lt;!-- end paragraph 5afa2fe0-5c15-46b7-95a5-e9aa03de57ba--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Scope: Une continuation s’exécute dans un scope fourni par la classe ContinuationScope (c&amp;#39;est globalement juste une classe qui contient un champs name) &lt;/li&gt;&lt;li&gt;Runnable: La classe Continuation prend en arguments un scope et un Runnable (type : &lt;b&gt;() → void&lt;/b&gt;) &lt;/li&gt;&lt;li&gt;.yield : Yield est la méthode qui permet d&amp;#39;interrompre un continuation.&lt;/li&gt;&lt;li&gt;.run : Run est la méthode qui permet de lancer ou reprendre la continuation. &lt;/li&gt;&lt;li&gt;.isDone : Is Done renvoie un booléen vrai si l’exécution du runnable est terminée.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_3 2f65281a-cd13-4aab-8fca-e9a60b6b39df--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-2f65281a-cd13-4aab-8fca-e9a60b6b39df&quot;&gt;Example&lt;/h4&gt;
&lt;!-- end heading_3 2f65281a-cd13-4aab-8fca-e9a60b6b39df--&gt;
&lt;!-- begin code d07f67bd-5a79-4d99-8742-d45040c8aba4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d07f67bd-5a79-4d99-8742-d45040c8aba4&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;public class Main {
    public static void continuationHelloWorld() {
        var scope = new ContinuationScope(&amp;quot;scope&amp;quot;);
        var continuation1 = new Continuation(scope, () -&amp;gt; {
            System.out.println(&amp;quot;start 1&amp;quot;);
            Continuation.yield(scope);
            System.out.println(&amp;quot;middle 1&amp;quot;);
            Continuation.yield(scope);
            System.out.println(&amp;quot;end 1&amp;quot;);
        });
        var continuation2 = new Continuation(scope, () -&amp;gt; {
            System.out.println(&amp;quot;start 2&amp;quot;);
            Continuation.yield(scope);
            System.out.println(&amp;quot;middle 2&amp;quot;);
            Continuation.yield(scope);
            System.out.println(&amp;quot;end 2&amp;quot;);
        });
        var list = List.of(continuation1, continuation2);

        while (!continuation1.isDone()) {
            list.forEach(Continuation::run);
        }
    }
    
    public static void main(String[] args) {
        continuationHelloWorld();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d07f67bd-5a79-4d99-8742-d45040c8aba4--&gt;
&lt;!-- begin paragraph 4c8dd63b-0a94-4b39-9b29-f38c18608493--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4c8dd63b-0a94-4b39-9b29-f38c18608493&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4c8dd63b-0a94-4b39-9b29-f38c18608493--&gt;
&lt;!-- begin heading_2 ad2076a7-3ab9-43e8-8b3b-e8d0441ee74c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ad2076a7-3ab9-43e8-8b3b-e8d0441ee74c&quot;&gt;Conclusions&lt;/h3&gt;
&lt;!-- end heading_2 ad2076a7-3ab9-43e8-8b3b-e8d0441ee74c--&gt;
&lt;!-- begin paragraph bccc215d-a4dd-44b3-b9ca-5ac63fae9aa8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bccc215d-a4dd-44b3-b9ca-5ac63fae9aa8&quot;&gt;En somme les thread virtuels permettent une construction facile de programmes concurrents et sont très performants, notamment au niveau de la mémoire :&lt;/p&gt;
&lt;!-- end paragraph bccc215d-a4dd-44b3-b9ca-5ac63fae9aa8--&gt;
&lt;!-- begin callout 57a95f77-7844-406f-986e-f814b5460592--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-57a95f77-7844-406f-986e-f814b5460592&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;☕&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;JDK 11 occupied a heap of 6.8 GB with a peak use of 4.7 GB&lt;br /&gt;
JDK 16 without Virtual Threads occupied a heap of 4.6 GB with a peak use of 3.7 GB&lt;br /&gt;
JDK 16 with Virtual Threads occupied a heap of 2.7 GB with a peak use of 2.37 GB&lt;br /&gt;
&lt;br /&gt;
So, using JDK 16 with these light-weight virtual threads resulted in:&lt;br /&gt;
&lt;br /&gt;
only 50% usage of heap memory compared to JDK 11&lt;br /&gt;
only 64% usage of heap memory compared to JDK 16 without virtual threads&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.jobrunr.io/en/blog/2020-08-17-jobrunr-loom-virtual-threads/&quot;&gt;&lt;i&gt;https://www.jobrunr.io/en/blog/2020-08-17-jobrunr-loom-virtual-threads/&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout 57a95f77-7844-406f-986e-f814b5460592--&gt;
&lt;!-- begin paragraph 9c0d825b-0735-45eb-8a74-6aa7235d9785--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c0d825b-0735-45eb-8a74-6aa7235d9785&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9c0d825b-0735-45eb-8a74-6aa7235d9785--&gt;
&lt;!-- begin paragraph e074d83e-a028-492c-b864-213d3d543bf4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e074d83e-a028-492c-b864-213d3d543bf4&quot;&gt;Pour finir, voilà un petit boilerplate pour commencer à jouer avec !&lt;/p&gt;
&lt;!-- end paragraph e074d83e-a028-492c-b864-213d3d543bf4--&gt;
&lt;!-- begin heading_2 82e8ea34-ad95-4f47-888a-d987749aa5b1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-82e8ea34-ad95-4f47-888a-d987749aa5b1&quot;&gt;Ressources&lt;/h3&gt;
&lt;!-- end heading_2 82e8ea34-ad95-4f47-888a-d987749aa5b1--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/forax/loom-fiber&quot;&gt;https://github.com/forax/loom-fiber&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.openjdk.java.net/display/loom/Main&quot;&gt;https://wiki.openjdk.java.net/display/loom/Main&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.openjdk.java.net/display/loom/Getting+started&quot;&gt;https://wiki.openjdk.java.net/display/loom/Getting+started&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html&quot;&gt;https://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.html&quot;&gt;http://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.javaadvent.com/2020/12/project-loom-and-structured-concurrency.html&quot;&gt;https://www.javaadvent.com/2020/12/project-loom-and-structured-concurrency.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/early-access-java-concurrency-with-project-loom&quot;&gt;https://dzone.com/articles/early-access-java-concurrency-with-project-loom&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.jobrunr.io/en/blog/2020-08-17-jobrunr-loom-virtual-threads/&quot;&gt;https://www.jobrunr.io/en/blog/2020-08-17-jobrunr-loom-virtual-threads/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 2ba9cb47-be6b-4ec2-b54c-d2b54ec61b87--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2ba9cb47-be6b-4ec2-b54c-d2b54ec61b87&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2ba9cb47-be6b-4ec2-b54c-d2b54ec61b87--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:5a5abc94463447e69e57ff498e9dcfad</id>
    <title>Décoder le passe sanitaire</title>
    <updated>2021-07-22T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/decoder-le-passe-sanitaire.html"/>
    <!--summary Que contient le passe sanitaire ? Qu&#039;y a-t-il dans ce QR code ? Voici ce que propose de découvrir dans cet article. Nous parlerons aussi de son fonctionnement au niveau européen, de sécurité et de certaines implications liées à ce passe.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Pass sanitaire"></category>    <category term="Analyse de données"></category>    <category term="EUDCC"></category>    <category term="Sécurité"></category>    <content type="html">
&lt;!-- begin paragraph 74b71da1-5a90-401e-8d77-f3b793d86c89--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-74b71da1-5a90-401e-8d77-f3b793d86c89&quot;&gt;Vous voilà vacciné contre la (le ?) Covid-19 avec 1, 2, voire 3 doses espacées. Pour l&amp;#39;occasion, on vous remet un certificat de vaccination avec un immense QR code à enregistrer dans l&amp;#39;application TousAntiCovid.&lt;/p&gt;
&lt;!-- end paragraph 74b71da1-5a90-401e-8d77-f3b793d86c89--&gt;
&lt;!-- begin paragraph 5e2b39d9-0490-4767-b395-62d8346167f5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e2b39d9-0490-4767-b395-62d8346167f5&quot;&gt;Un QR code (et tout système de code barre apparenté) est un moyen de stocker de l’information. Les versions récentes du pass sanitaire sont alignées depuis le 1er juillet 2021 sur son équivalent européen : le &lt;i&gt;Digital Green Certificate&lt;/i&gt; (DGC), devenu &lt;a href=&quot;https://ec.europa.eu/info/live-work-travel-eu/coronavirus-response/safe-covid-19-vaccines-europeans/eu-digital-covid-certificate_en&quot;&gt;&lt;i&gt;European Digital Covid Certificate&lt;/i&gt;&lt;/a&gt; (EUDCC), qui se base justement sur un QR code. Que contient-il ?&lt;/p&gt;
&lt;!-- end paragraph 5e2b39d9-0490-4767-b395-62d8346167f5--&gt;
&lt;!-- begin paragraph 748da5da-30f2-4c2d-9ce3-ee4f3e85d398--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-748da5da-30f2-4c2d-9ce3-ee4f3e85d398&quot;&gt;Nous allons d&amp;#39;abord voir comment récupérer le contenu du QR code. Puis, nous verrons rapidement ce contenu. Ensuite, nous parlerons de la sécurité mise en place autour du certificat et nous finirons en comparant avec le système existant.&lt;/p&gt;
&lt;!-- end paragraph 748da5da-30f2-4c2d-9ce3-ee4f3e85d398--&gt;
&lt;!-- begin heading_1 cea81f0a-c4d6-4590-86f5-ef09011597a5--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-cea81f0a-c4d6-4590-86f5-ef09011597a5&quot;&gt;Récupérer le contenu&lt;/h2&gt;
&lt;!-- end heading_1 cea81f0a-c4d6-4590-86f5-ef09011597a5--&gt;
&lt;!-- begin paragraph 615a273d-f0ef-44c1-a7ff-428dda63718d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-615a273d-f0ef-44c1-a7ff-428dda63718d&quot;&gt;Pour pouvoir lire le contenu, vous pouvez au choix utiliser une bibliothèque comme &lt;a href=&quot;https://github.com/zxing/zxing&quot;&gt;ZXing&lt;/a&gt; ou allez directement faire une recherche sur &lt;a href=&quot;https://duckduckgo.com/?q=online%20qr%20code%20scanner&quot;&gt;&amp;quot;online qr code scanner&amp;quot;&lt;/a&gt;, pour trouver un scanner qui fonctionne avec votre navigateur et votre webcam (par exemple, &lt;a href=&quot;https://webqr.com/&quot;&gt;https://webqr.com/&lt;/a&gt;).&lt;/p&gt;
&lt;!-- end paragraph 615a273d-f0ef-44c1-a7ff-428dda63718d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/5a5abc94-4634-47e6-9e57-ff498e9dcfad/screenshot_2021-07-21_at_13.49.33.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Ce QR code contient un certificat compatible, mais non valide. Vous ne pourrez pas le lire avec l&amp;#39;application TousAntiCovid. En dehors du problème de signature, il indique que Jean Martin (né le 3 février 1970) s&amp;#39;est fait vacciner le 1er juillet 2021 avec le vaccin de Pfizer.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 86f230e4-310b-464c-b741-e64d53e02abf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86f230e4-310b-464c-b741-e64d53e02abf&quot;&gt;Vous récupérez une chaîne de caractères commençant par &lt;code&gt;HC&lt;/code&gt; (pour &lt;i&gt;Health Certificate&lt;/i&gt;), un numéro de version (1, 2...), &lt;code&gt;:&lt;/code&gt; et succession de caractères.&lt;/p&gt;
&lt;!-- end paragraph 86f230e4-310b-464c-b741-e64d53e02abf--&gt;
&lt;!-- begin paragraph 11048fcd-eb63-420b-81d8-1c0e9ca72aec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-11048fcd-eb63-420b-81d8-1c0e9ca72aec&quot;&gt;Le QR code ci-dessus donne la chaîne suivante (les sauts de ligne ont été ajoutés pour des questions de présentation) :&lt;/p&gt;
&lt;!-- end paragraph 11048fcd-eb63-420b-81d8-1c0e9ca72aec--&gt;
&lt;!-- begin code cf052764-bd1a-4eaa-8c1b-d404d09dddcc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-cf052764-bd1a-4eaa-8c1b-d404d09dddcc&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;HC1:6BF$RDA-SMAHN-H6SKJPT.-7G2TZ97+S8Y4CXEJW.TFJTXG4DCESYCTSJ2TP$2G*P5Y
IJ6S4HZ6SH9+2QH/56SP.E5BQ95ZM376ZIE7PM1VE.Q6T:H:1NPK9/1AAJ1KK9%OC+G9QJP
NF67J6QW6D9RY466PPXY0E7J7UJQWT.+S1QDC8CI6C6XIO$9KZ56DE/.QC$Q3J62:6LZ6O5
9++9-G9+E93ZM$96PZ6+Q6X46+E54A9NF625F646L+9AKPCPP7ZMN27QW6ZOQ.NE/E2$4JY
/K9:K4F7D*G2SV /KF-KXIN2SV6AL3*I**GYZQVG9YJC/HLIJLKNF8JF1727WLTPL 6KNYM
B26REDFAQOQUX9RGQR.RE1G6*%3PNNPXSAXB*LM.NEMUN50I++VKZ39GTLV75+UJ+0W8J:A
T0F6 WAKWVV7U*V7J:VM30AGRK2&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code cf052764-bd1a-4eaa-8c1b-d404d09dddcc--&gt;
&lt;!-- begin paragraph 89283471-ba42-43a3-9f9d-928428f48b53--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89283471-ba42-43a3-9f9d-928428f48b53&quot;&gt;Le contenu de cette chaîne de caractères se base sur des &lt;a href=&quot;https://github.com/ehn-dcc-development/hcert-spec&quot;&gt;technologies standardisées&lt;/a&gt; (ou presque...). En effet, en retirant l&amp;#39;en-tête (&lt;code&gt;HC1:&lt;/code&gt;), la chaîne que vous obtenez est une chaîne encodée en Base 45. Base 45 est lié à une &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-faltstrom-base45/&quot;&gt;future RFC&lt;/a&gt; (encore en brouillon actuellement) qui se base sur un ensemble de 45 caractères pour encoder des données binaires. Il faut 3 caractères pour encoder 2 octets.&lt;/p&gt;
&lt;!-- end paragraph 89283471-ba42-43a3-9f9d-928428f48b53--&gt;
&lt;!-- begin paragraph 55e8b760-99e3-44ea-9e5c-4bd43a7bb852--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-55e8b760-99e3-44ea-9e5c-4bd43a7bb852&quot;&gt;Il est possible de trouver une bibliothèque Java pour gérer l&amp;#39;encodage Base 45.&lt;/p&gt;
&lt;!-- end paragraph 55e8b760-99e3-44ea-9e5c-4bd43a7bb852--&gt;
&lt;!-- begin code 38ab1285-19ed-4acc-b588-3484c47d313b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-38ab1285-19ed-4acc-b588-3484c47d313b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&amp;quot;io.github.ehn-digital-green-development&amp;quot; % &amp;quot;base45&amp;quot; % &amp;quot;0.0.3&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 38ab1285-19ed-4acc-b588-3484c47d313b--&gt;
&lt;!-- begin paragraph 3e1e2253-6cfb-45e2-a5f5-1bea698d67b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3e1e2253-6cfb-45e2-a5f5-1bea698d67b7&quot;&gt;Cette bibliothèque est directement développée par l&amp;#39;organisation Github, responsable de la mise en place du système EUDCC : &lt;a href=&quot;https://github.com/ehn-dcc-development&quot;&gt;European eHealth Network - Digital Covid Certificate Coordination&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 3e1e2253-6cfb-45e2-a5f5-1bea698d67b7--&gt;
&lt;!-- begin paragraph 80f15e59-b8f8-42cf-8338-1f563f270bad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-80f15e59-b8f8-42cf-8338-1f563f270bad&quot;&gt;Elle s&amp;#39;utilise ainsi :&lt;/p&gt;
&lt;!-- end paragraph 80f15e59-b8f8-42cf-8338-1f563f270bad--&gt;
&lt;!-- begin code 7226a9e6-5468-4dfe-926b-50951639b49a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7226a9e6-5468-4dfe-926b-50951639b49a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val base45Content: String   = qrCodeData.split(&amp;quot;:&amp;quot;, 2)(1).trim
val binaryData: Array[Byte] = Base45.getDecoder.decode(base45Content)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7226a9e6-5468-4dfe-926b-50951639b49a--&gt;
&lt;!-- begin paragraph 4db47c5d-a9bd-488e-bf3b-25db74a17540--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4db47c5d-a9bd-488e-bf3b-25db74a17540&quot;&gt;Les données obtenues sont des données compressées avec ZLIB. Le JDK inclut cette lib.&lt;/p&gt;
&lt;!-- end paragraph 4db47c5d-a9bd-488e-bf3b-25db74a17540--&gt;
&lt;!-- begin code 67f20190-5536-4986-ab66-b346989c17f3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-67f20190-5536-4986-ab66-b346989c17f3&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val inflater = new Inflater()
inflater.setInput(binaryData)
val buffer: Array[Byte] = Array.ofDim[Byte](2048)
val length: Int         = inflater.inflate(buffer)
inflater.end()

val data: Array[Byte] = Array.copyOf(buffer, length)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 67f20190-5536-4986-ab66-b346989c17f3--&gt;
&lt;!-- begin paragraph 5351768e-63b0-48d3-835d-303da476e2b2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5351768e-63b0-48d3-835d-303da476e2b2&quot;&gt;À ce niveau, nous obtenons une structure de type CBOR. CBOR est un format de donnée binaire qui est un sur-ensemble de JSON sur le plan sémantique et qui permet d&amp;#39;avoir un encodage compact. CBOR est spécifié par la &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8949&quot;&gt;RFC 8949&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 5351768e-63b0-48d3-835d-303da476e2b2--&gt;
&lt;!-- begin paragraph 75a4c3b2-66f3-40bb-a653-9a975712949e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75a4c3b2-66f3-40bb-a653-9a975712949e&quot;&gt;La structure CBOR que nous récupérons du QR code répond aux spécifications COSE (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8152&quot;&gt;RFC 8152 - draft&lt;/a&gt;). Cette structure présente 3 champs :&lt;/p&gt;
&lt;!-- end paragraph 75a4c3b2-66f3-40bb-a653-9a975712949e--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;i&gt;protected&lt;/i&gt; : contenant un document CBOR au format binaire et représentant des paramètres.&lt;/li&gt;&lt;li&gt;&lt;i&gt;unprotected&lt;/i&gt; : contenant une table représentant des paramètres complémentaires.&lt;/li&gt;&lt;li&gt;Le contenu du message au format binaire&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 805dfe65-c5a0-44d1-b5d3-e63e71a20740--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-805dfe65-c5a0-44d1-b5d3-e63e71a20740&quot;&gt;Un dernier champ peut être ajouté afin d&amp;#39;y déposer une signature calculée en fonction des champs précédents.&lt;/p&gt;
&lt;!-- end paragraph 805dfe65-c5a0-44d1-b5d3-e63e71a20740--&gt;
&lt;!-- begin paragraph 4dcdd138-d600-4e07-ad36-5fd5aeeeb10c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4dcdd138-d600-4e07-ad36-5fd5aeeeb10c&quot;&gt;Il existe différentes bibliothèques gérant le format CBOR, dont une extension de Jackson. Cependant, cette extension ne permet d&amp;#39;avoir qu&amp;#39;une perception limitée à la perception JSON, sans pouvoir explorer la variété de type que propose CBOR. La plupart des projets autour du certificat européen utilisent la bibliothèque &lt;a href=&quot;https://github.com/peteroupc/CBOR-Java&quot;&gt;CBOR-Java&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 4dcdd138-d600-4e07-ad36-5fd5aeeeb10c--&gt;
&lt;!-- begin paragraph 7df01140-8ad1-4d07-a816-9d5584fa291b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7df01140-8ad1-4d07-a816-9d5584fa291b&quot;&gt;Je vous propose d&amp;#39;utiliser ici la bibliothèque ci-dessous, ce qui permettra en plus de vérifier le respect du standard au niveau du certificat européen.&lt;/p&gt;
&lt;!-- end paragraph 7df01140-8ad1-4d07-a816-9d5584fa291b--&gt;
&lt;!-- begin code e0691fed-29d7-4746-8e37-a108622df9fd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e0691fed-29d7-4746-8e37-a108622df9fd&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&amp;quot;co.nstant.in&amp;quot; % &amp;quot;cbor&amp;quot; % &amp;quot;0.9&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e0691fed-29d7-4746-8e37-a108622df9fd--&gt;
&lt;!-- begin paragraph 2053a83b-0152-4ac1-a0bd-1e056808dc6d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2053a83b-0152-4ac1-a0bd-1e056808dc6d&quot;&gt;La récupération du modèle mémoire de la structure s&amp;#39;obtient de la manière suivante :&lt;/p&gt;
&lt;!-- end paragraph 2053a83b-0152-4ac1-a0bd-1e056808dc6d--&gt;
&lt;!-- begin code 1c611e23-615c-4e23-98e7-3b20dfc6f98e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1c611e23-615c-4e23-98e7-3b20dfc6f98e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val healthCertificate: List[DataItems] = CborDecoder.decode(data)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1c611e23-615c-4e23-98e7-3b20dfc6f98e--&gt;
&lt;!-- begin paragraph 94e422c2-1b67-4a01-a149-360bc3588946--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-94e422c2-1b67-4a01-a149-360bc3588946&quot;&gt;Pour rappel, les étapes pour récupérer de l&amp;#39;information intelligible sont :&lt;/p&gt;
&lt;!-- end paragraph 94e422c2-1b67-4a01-a149-360bc3588946--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Analyse et retrait du préfixe &lt;code&gt;HCn:&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Décodage base 45&lt;/li&gt;&lt;li&gt;Décompression ZLIB&lt;/li&gt;&lt;li&gt;Analyse avec un parser CBOR&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph a7bad6b3-aff6-4cee-8951-6889f8bbf182--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a7bad6b3-aff6-4cee-8951-6889f8bbf182&quot;&gt;Dans ces phases, nous n&amp;#39;avons pas eu affaire à du chiffrement. Il n&amp;#39;y a pas de mot de passe ou d&amp;#39;autres systèmes permettant de protéger le contenu. Au contraire, ce contenu est directement accessible à partir de bibliothèques librement accessibles.&lt;/p&gt;
&lt;!-- end paragraph a7bad6b3-aff6-4cee-8951-6889f8bbf182--&gt;
&lt;!-- begin heading_1 249b82ee-2560-4145-bcb0-e0d0d556c264--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-249b82ee-2560-4145-bcb0-e0d0d556c264&quot;&gt;Contenu&lt;/h2&gt;
&lt;!-- end heading_1 249b82ee-2560-4145-bcb0-e0d0d556c264--&gt;
&lt;!-- begin paragraph b9f7fe59-e5be-4fcf-bfcc-b9d7ad3db0f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b9f7fe59-e5be-4fcf-bfcc-b9d7ad3db0f9&quot;&gt;Il y a trois champs qui vont nous intéresser :&lt;/p&gt;
&lt;!-- end paragraph b9f7fe59-e5be-4fcf-bfcc-b9d7ad3db0f9--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;i&gt;protected&lt;/i&gt; qui contient des informations sur l&amp;#39;algorithme utiliser pour la signature et un &lt;i&gt;keyId&lt;/i&gt; qui permet de retrouver la clé publique pour vérifier la signature. Nous en parlerons plus loin.&lt;/li&gt;&lt;li&gt;Le contenu qui conserve les informations du certificat.&lt;/li&gt;&lt;li&gt;La signature qui garantit la validité du certificat. Nous en parlerons aussi plus loin.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 1c05993a-5d54-4f5a-b3de-f22e69fa541e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1c05993a-5d54-4f5a-b3de-f22e69fa541e&quot;&gt;Le champ &lt;i&gt;unprotected&lt;/i&gt; n&amp;#39;est apparemment pas utilisé actuellement.&lt;/p&gt;
&lt;!-- end paragraph 1c05993a-5d54-4f5a-b3de-f22e69fa541e--&gt;
&lt;!-- begin paragraph 6350e98a-fca5-4390-bae4-e8971a173322--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6350e98a-fca5-4390-bae4-e8971a173322&quot;&gt;La partie contenu est équivalente à un document JSON avec 3 niveaux d&amp;#39;imbrication. Le premier niveau donne l&amp;#39;identifiant de l&amp;#39;émetteur du certificat et sa période de validité. De notre exemple précédent, on obtient :&lt;/p&gt;
&lt;!-- end paragraph 6350e98a-fca5-4390-bae4-e8971a173322--&gt;
&lt;!-- begin code 861bdf56-a4d0-4083-b027-edbc8c944f66--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-861bdf56-a4d0-4083-b027-edbc8c944f66&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Issuer (field 1):     CNAM
Issued at (field 4):  2021-07-01
Expired at (field 6): 2023-07-01&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 861bdf56-a4d0-4083-b027-edbc8c944f66--&gt;
&lt;!-- begin paragraph 0942eb45-1dbd-4a95-acd6-07e0a42d9bab--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0942eb45-1dbd-4a95-acd6-07e0a42d9bab&quot;&gt;Un 4e champ accompagne cette structure. Ce champ est annoté &lt;code&gt;-260&lt;/code&gt; et il contient les faits à l&amp;#39;origine du certificat.&lt;/p&gt;
&lt;!-- end paragraph 0942eb45-1dbd-4a95-acd6-07e0a42d9bab--&gt;
&lt;!-- begin paragraph ac43eda9-e592-4ecd-b338-24c254cea2d0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ac43eda9-e592-4ecd-b338-24c254cea2d0&quot;&gt;Parmi ces informations, il y a tout d&amp;#39;abord la version du schéma de certificat utilisé (clé &lt;code&gt;ver&lt;/code&gt;). Nous y trouvons une structure contenant votre nom (clé &lt;code&gt;nam&lt;/code&gt;), ainsi que votre nom encodé à la manière de ce que vous pouvez trouver en bas des cartes d&amp;#39;identité ou des passeports. Vous trouvez aussi votre date de naissance (clé &lt;code&gt;dob&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph ac43eda9-e592-4ecd-b338-24c254cea2d0--&gt;
&lt;!-- begin paragraph 4571d0eb-96f2-4179-98c6-c41d41f81e83--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4571d0eb-96f2-4179-98c6-c41d41f81e83&quot;&gt;Un dernier champ contient la liste des vaccins (clé &lt;code&gt;v&lt;/code&gt;). Avec une capacité maximale théorique de 2953 octets, il est, en effet, possible de stocker plusieurs actes dans un QR code, qu&amp;#39;il s&amp;#39;agisse de tests, de vaccins ou d&amp;#39;infection. À titre indicatif, la structure complète de l&amp;#39;exemple vu ci-dessus fait 300 octets. De ce fait, le QR code EUDCC peut faire office de carnet de vaccination.&lt;/p&gt;
&lt;!-- end paragraph 4571d0eb-96f2-4179-98c6-c41d41f81e83--&gt;
&lt;!-- begin paragraph e2481404-a008-46fd-8faf-3bc1f53eb327--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2481404-a008-46fd-8faf-3bc1f53eb327&quot;&gt;Au niveau de chaque acte de &amp;quot;vaccination&amp;quot;, nous avons tout d&amp;#39;abord un identifiant unique appelé UVCI (clé &lt;code&gt;ci&lt;/code&gt;), qui apparait dans notre structure sous forme d&amp;#39;URN. Nous décrirons cet identifiant plus loin. Nous avons ensuite le pays émetteur (clé &lt;code&gt;co&lt;/code&gt;). Pour les vaccins, nous trouvons derrière, le nombre de doses reçues (clé &lt;code&gt;sd&lt;/code&gt;), le nombre de doses nécessaires (clé &lt;code&gt;dn&lt;/code&gt;), la date de la dernière dose (clé &lt;code&gt;dt&lt;/code&gt;), le code du fabricant du vaccin (clé &lt;code&gt;ma&lt;/code&gt;) et le code produit administré (clé &lt;code&gt;mp&lt;/code&gt;). Pour ces codes, l&amp;#39;ensemble des valeurs sont décrites dans un &lt;a href=&quot;https://ec.europa.eu/health/sites/default/files/ehealth/docs/digital-green-certificates_dt-specifications_en.pdf&quot;&gt;document accessible&lt;/a&gt;. Dans cette structure, nous allons aussi trouver l&amp;#39;agent ciblé (clé &lt;code&gt;tg&lt;/code&gt; — c&amp;#39;est la Covid-19), le type d&amp;#39;administration.&lt;/p&gt;
&lt;!-- end paragraph e2481404-a008-46fd-8faf-3bc1f53eb327--&gt;
&lt;!-- begin paragraph e3fcf834-aa73-4a2c-960f-a18ff9fd1ae9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e3fcf834-aa73-4a2c-960f-a18ff9fd1ae9&quot;&gt;Avec notre exemple, nous avons les données suivantes :&lt;/p&gt;
&lt;!-- end paragraph e3fcf834-aa73-4a2c-960f-a18ff9fd1ae9--&gt;
&lt;!-- begin code 2023f226-54c9-4878-bcc8-cf7968c73544--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2023f226-54c9-4878-bcc8-cf7968c73544&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;ci: urn:uvci:01:FR:AB123Z#8
co: FR
dn: 2
dt: 2021-07-01
is: CNAM
ma: ORG-100030215 (Biontech Manufacturing GmbH)
mp: EU/1/20/1528 (Comirnaty)
sd: 2
tg: 840539006 (COVID-19)
vp: J07BX03 (covid-19 vaccines)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2023f226-54c9-4878-bcc8-cf7968c73544--&gt;
&lt;!-- begin paragraph 9b676631-ce12-40a7-b3ef-b7ca93af910c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b676631-ce12-40a7-b3ef-b7ca93af910c&quot;&gt;Les données que nous voyons sont certes un exemple avec un faux certificat, mais elles sont proches de ce que vous avez comme certificats (si vous êtes vaccinés) ou de ce que vous aurez (si vous comptez vous faire vacciner). Ces données sont avant tout factuelles et traduisent en partie le manque de connaissance scientifique sur la Covid-19 de par l&amp;#39;absence de date de fin de validité du vaccin. Nous avons seulement une absence de validité du certificat, ce qui est différent.&lt;/p&gt;
&lt;!-- end paragraph 9b676631-ce12-40a7-b3ef-b7ca93af910c--&gt;
&lt;!-- begin heading_1 108b772f-1aed-437a-b7f7-0b18fa0c0776--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-108b772f-1aed-437a-b7f7-0b18fa0c0776&quot;&gt;Unicité de l&amp;#39;information&lt;/h2&gt;
&lt;!-- end heading_1 108b772f-1aed-437a-b7f7-0b18fa0c0776--&gt;
&lt;!-- begin paragraph 407f3f43-69be-4c05-b30d-3130e07f55f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-407f3f43-69be-4c05-b30d-3130e07f55f0&quot;&gt;Nous voyons qu’en utilisant quelques bibliothèques sur le Net, nous pouvons accéder à des informations personnelles du certificat, sachant que ces informations ne sont pas chiffrées. Néanmoins, elles ne sont pas nécessairement suffisantes pour vous identifier : combien de Jean Martin sont nés le même jour dans tous les pays participants au programme DGC ? D’un autre côté, il n’y a probablement pas beaucoup de Côme Batz de Saint-Anmien. Il est nécessaire d&amp;#39;accompagner le certificat d&amp;#39;un document de type CNI ou passeport, par exemple, pour être sûr qu&amp;#39;il n&amp;#39;y a pas usurpation d&amp;#39;identité. Il reste à ce niveau à comprendre ce que vaut l’UVCI vu précédemment.&lt;/p&gt;
&lt;!-- end paragraph 407f3f43-69be-4c05-b30d-3130e07f55f0--&gt;
&lt;!-- begin paragraph 3675e654-ad6d-4ff7-845a-b9ca56f78d6d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3675e654-ad6d-4ff7-845a-b9ca56f78d6d&quot;&gt;L&amp;#39;UVCI est un identifiant unique qui correspond à un &lt;a href=&quot;https://ec.europa.eu/health/sites/default/files/ehealth/docs/vaccination-proof_interoperability-guidelines_en.pdf&quot;&gt;&lt;i&gt;Unique Vaccination Certificate/Assertion Identifier&lt;/i&gt;&lt;/a&gt;. L&amp;#39;UVCI n&amp;#39;est pas nécessairement un URN, mais il doit contenir un numéro de version et un code pays émetteur en préfixe, ainsi qu&amp;#39;un checksum en suffixe. Le reste de l&amp;#39;UVCI ainsi que le format peut varier d&amp;#39;un pays à l&amp;#39;autre.&lt;/p&gt;
&lt;!-- end paragraph 3675e654-ad6d-4ff7-845a-b9ca56f78d6d--&gt;
&lt;!-- begin paragraph 9ace829e-296b-48b0-a708-fb1f11709f86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9ace829e-296b-48b0-a708-fb1f11709f86&quot;&gt;C&amp;#39;est l&amp;#39;UVCI qui assure l&amp;#39;unicité de l&amp;#39;acte de vaccination (ou du test ou de l&amp;#39;infection). Il n&amp;#39;est pas exploitable directement à partir d&amp;#39;un scanner sans connexion. En effet, il ne doit pas contenir de données personnelles. Par contre, il peut agir comme une clé étrangère et permet de retrouver des informations complémentaires sur une autre base de données. On s&amp;#39;attend à ce que cette base soit gérée par un organisme de Sécurité sociale et ne soit pas si facilement accessible (!?).&lt;/p&gt;
&lt;!-- end paragraph 9ace829e-296b-48b0-a708-fb1f11709f86--&gt;
&lt;!-- begin heading_1 0770009e-9830-4de5-bfab-4556ad182adf--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-0770009e-9830-4de5-bfab-4556ad182adf&quot;&gt;Sécurité&lt;/h2&gt;
&lt;!-- end heading_1 0770009e-9830-4de5-bfab-4556ad182adf--&gt;
&lt;!-- begin paragraph 78ffa9d1-847a-4b86-ac42-93e91580222b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78ffa9d1-847a-4b86-ac42-93e91580222b&quot;&gt;À ce niveau, nous n’avons pas encore vérifié si le &lt;i&gt;pass&lt;/i&gt; est valide. Nous avons accès à certaines informations personnelles, mais nous n’avons pas montré si ces informations sont certifiées (y a-t-il un cachet, un tampon, une signature d’une autorité reconnue ?) et nous ne savons pas si les informations sanitaires sont valides.&lt;/p&gt;
&lt;!-- end paragraph 78ffa9d1-847a-4b86-ac42-93e91580222b--&gt;
&lt;!-- begin paragraph c61f5f73-2e6c-406d-b2fc-fd2b7a8031f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c61f5f73-2e6c-406d-b2fc-fd2b7a8031f3&quot;&gt;Pour cela, il faut s’intéresser au reste de la structure COSE. Dans la partie &lt;code&gt;protected&lt;/code&gt;, qui contient le binaire d&amp;#39;une structure CBOR, nous allons trouver deux champs :&lt;/p&gt;
&lt;!-- end paragraph c61f5f73-2e6c-406d-b2fc-fd2b7a8031f3--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;1&lt;/code&gt; représentant l&amp;#39;algorithme de chiffrement (ici &lt;code&gt;-7&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;&lt;code&gt;4&lt;/code&gt; représentant le &lt;code&gt;kid&lt;/code&gt; ou KeyID.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 5d957e01-dd43-4f5d-af5c-fc75c4be7cd6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d957e01-dd43-4f5d-af5c-fc75c4be7cd6&quot;&gt;Le kid est un identifiant en base 64 faisant référence à une entrée dans une base de données des organismes délivrant le certificat numérique (organisme administratif de Sécurité sociale, hôpital, centre de test...). Cette base de données contient des informations au format LDAP concernant l&amp;#39;organisme. Elle contient la &lt;b&gt;clé publique&lt;/b&gt; de ces organismes. Chaque pays a une copie de cette base de données (&lt;a href=&quot;https://github.com/lovasoa/sanipasse/blob/master/src/assets/Digital_Green_Certificate_Signing_Keys.json&quot;&gt;une copie&lt;/a&gt; est disponible dans le projet &lt;a href=&quot;https://github.com/lovasoa/sanipasse&quot;&gt;sanipasse&lt;/a&gt;).&lt;/p&gt;
&lt;!-- end paragraph 5d957e01-dd43-4f5d-af5c-fc75c4be7cd6--&gt;
&lt;!-- begin paragraph aa28f058-4a41-4106-884a-dc9c44f4b871--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aa28f058-4a41-4106-884a-dc9c44f4b871&quot;&gt;La partie signature de la structure récupérée se base sur algorithme de chiffrement utilisant un système de clé privée / clé publique. &lt;b&gt;La signature est générée&lt;/b&gt; à partir du contenu de la structure COSE, de la &lt;b&gt;clé privée&lt;/b&gt; de l&amp;#39;organisme faisant autorité et d&amp;#39;un sel &lt;code&gt;&amp;quot;Signature1&amp;quot;&lt;/code&gt; (appelé &lt;i&gt;context string&lt;/i&gt;).  L&amp;#39;algorithme utilisé est ECDSA avec la fonction de hachage SHA-256. &lt;b&gt;La vérification&lt;/b&gt; se fait en utilisant la &lt;b&gt;clé publique&lt;/b&gt;, le contenu COSE, le sel et la signature. L&amp;#39;algorithme indique si l&amp;#39;ensemble est cohérent. Si la moindre information est changée dans la structure COSE ou dans la signature, la vérification échouera.&lt;/p&gt;
&lt;!-- end paragraph aa28f058-4a41-4106-884a-dc9c44f4b871--&gt;
&lt;!-- begin paragraph 6cb6c229-4565-4576-9c8c-e639f323b854--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6cb6c229-4565-4576-9c8c-e639f323b854&quot;&gt;Cette approche permet de ne pas communiquer d&amp;#39;information lors d&amp;#39;une vérification. Seule la clé de l&amp;#39;organisme (le kid) est transmis pour récupérer la clé publique. D&amp;#39;ailleurs, il est possible de conserver la base des organismes au sein du système de vérification.&lt;/p&gt;
&lt;!-- end paragraph 6cb6c229-4565-4576-9c8c-e639f323b854--&gt;
&lt;!-- begin heading_2 e322e933-b3a0-4cb9-8cac-2f99bb2bddfc--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e322e933-b3a0-4cb9-8cac-2f99bb2bddfc&quot;&gt;Fiabilité de la signature&lt;/h3&gt;
&lt;!-- end heading_2 e322e933-b3a0-4cb9-8cac-2f99bb2bddfc--&gt;
&lt;!-- begin paragraph e2dc016f-cd7d-4f2e-8512-88fada6e44aa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2dc016f-cd7d-4f2e-8512-88fada6e44aa&quot;&gt;Tant que l&amp;#39;algorithme utilisé est considéré comme suffisamment sûr pour signer des documents et vérifier ces signatures, il n&amp;#39;y a pas de faille. Par contre, c&amp;#39;est l&amp;#39;employé qui crée la liaison entre l&amp;#39;acte médical et le certificat. Il a ainsi la possibilité de délivrer certificat sans acte médical s&amp;#39;il le souhaite. Le certificat est alors techniquement valide et passera tous les contrôles, mais il est faux vis-à-vis de la loi. C&amp;#39;est ce qui est arrivé &lt;a href=&quot;https://www.francetvinfo.fr/sante/maladie/coronavirus/vaccin/covid-19-six-personnes-mises-en-examen-pour-trafic-de-faux-certificats-de-vaccination_4705981.html&quot;&gt;durant le mois de juillet&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph e2dc016f-cd7d-4f2e-8512-88fada6e44aa--&gt;
&lt;!-- begin callout bd7f1890-8173-42f0-b59a-6d68f84fa02d--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-bd7f1890-8173-42f0-b59a-6d68f84fa02d&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;⛔&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Pour rappel, posséder un faux certificat (tout comme n&amp;#39;importe quel faux documents) est punie par de l&amp;#39;emprisonnement et une amende (Code Pénal, &lt;a href=&quot;https://www.legifrance.gouv.fr/codes/id/LEGISCTA000006149854/2020-11-25/&quot;&gt;article 441-1 à 441-12&lt;/a&gt;), pouvant impliquer des peines complémentaires. La peine est plus lourde dans le cas de son utilisation. Il en va de même pour la personne qui crée un faux certificat.&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout bd7f1890-8173-42f0-b59a-6d68f84fa02d--&gt;
&lt;!-- begin heading_1 1e9d2434-b952-4068-8e6b-6d3f090c6a90--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1e9d2434-b952-4068-8e6b-6d3f090c6a90&quot;&gt;Systèmes existants&lt;/h2&gt;
&lt;!-- end heading_1 1e9d2434-b952-4068-8e6b-6d3f090c6a90--&gt;
&lt;!-- begin paragraph d1fe13d7-11fb-4030-8487-86965b71242e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d1fe13d7-11fb-4030-8487-86965b71242e&quot;&gt;On peut s’intéresser à faire un rapprochement entre l’EUDCC d’un côté et le numéro de Sécurité sociale et le carnet de santé de l’autre.&lt;/p&gt;
&lt;!-- end paragraph d1fe13d7-11fb-4030-8487-86965b71242e--&gt;
&lt;!-- begin paragraph 15a9cc09-a4c7-4302-bb03-92b1676f0dbc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-15a9cc09-a4c7-4302-bb03-92b1676f0dbc&quot;&gt;Le numéro de Sécurité sociale est une invention de René Carmille (par la suite résistant français, mort à Dachau) adoptée par la Sécurité sociale en 1946. Certaines utilisations controversées de ce numéro ont conduit à la création de la loi Informatique et Liberté en 1978, ainsi qu’à la création de la CNIL. Le numéro de Sécurité sociale est un numéro qui permet d’identifier indirectement une personne. Il ne contient ni nom et prénom, ni dossier médical, mais il permet de connaître le sexe d’une personne, sa date de naissance, sa commune de naissance. Il est unique à chaque personne enregistrée au sein de la Sécurité sociale.&lt;/p&gt;
&lt;!-- end paragraph 15a9cc09-a4c7-4302-bb03-92b1676f0dbc--&gt;
&lt;!-- begin paragraph b7be9d36-b9bd-4dd4-ae94-44fb33128150--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b7be9d36-b9bd-4dd4-ae94-44fb33128150&quot;&gt;Le carnet de santé est créé en 1938 par l’artiste Louise Hervieu. Il est adopté un an plus tard par le gouvernement et est rendu obligatoire en 1945. Son contenu a évolué au cours du temps. Mais il est avant tout personnel et soumis au secret professionnel. Ce carnet contient l’identification de la personne, son numéro de Sécurité sociale, un suivi détaillé de la santé de la personne durant son enfance, ainsi que l’ensemble des vaccins réalisés.&lt;/p&gt;
&lt;!-- end paragraph b7be9d36-b9bd-4dd4-ae94-44fb33128150--&gt;
&lt;!-- begin paragraph a80c0cfc-8fe2-4676-b0ac-7901e55b772b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a80c0cfc-8fe2-4676-b0ac-7901e55b772b&quot;&gt;Si votre organisme de Sécurité sociale ne connait pas le contenu de votre carnet de santé, elle voit régulièrement passer vos actes médicaux, afin de vous fournir les remboursements qui vous sont dus. Ainsi que votre mutuelle, si vous en avez une. Ces actes médicaux ne sont pour la plupart pas inscrit dans votre carnet de santé. Ces organismes possèdent même de plus de détails en cas d&amp;#39;arrêt de travail.&lt;/p&gt;
&lt;!-- end paragraph a80c0cfc-8fe2-4676-b0ac-7901e55b772b--&gt;
&lt;!-- begin paragraph f26e1bd2-5cde-4190-8003-1312a34f1611--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f26e1bd2-5cde-4190-8003-1312a34f1611&quot;&gt;Mais en dehors de votre organisme de Sécurité sociale, aucun autre organisme ne possède d&amp;#39;information santé… À part, votre mutuelle, votre smartphone et ce qu&amp;#39;il peut y avoir derrière.&lt;/p&gt;
&lt;!-- end paragraph f26e1bd2-5cde-4190-8003-1312a34f1611--&gt;
&lt;!-- begin heading_1 f54f4896-f5e3-49d3-aa21-d14119bf516c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f54f4896-f5e3-49d3-aa21-d14119bf516c&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 f54f4896-f5e3-49d3-aa21-d14119bf516c--&gt;
&lt;!-- begin paragraph a57fdcca-1392-40b6-8602-f9c0f11ce24a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a57fdcca-1392-40b6-8602-f9c0f11ce24a&quot;&gt;Le passe sanitaire est devenu depuis le 1er juillet 2021 un certificat européen de vaccination ou non-infection, qui pour le moment s&amp;#39;applique à la Covid-19. Il apparaît sous forme de QR code qui se décode en se basant sur des technologies standardisées ou clairement documentées.&lt;/p&gt;
&lt;!-- end paragraph a57fdcca-1392-40b6-8602-f9c0f11ce24a--&gt;
&lt;!-- begin paragraph 0fa23fb9-3cb9-4916-b2ce-cd0e7dd82dc2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0fa23fb9-3cb9-4916-b2ce-cd0e7dd82dc2&quot;&gt;Cependant, le certificat contient certaines données personnelles qui sont portées à la connaissance des personnes physiques ou morales en charge de la vérification. Elles ne suffisent pas pour vous identifier complètement, mais elles ont néanmoins un fort potentiel.&lt;/p&gt;
&lt;!-- end paragraph 0fa23fb9-3cb9-4916-b2ce-cd0e7dd82dc2--&gt;
&lt;!-- begin paragraph bfacb463-fc2e-49fa-99fd-166de7a29f69--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bfacb463-fc2e-49fa-99fd-166de7a29f69&quot;&gt;Au niveau sécurité, la falsification d&amp;#39;un certificat est très difficile sans avoir la clé privée utilisée par signer le document. Les récents événements ont montré que le manque de loyauté du personnel ayant accès à la clé privée est un risque à prendre en compte dans ce système, même si ce sujet est pris en compte par le législateur.&lt;/p&gt;
&lt;!-- end paragraph bfacb463-fc2e-49fa-99fd-166de7a29f69--&gt;
&lt;!-- begin paragraph 46d819f3-94d5-42ea-99cf-f8aed4cf04d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-46d819f3-94d5-42ea-99cf-f8aed4cf04d1&quot;&gt;Il reste que le certificat Covid européen nous met face à un niveau d&amp;#39;échange de données personnelles, en particulier de données de santé, que nous n&amp;#39;avons pas encore connu dans notre société moderne. Les circonstances étant exceptionnelles, il serait alors normal d&amp;#39;accepter ce dispositif.&lt;/p&gt;
&lt;!-- end paragraph 46d819f3-94d5-42ea-99cf-f8aed4cf04d1--&gt;
&lt;!-- begin paragraph fd9eccb6-4970-44b6-8b62-736ca66bcdb4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fd9eccb6-4970-44b6-8b62-736ca66bcdb4&quot;&gt;&lt;i&gt;Il y a un message caché dans le QR code du certificat ci-dessous…&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph fd9eccb6-4970-44b6-8b62-736ca66bcdb4--&gt;&lt;div class=&quot;block-embed level-0&quot;&gt;&lt;div class=&quot;player twitter&quot;&gt;&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/dgageot/status/1419332959606280193?s=20&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin heading_1 e41ecb6a-2254-4d83-863f-310fd4ab87c7--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e41ecb6a-2254-4d83-863f-310fd4ab87c7&quot;&gt;Lien&lt;/h2&gt;
&lt;!-- end heading_1 e41ecb6a-2254-4d83-863f-310fd4ab87c7--&gt;
&lt;!-- begin paragraph f54fc99f-6518-4721-9d4b-20c9e77c2d99--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f54fc99f-6518-4721-9d4b-20c9e77c2d99&quot;&gt;&lt;i&gt;European eHealth network - Digital Covid Certificate Coordination&lt;/i&gt; est une organisation Github qui regroupe l&amp;#39;ensemble des projets autour du certificat Covid européen, afin de proposer aux États membres une plateforme commune sur ce sujet : &lt;a href=&quot;https://github.com/ehn-dcc-development&quot;&gt;https://github.com/ehn-dcc-development&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph f54fc99f-6518-4721-9d4b-20c9e77c2d99--&gt;
&lt;!-- begin paragraph 856986fc-2129-4cc6-b84b-646acb026dc5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-856986fc-2129-4cc6-b84b-646acb026dc5&quot;&gt;Le lien précédent est à compléter avec cette organisation Github autour de l&amp;#39;EUDCC  : &lt;a href=&quot;https://github.com/eu-digital-green-certificates&quot;&gt;https://github.com/eu-digital-green-certificates&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 856986fc-2129-4cc6-b84b-646acb026dc5--&gt;
&lt;!-- begin paragraph 273a00cf-e505-4372-9c07-59320cc62152--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-273a00cf-e505-4372-9c07-59320cc62152&quot;&gt;Le projet &lt;a href=&quot;https://github.com/lovasoa/sanipasse&quot;&gt;sanipasse&lt;/a&gt; de &lt;a href=&quot;https://github.com/lovasoa&quot;&gt;Ophir LOJKINE&lt;/a&gt; propose un service hébergeable permettant de vérifier des passes sanitaires et autres certificats Covid européens.&lt;/p&gt;
&lt;!-- end paragraph 273a00cf-e505-4372-9c07-59320cc62152--&gt;
&lt;!-- begin paragraph 32d04fb5-40d1-4a1f-95c1-d6b21815c7ce--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-32d04fb5-40d1-4a1f-95c1-d6b21815c7ce&quot;&gt;Le thread Twitter de &lt;a href=&quot;https://twitter.com/gilbsgilbs&quot;&gt;@gilbsgilbs&lt;/a&gt; ci-dessous permet de compléter les explications de l&amp;#39;article.&lt;/p&gt;
&lt;!-- end paragraph 32d04fb5-40d1-4a1f-95c1-d6b21815c7ce--&gt;&lt;div class=&quot;block-embed level-0&quot;&gt;&lt;div class=&quot;player twitter&quot;&gt;&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/gilbsgilbs/status/1409602942441648129?s=20&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:6a17e00c504b49e0a7feb914e4d8ab85</id>
    <title>Sortie de gâteau pour ZIO 1.0</title>
    <updated>2021-06-30T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/sortie-de-gateau-pour-zio-1-0.html"/>
    <!--summary -->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="ZIO"></category>    <content type="html">
&lt;!-- begin callout e693812a-23d9-4707-b282-998be30897a6--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-e693812a-23d9-4707-b282-998be30897a6&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;⚠️&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;Attention, on est maintenant en ZIO 2.0, la gestion de l’envi&lt;b&gt;R&lt;/b&gt;onement dans &lt;code&gt;ZIO[-R, +E, +A]&lt;/code&gt; a changé.&lt;br /&gt;
&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Avant la RC-18, on avait une sorte de cake pattern pour gérer R (et donc la composition était complexe)&lt;/li&gt;&lt;li&gt;Avec la RC-18 / ZIO 1.0, on a eu Has qui a permis de composer facilement, sans encodage, le set hétérogène des ressources que l’IO va utiliser (dépendances)&lt;/li&gt;&lt;li&gt;Avec ZIO 2.0, on a un système beaucoup plus à plat pour gérer l’enviRonement, qui est beaucoup plus intégré. Par exemple, si on a besoin d’une DB, et d’une API, on va avoir un &lt;code&gt;ZIO[DB with API, E, A]&lt;/code&gt;, au lieu de &lt;code&gt;ZIO[Has[DB] with Has[API], E, A]&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout e693812a-23d9-4707-b282-998be30897a6--&gt;
&lt;!-- begin divider 4929045f-5535-4796-addc-dcfcad5ae47b--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-4929045f-5535-4796-addc-dcfcad5ae47b&quot;&gt;
&lt;!-- end divider 4929045f-5535-4796-addc-dcfcad5ae47b--&gt;
&lt;!-- begin paragraph 39f76322-239c-4848-9baf-04143f102b76--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-39f76322-239c-4848-9baf-04143f102b76&quot;&gt;Parlons un peu de &lt;code&gt;Has&lt;/code&gt; et du &lt;code&gt;Zlayer&lt;/code&gt;, ZIO approche bientôt la version 1.0, néanmoins beaucoup de choses ont changé entre le RC17 et la RC18. &lt;/p&gt;
&lt;!-- end paragraph 39f76322-239c-4848-9baf-04143f102b76--&gt;
&lt;!-- begin heading_1 cd586d7d-f78c-4a38-a5b4-b29253006852--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-cd586d7d-f78c-4a38-a5b4-b29253006852&quot;&gt;ZIO c&amp;#39;est le Future&lt;/h2&gt;
&lt;!-- end heading_1 cd586d7d-f78c-4a38-a5b4-b29253006852--&gt;
&lt;!-- begin paragraph 395c9acf-c951-4442-9fb6-ad58161099c4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-395c9acf-c951-4442-9fb6-ad58161099c4&quot;&gt;L&amp;#39;objectif principal de ZIO est de fournir l&amp;#39;outil qui permet de faire de la programmation fonctionnelle pleinement.&lt;/p&gt;
&lt;!-- end paragraph 395c9acf-c951-4442-9fb6-ad58161099c4--&gt;
&lt;!-- begin paragraph 6e2f601b-cfae-45ec-a353-7674d55bb0b3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e2f601b-cfae-45ec-a353-7674d55bb0b3&quot;&gt;Une fonction &lt;code&gt;A ⇒ B&lt;/code&gt; se doit d&amp;#39;être pure, c&amp;#39;est-à-dire :&lt;/p&gt;
&lt;!-- end paragraph 6e2f601b-cfae-45ec-a353-7674d55bb0b3--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;être complète&lt;/li&gt;&lt;li&gt;être déterministe&lt;/li&gt;&lt;li&gt;sans effet de bord&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 9289de69-3683-4790-a835-420162750c3d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9289de69-3683-4790-a835-420162750c3d&quot;&gt;Les outils classiques pour résoudre ce genre de problèmes sont : &lt;/p&gt;
&lt;!-- end paragraph 9289de69-3683-4790-a835-420162750c3d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;Either&lt;/code&gt;, qui offre une sortie alternative pour être complète, &lt;code&gt;A ⇒ Either[E, B]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;State&lt;/code&gt;, qui permet de représenter de manipuler qui ont besoin d&amp;#39;un état : &lt;code&gt;(S, A) ⇒ (S, B)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reader&lt;/code&gt;, qui permet de manipuler des fonctions qui ont besoin d&amp;#39;une dépendance : &lt;code&gt;(R, A) ⇒ B&lt;/code&gt; (ou &lt;code&gt;A ⇒ R ⇒ B&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;IO&lt;/code&gt; ou &lt;code&gt;Task&lt;/code&gt;, qui permet de marquer une valeur comme étant disponible que si on exécute le programme : &lt;code&gt;A ⇒ IO[B]&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 5e381052-b08e-4704-8156-dbf653fb6bd3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e381052-b08e-4704-8156-dbf653fb6bd3&quot;&gt;Ce qui donne une pile assez classique : &lt;/p&gt;
&lt;!-- end paragraph 5e381052-b08e-4704-8156-dbf653fb6bd3--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;A ⇒ IO[Reader[R, State[S, Either[E, B]]]]&lt;/code&gt; !&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph b1ac3293-2f7f-4938-88a7-88bda7b18055--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b1ac3293-2f7f-4938-88a7-88bda7b18055&quot;&gt;Sans parler du reste pour faire de la programmation concurrente.&lt;/p&gt;
&lt;!-- end paragraph b1ac3293-2f7f-4938-88a7-88bda7b18055--&gt;
&lt;!-- begin paragraph 8af4eebc-d8ae-4e8f-a4c8-2d101d2d2153--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8af4eebc-d8ae-4e8f-a4c8-2d101d2d2153&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8af4eebc-d8ae-4e8f-a4c8-2d101d2d2153--&gt;
&lt;!-- begin paragraph c20ee7f9-3834-43a0-b62f-b99dd2e148e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c20ee7f9-3834-43a0-b62f-b99dd2e148e7&quot;&gt;Finalement &lt;code&gt;A ⇒ ZIO[R, E, B]&lt;/code&gt;  est une réponse moderne, en un seul type, à l&amp;#39;équivalent de &lt;code&gt;A ⇒ Reader[R, IO[Either[E, B]]]&lt;/code&gt;, mais avec un type et les combinateurs qui vont avec ! Qui est efficace pour représenter les différents cas limites : &lt;/p&gt;
&lt;!-- end paragraph c20ee7f9-3834-43a0-b62f-b99dd2e148e7--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;B&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;IO[B]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;IO[Either[Throwable, B]]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;R =&amp;gt; IO[Either[E, B]]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;(R, IO[Reader[R, Either[E, B]]])&lt;/code&gt;   &lt;i&gt;// Provide&lt;/i&gt; &lt;/li&gt;&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin divider e5695fab-7229-42fd-8237-22de309e7033--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-e5695fab-7229-42fd-8237-22de309e7033&quot;&gt;
&lt;!-- end divider e5695fab-7229-42fd-8237-22de309e7033--&gt;
&lt;!-- begin paragraph 6a8ef720-ae97-4352-9707-19329d0f1930--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a8ef720-ae97-4352-9707-19329d0f1930&quot;&gt;Pourquoi intégrer aussi le &lt;code&gt;Reader&lt;/code&gt; ? ZIO fournit à la base pour les erreurs dans &lt;code&gt;IO[+E, +B]&lt;/code&gt; , au lieu de faire &lt;code&gt;IO[Either[E, B]]&lt;/code&gt;, cela a permis de mieux gérer les erreurs et l&amp;#39;absence d&amp;#39;erreur. Néanmoins dans les cas d&amp;#39;usage classique, on devait continuer à se trainer des fonctions tout le temps. &lt;/p&gt;
&lt;!-- end paragraph 6a8ef720-ae97-4352-9707-19329d0f1930--&gt;
&lt;!-- begin paragraph edb4e3e4-6cff-485e-ac84-0bbc40ce0289--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-edb4e3e4-6cff-485e-ac84-0bbc40ce0289&quot;&gt;L&amp;#39;année dernière ZIO est passé de &lt;/p&gt;
&lt;!-- end paragraph edb4e3e4-6cff-485e-ac84-0bbc40ce0289--&gt;
&lt;!-- begin code 1e2527bc-0258-4765-a8cf-b2269adcf5c5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1e2527bc-0258-4765-a8cf-b2269adcf5c5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def println(line: String): Console =&amp;gt; IO[Nothing, Unit]
// Console : Il faut fournir une Console
// Nothing : Il n&amp;#39;y a pas d&amp;#39;erreur attendue
// Unit : Il n&amp;#39;a a pas de valeur de retour attendue&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1e2527bc-0258-4765-a8cf-b2269adcf5c5--&gt;
&lt;!-- begin paragraph fa117e83-1672-41e8-a2f8-a316e63e1324--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa117e83-1672-41e8-a2f8-a316e63e1324&quot;&gt;à&lt;/p&gt;
&lt;!-- end paragraph fa117e83-1672-41e8-a2f8-a316e63e1324--&gt;
&lt;!-- begin code a1e0bd1f-fc0f-42d1-8ddb-fe4bf25ed5e2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a1e0bd1f-fc0f-42d1-8ddb-fe4bf25ed5e2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def println(line: String): ZIO[Console, Nothing, Unit]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a1e0bd1f-fc0f-42d1-8ddb-fe4bf25ed5e2--&gt;
&lt;!-- begin paragraph 27a71c6c-975f-4fd1-8d54-523eadb5b549--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27a71c6c-975f-4fd1-8d54-523eadb5b549&quot;&gt;Comme à l&amp;#39;arrivée du rasoir à 5 lames, on s&amp;#39;est demandés si ZIO n&amp;#39;allait pas ajouter un nouveau type chaque année. Pour l&amp;#39;instant ce n&amp;#39;est pas le cas, néanmoins il y a du changement.&lt;/p&gt;
&lt;!-- end paragraph 27a71c6c-975f-4fd1-8d54-523eadb5b549--&gt;
&lt;!-- begin heading_1 8b33f89d-abff-47b0-bc91-e30e45159e93--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-8b33f89d-abff-47b0-bc91-e30e45159e93&quot;&gt;Composition&lt;/h2&gt;
&lt;!-- end heading_1 8b33f89d-abff-47b0-bc91-e30e45159e93--&gt;
&lt;!-- begin paragraph d5115009-9282-4049-9fb6-baa557210b14--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d5115009-9282-4049-9fb6-baa557210b14&quot;&gt;Pour la composition, ZIO a choisi de rester très proche de ce qui est offert par le design de Scala 2, en se basant sur la covariance et la contravariance. On a un &lt;code&gt;ZIO[-R, +E, +A]&lt;/code&gt; qui a même variance qu&amp;#39;une fonction &lt;code&gt;R =&amp;gt; Either[E, A]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph d5115009-9282-4049-9fb6-baa557210b14--&gt;
&lt;!-- begin paragraph 313479a2-2cfc-40dc-93c9-c4f6355e790a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-313479a2-2cfc-40dc-93c9-c4f6355e790a&quot;&gt;Pour la composition, on utilise flatMap qui repose sur le mécanisme de variance : &lt;/p&gt;
&lt;!-- end paragraph 313479a2-2cfc-40dc-93c9-c4f6355e790a--&gt;
&lt;!-- begin code 478a951f-c09f-4020-be89-f1643915a11f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-478a951f-c09f-4020-be89-f1643915a11f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait ZIO[-R, +E, +A] {
  def flatMap[R1 &amp;lt;: R, E1 &amp;gt;: E, B](k: A =&amp;gt; ZIO[R1, E1, B]): ZIO[R1, E1, B]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 478a951f-c09f-4020-be89-f1643915a11f--&gt;
&lt;!-- begin paragraph c66592cb-db81-4d73-856b-7149d66ccc78--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c66592cb-db81-4d73-856b-7149d66ccc78&quot;&gt;Si on n&amp;#39;avait pas cette variance, on aurait des compositions un peu étranges et besogneuses.&lt;/p&gt;
&lt;!-- end paragraph c66592cb-db81-4d73-856b-7149d66ccc78--&gt;
&lt;!-- begin heading_2 10511b0c-2176-498d-a92e-bbbf51fcbaf6--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-10511b0c-2176-498d-a92e-bbbf51fcbaf6&quot;&gt;Le error channel&lt;/h3&gt;
&lt;!-- end heading_2 10511b0c-2176-498d-a92e-bbbf51fcbaf6--&gt;
&lt;!-- begin paragraph a80a1234-c166-444f-8946-c9f04ef38c11--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a80a1234-c166-444f-8946-c9f04ef38c11&quot;&gt;Avec les fonctions :&lt;/p&gt;
&lt;!-- end paragraph a80a1234-c166-444f-8946-c9f04ef38c11--&gt;
&lt;!-- begin code d3b9f5c3-8468-4a07-9a32-ebab8472e207--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d3b9f5c3-8468-4a07-9a32-ebab8472e207&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def get: IO[E1, A]
def set(a: A): IO[E2, Unit]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d3b9f5c3-8468-4a07-9a32-ebab8472e207--&gt;
&lt;!-- begin paragraph 5720110c-cc39-4564-a25b-ac9c4bd28f5d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5720110c-cc39-4564-a25b-ac9c4bd28f5d&quot;&gt;Si on compose sans utiliser le mécanisme de variance, on obtient : &lt;/p&gt;
&lt;!-- end paragraph 5720110c-cc39-4564-a25b-ac9c4bd28f5d--&gt;
&lt;!-- begin code 39baa256-5255-460d-b1cb-9d19fc418fcb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-39baa256-5255-460d-b1cb-9d19fc418fcb&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val prg: IO[E1 Either E2, Unit] = for {
  a &amp;lt;- get
  _ &amp;lt;- set(a)
} yield {}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 39baa256-5255-460d-b1cb-9d19fc418fcb--&gt;
&lt;!-- begin paragraph f6a6d613-95a4-4abd-86ad-62cd3e1d42ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f6a6d613-95a4-4abd-86ad-62cd3e1d42ee&quot;&gt;Plus on compose, plus on va avoir un type complexe sur l&amp;#39;erreur qui va devenir difficilement gérable. Ici on a &lt;code&gt;Either[E1, E2]&lt;/code&gt; (que l&amp;#39;on peut écrire &lt;code&gt;E1 Either E2&lt;/code&gt;), mais avec plusieurs erreurs obtient des &lt;code&gt;Either[E1, Either[E2, Either[E3, ...]]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph f6a6d613-95a4-4abd-86ad-62cd3e1d42ee--&gt;
&lt;!-- begin paragraph ae026470-be1c-4839-860b-e6eb0bb4a3df--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ae026470-be1c-4839-860b-e6eb0bb4a3df&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ae026470-be1c-4839-860b-e6eb0bb4a3df--&gt;
&lt;!-- begin paragraph 25b9f5ce-e6ae-47e2-a2a7-7a7c5375887a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-25b9f5ce-e6ae-47e2-a2a7-7a7c5375887a&quot;&gt;La composition sur l&amp;#39;erreur se fait en utilisant la covariance : &lt;/p&gt;
&lt;!-- end paragraph 25b9f5ce-e6ae-47e2-a2a7-7a7c5375887a--&gt;
&lt;!-- begin code daf9d334-b521-4ac4-942f-3d88e21d7dda--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-daf9d334-b521-4ac4-942f-3d88e21d7dda&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait IO[+E, +A] {
  def flatMap[B, EE &amp;gt;: E](f: A =&amp;gt; IO[EE, B]): IO[EE, B]
  //def flatMap[B, E2](f: A =&amp;gt; IO[E2, B]): IO[Either[E, E2], B]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code daf9d334-b521-4ac4-942f-3d88e21d7dda--&gt;
&lt;!-- begin paragraph f08a01ac-6d78-446f-a811-72f244dc5c4e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f08a01ac-6d78-446f-a811-72f244dc5c4e&quot;&gt;Dans notre cas, le compilateur Scala va chercher un type EE, qui est un supertype de E1 et de E2.&lt;/p&gt;
&lt;!-- end paragraph f08a01ac-6d78-446f-a811-72f244dc5c4e--&gt;
&lt;!-- begin paragraph 0034af70-29d2-4f80-b0be-7e4620b48fb3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0034af70-29d2-4f80-b0be-7e4620b48fb3&quot;&gt;Lorsque l&amp;#39;on définit les erreurs avec un super type (ici KVSError), le compilateur fait le reste.&lt;/p&gt;
&lt;!-- end paragraph 0034af70-29d2-4f80-b0be-7e4620b48fb3--&gt;
&lt;!-- begin code 366904f1-0344-4801-849d-fff32dc4efad--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-366904f1-0344-4801-849d-fff32dc4efad&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;sealed abstract class KVSError(msg:String) extends Throwable(msg)

class E1 extends KVSError(&amp;quot;e1 error&amp;quot;)

class E2 extends KVSError(&amp;quot;e2 error&amp;quot;)

class E3 extends KVSError(&amp;quot;e3 error&amp;quot;)


val prg: IO[KVSError, Unit] = for {
  a &amp;lt;- get
  _ &amp;lt;- set(a)
} yield {}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 366904f1-0344-4801-849d-fff32dc4efad--&gt;
&lt;!-- begin paragraph b22451c7-22a9-4c9f-ad5c-ec811331fefb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b22451c7-22a9-4c9f-ad5c-ec811331fefb&quot;&gt;Pour avoir soit &lt;code&gt;E1&lt;/code&gt;, soit &lt;code&gt;E2&lt;/code&gt;, le compilateur trouve le type juste au-dessus, une solution qui est moins précise que &lt;code&gt;Either[E1, E2]&lt;/code&gt;, mais beaucoup plus utilisable pour faire de la composition.&lt;/p&gt;
&lt;!-- end paragraph b22451c7-22a9-4c9f-ad5c-ec811331fefb--&gt;
&lt;!-- begin code 5b400401-f843-48d7-8b83-fea1742fd932--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5b400401-f843-48d7-8b83-fea1742fd932&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def toKVSError(e: Either[E1, E2]): KVSError = e.merge

//Ici on est moins précis, KVSError est un supertype de E3 aussi.
def toEither(e: KVSError): Option[Either[E1, E2]] = {
  e match {
    case e1: E1 =&amp;gt; Some(Left(e1))
    case e2: E2 =&amp;gt; Some(Right(e2))
    case _      =&amp;gt; None
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5b400401-f843-48d7-8b83-fea1742fd932--&gt;
&lt;!-- begin heading_2 e34bcc1a-f6c6-4ab6-8836-ac5e0943d631--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e34bcc1a-f6c6-4ab6-8836-ac5e0943d631&quot;&gt;R (l&amp;#39;injection de dépendance)&lt;/h3&gt;
&lt;!-- end heading_2 e34bcc1a-f6c6-4ab6-8836-ac5e0943d631--&gt;
&lt;!-- begin paragraph 8ac6094a-1167-4fc0-8c54-8d82e3caaf3d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8ac6094a-1167-4fc0-8c54-8d82e3caaf3d&quot;&gt;Pour R, il va se passer relativement la même chose. Avec les fonctions suivantes. Sans le &lt;code&gt;R&lt;/code&gt; dans &lt;code&gt;ZIO[-R, +E, +A]&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 8ac6094a-1167-4fc0-8c54-8d82e3caaf3d--&gt;
&lt;!-- begin code 0b32bb00-3046-4e88-88b1-e95df93ebc97--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0b32bb00-3046-4e88-88b1-e95df93ebc97&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;type IO[E, A] = ZIO[Any, E, A]
type UIO[A]   = IO[Nothing, A]

def currentTime(unit: TimeUnit): Clock =&amp;gt; UIO[Long]
def putStrLn(line: String): Console =&amp;gt; UIO[Unit]

def printTime: (Clock, Console) =&amp;gt; UIO[Unit] = 
  (clock, console) =&amp;gt; for {
    t &amp;lt;- currentTime(TimeUnit.SECONDS)(clock)
    _ &amp;lt;- putStrLn(t + &amp;quot;s&amp;quot;)(console)
  } yield {}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0b32bb00-3046-4e88-88b1-e95df93ebc97--&gt;
&lt;!-- begin paragraph 4ac614d9-6dab-4082-ab27-bf6728318d57--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4ac614d9-6dab-4082-ab27-bf6728318d57&quot;&gt;ou avec les implicites&lt;/p&gt;
&lt;!-- end paragraph 4ac614d9-6dab-4082-ab27-bf6728318d57--&gt;
&lt;!-- begin code 1ce3a741-852a-46f3-a0b4-6ab9f0c4f7e2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1ce3a741-852a-46f3-a0b4-6ab9f0c4f7e2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;type IO[E, A] = ZIO[Any, E, A]
type UIO[A]   = IO[Nothing, A]

def currentTime(unit: TimeUnit)(implicit clock: Clock): UIO[Long]
def putStrLn(line: String)(implicit console: Console): UIO[Unit]

def printTime(implicit clock: Clock, console: Console): UIO[Unit] = 
  for {
    t &amp;lt;- currentTime(TimeUnit.SECONDS)
    _ &amp;lt;- putStrLn(t + &amp;quot;s&amp;quot;)
  } yield {}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1ce3a741-852a-46f3-a0b4-6ab9f0c4f7e2--&gt;
&lt;!-- begin paragraph b672a043-5caa-4715-9614-5948e374e506--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b672a043-5caa-4715-9614-5948e374e506&quot;&gt;Avec le nouveau paramètre, on va avoir : &lt;/p&gt;
&lt;!-- end paragraph b672a043-5caa-4715-9614-5948e374e506--&gt;
&lt;!-- begin code 296edb78-215c-4104-90fc-f1eee22b352c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-296edb78-215c-4104-90fc-f1eee22b352c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;type IO[E, A]   = ZIO[Any, E, A]
type UIO[A]     = IO[Nothing, A]
//URIO est un effet qui dépend de R pour produire sans erreur un A
type URIO[R, A] = ZIO[R, Nothing, A]

def currentTime(unit: TimeUnit): URIO[Clock, Long]
def putStrLn(line: String): URIO[Console, Unit]

def printTime: URIO[Clock with Console, Unit] = 
  for {
    t &amp;lt;- currentTime(TimeUnit.SECONDS)
    _ &amp;lt;- putStrLn(t + &amp;quot;s&amp;quot;)
  } yield {}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 296edb78-215c-4104-90fc-f1eee22b352c--&gt;
&lt;!-- begin paragraph 6e6b835d-694e-4c0f-abef-690401123372--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e6b835d-694e-4c0f-abef-690401123372&quot;&gt;&lt;br /&gt;
Pour le channel d&amp;#39;erreur en sortie &lt;code&gt;Either[E1, E2]&lt;/code&gt; par &lt;code&gt;EE&lt;/code&gt; avec &lt;code&gt;EE &amp;gt;: E1&lt;/code&gt;, &lt;code&gt;EE &amp;gt;: E2&lt;/code&gt;. &lt;br /&gt;
En entrée, on va pouvoir remplacer &lt;code&gt;(R1, R2)&lt;/code&gt; par &lt;code&gt;R = Env[R1 with R2&lt;/code&gt;] avec &lt;code&gt;R &amp;lt;: R1&lt;/code&gt;, &lt;code&gt;R &amp;lt;: R2&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6e6b835d-694e-4c0f-abef-690401123372--&gt;
&lt;!-- begin code 0b8c1667-540c-418e-8afd-0ed248f332e5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0b8c1667-540c-418e-8afd-0ed248f332e5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Env[+T] {
	//def get[A &amp;gt;: T]:A
	//def add[A](a: A): Env[T with A]
}

object Env {
	//def zero: Env[Any]
}

def toT[R1, R2](r: Env[R1 with R2]): (R1, R2) = ???
def toR[R1, R2](r1: R1, r2: R2): Env[R1 with R2] = ??? // à la main ou macro ...&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0b8c1667-540c-418e-8afd-0ed248f332e5--&gt;
&lt;!-- begin paragraph 2276f153-4a75-435c-b522-f2e46561e3ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2276f153-4a75-435c-b522-f2e46561e3ee&quot;&gt;Le code compose mieux et permet de gérer automatiquement les dépendances que l&amp;#39;on a lors de la composition. A condition que l&amp;#39;on puisse faire la composition :&lt;/p&gt;
&lt;!-- end paragraph 2276f153-4a75-435c-b522-f2e46561e3ee--&gt;
&lt;!-- begin code 6f3b4445-0417-4dbb-942b-712cac7807a5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6f3b4445-0417-4dbb-942b-712cac7807a5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def toR[R1, R2](r1: R1, r2: R2): R1 with R2&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6f3b4445-0417-4dbb-942b-712cac7807a5--&gt;
&lt;!-- begin paragraph 32e3f079-b0a4-48bc-84a1-909dba84dd12--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-32e3f079-b0a4-48bc-84a1-909dba84dd12&quot;&gt;Les dépendances que l&amp;#39;on va pouvoir récupérer au fur à mesure que l&amp;#39;on en a besoin dans ZIO : &lt;/p&gt;
&lt;!-- end paragraph 32e3f079-b0a4-48bc-84a1-909dba84dd12--&gt;
&lt;!-- begin heading_2 1675f44d-c40c-49e3-b5e4-c2248e6d4847--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-1675f44d-c40c-49e3-b5e4-c2248e6d4847&quot;&gt;Les resources de ZIO&lt;/h3&gt;
&lt;!-- end heading_2 1675f44d-c40c-49e3-b5e4-c2248e6d4847--&gt;
&lt;!-- begin paragraph 5f79b5f6-5a00-4fd1-a807-1c275eff238a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f79b5f6-5a00-4fd1-a807-1c275eff238a&quot;&gt;Les ressources qui sont disponibles par défaut dans ZIO sont des re-implémentations des composants de base de la JVM pour que l&amp;#39;on puisse les utiliser en FP, ainsi que les manipuler lors des tests unitaires.&lt;/p&gt;
&lt;!-- end paragraph 5f79b5f6-5a00-4fd1-a807-1c275eff238a--&gt;
&lt;!-- begin heading_3 ff09af54-2542-4353-9c2d-c68b7f1488a5--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-ff09af54-2542-4353-9c2d-c68b7f1488a5&quot;&gt;Clock&lt;/h4&gt;
&lt;!-- end heading_3 ff09af54-2542-4353-9c2d-c68b7f1488a5--&gt;
&lt;!-- begin code d673c104-67ef-4dcd-8c0e-ad28af6d6e00--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d673c104-67ef-4dcd-8c0e-ad28af6d6e00&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def currentTime(unit: TimeUnit): URIO[Clock, Long]
def currentDateTime: ZIO[Clock, DateTimeException, OffsetDateTime]
def nanoTime: URIO[Clock, Long]
def sleep(duration: Duration): URIO[Clock, Unit]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d673c104-67ef-4dcd-8c0e-ad28af6d6e00--&gt;
&lt;!-- begin paragraph 762de5ce-7f25-4345-a2d7-3c0f0c832374--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-762de5ce-7f25-4345-a2d7-3c0f0c832374&quot;&gt;Si on prend &lt;code&gt;nanoTime&lt;/code&gt; , cela se lit comme un &lt;code&gt;Clock =&amp;gt; IO[Long]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 762de5ce-7f25-4345-a2d7-3c0f0c832374--&gt;
&lt;!-- begin heading_3 136d2b42-4ab6-4a3f-94d5-b36fa7e3d60b--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-136d2b42-4ab6-4a3f-94d5-b36fa7e3d60b&quot;&gt;Console&lt;/h4&gt;
&lt;!-- end heading_3 136d2b42-4ab6-4a3f-94d5-b36fa7e3d60b--&gt;
&lt;!-- begin paragraph 129df7b8-528b-48e3-a277-ef411e270225--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-129df7b8-528b-48e3-a277-ef411e270225&quot;&gt;On est sur le classique, comment lire et écrire dans la console&lt;/p&gt;
&lt;!-- end paragraph 129df7b8-528b-48e3-a277-ef411e270225--&gt;
&lt;!-- begin code e3ae27cb-c676-4727-a99b-bdd80b43e89d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e3ae27cb-c676-4727-a99b-bdd80b43e89d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def putStr(line: String): URIO[Console, Unit]
def putStrLn(line: String): URIO[Console, Unit]
def getStrLn: ZIO[Console, IOException, String]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e3ae27cb-c676-4727-a99b-bdd80b43e89d--&gt;
&lt;!-- begin paragraph 2ffb5055-7b0f-4639-976d-8f81a23120fe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2ffb5055-7b0f-4639-976d-8f81a23120fe&quot;&gt;avec Clock et Console, on va pouvoir faire des programmes assez classiques :&lt;/p&gt;
&lt;!-- end paragraph 2ffb5055-7b0f-4639-976d-8f81a23120fe--&gt;
&lt;!-- begin code 23dad004-97b2-4202-a7e2-de46c5063c60--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-23dad004-97b2-4202-a7e2-de46c5063c60&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Hello {
  import zio._
  import zio.console._
  import zio.clock._

  val prg: ZIO[Console with Clock, Exception, Unit] = for {
    _    &amp;lt;- putStr(&amp;quot;Hello, what is your name ? &amp;gt; &amp;quot;)
    name &amp;lt;- getStrLn
    dt   &amp;lt;- currentDateTime
    _    &amp;lt;- putStrLn(s&amp;quot;Hello $name, it is $dt&amp;quot;)
  } yield {}

  def main(args: Array[String]): Unit = {
    zio.Runtime.default.unsafeRun(prg)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 23dad004-97b2-4202-a7e2-de46c5063c60--&gt;
&lt;!-- begin paragraph 75eeb3ca-1d3d-4231-ac21-dc7b6b082305--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75eeb3ca-1d3d-4231-ac21-dc7b6b082305&quot;&gt;Notre programme est un &lt;code&gt;ZIO[Console with Clock, Exception, Unit]&lt;/code&gt; alors que cela utilise avec des effets une fois l&amp;#39;horloge et trois fois la console.&lt;/p&gt;
&lt;!-- end paragraph 75eeb3ca-1d3d-4231-ac21-dc7b6b082305--&gt;
&lt;!-- begin heading_3 c28c6d74-6b30-4f37-b2c8-85310c32a1f3--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-c28c6d74-6b30-4f37-b2c8-85310c32a1f3&quot;&gt;System&lt;/h4&gt;
&lt;!-- end heading_3 c28c6d74-6b30-4f37-b2c8-85310c32a1f3--&gt;
&lt;!-- begin code b99ef521-2216-48d5-be15-06a4c6e984a0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b99ef521-2216-48d5-be15-06a4c6e984a0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def env(variable: String): ZIO[System, SecurityException, Option[String]]
def property(prop: String): ZIO[System, Throwable, Option[String]]
def lineSeparator: URIO[System, String]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b99ef521-2216-48d5-be15-06a4c6e984a0--&gt;
&lt;!-- begin heading_3 5cf55961-4de6-404a-ae84-22c8e12178ae--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-5cf55961-4de6-404a-ae84-22c8e12178ae&quot;&gt;Random&lt;/h4&gt;
&lt;!-- end heading_3 5cf55961-4de6-404a-ae84-22c8e12178ae--&gt;
&lt;!-- begin code a6ad267e-714a-4e48-96f2-37f15b86e6d8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a6ad267e-714a-4e48-96f2-37f15b86e6d8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def nextBoolean: ZIO[Random, Nothing, Boolean]                  
def nextBytes(length: =&amp;gt; Int): ZIO[Random, Nothing, Chunk[Byte]]
def nextDouble: ZIO[Random, Nothing, Double]                    
def nextFloat: ZIO[Random, Nothing, Float]                      
def nextGaussian: ZIO[Random, Nothing, Double]                  
def nextInt(n: =&amp;gt; Int): ZIO[Random, Nothing, Int]               
def nextInt: ZIO[Random, Nothing, Int]                          
def nextLong: ZIO[Random, Nothing, Long]                        
def nextLong(n: =&amp;gt; Long): ZIO[Random, Nothing, Long]            
def nextPrintableChar: ZIO[Random, Nothing, Char]               
def nextString(length: =&amp;gt; Int): ZIO[Random, Nothing, String]    
def shuffle[A](list: =&amp;gt; List[A]): ZIO[Random, Nothing, List[A]]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a6ad267e-714a-4e48-96f2-37f15b86e6d8--&gt;
&lt;!-- begin heading_3 7253fe9e-c283-4670-b00e-22c51b888837--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-7253fe9e-c283-4670-b00e-22c51b888837&quot;&gt;Blocking&lt;/h4&gt;
&lt;!-- end heading_3 7253fe9e-c283-4670-b00e-22c51b888837--&gt;
&lt;!-- begin paragraph 5a0bcc4d-24b9-4d4c-9dd2-de1da12d8dde--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a0bcc4d-24b9-4d4c-9dd2-de1da12d8dde&quot;&gt;Blocking sert à wrapper les effets qui ont besoin d&amp;#39;être mis dans une Thread Pool à part (&lt;a href=&quot;https://typelevel.org/cats-effect/concurrency/basics.html#choosing-thread-pool&quot;&gt;explication&lt;/a&gt;). Par exemple, dans le &lt;a href=&quot;https://blog.univalence.io/zio-concurrence-a-la-carte/&quot;&gt;dernier article&lt;/a&gt; à la fin : &lt;/p&gt;
&lt;!-- end paragraph 5a0bcc4d-24b9-4d4c-9dd2-de1da12d8dde--&gt;
&lt;!-- begin code 0d7bb305-4cb8-4730-a685-79c6a9a84a93--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0d7bb305-4cb8-4730-a685-79c6a9a84a93&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def blocking[R &amp;lt;: Blocking, E, A](zio: ZIO[R, E, A]): ZIO[R, E, A]
def effectBlocking[A](effect: =&amp;gt; A): ZIO[Blocking, Throwable, A]
def effectBlockingCancelable[A](effect: =&amp;gt; A)(cancel: UIO[Unit]): ZIO[Blocking, Throwable, A]
def effectBlockingIO[A](effect: =&amp;gt; A): ZIO[Blocking, IOException, A]
def effectBlockingInterrupt[A](effect: =&amp;gt; A): ZIO[Blocking, Throwable, A]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0d7bb305-4cb8-4730-a685-79c6a9a84a93--&gt;
&lt;!-- begin heading_2 5c760663-a3a8-49a2-bfd5-1aebecd3d0b8--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-5c760663-a3a8-49a2-bfd5-1aebecd3d0b8&quot;&gt;(R1, R2) ⇒ R1 with R2&lt;/h3&gt;
&lt;!-- end heading_2 5c760663-a3a8-49a2-bfd5-1aebecd3d0b8--&gt;
&lt;!-- begin paragraph 4b4888c8-8771-4282-b05b-762c532b3cdc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4b4888c8-8771-4282-b05b-762c532b3cdc&quot;&gt;La clé pour rendre le code composable est la contravariance sur R, qui permet de composer différent effet qui dépendent de différents R, sans combiner à la main.&lt;/p&gt;
&lt;!-- end paragraph 4b4888c8-8771-4282-b05b-762c532b3cdc--&gt;
&lt;!-- begin paragraph b1b2b0ac-95ac-4f82-8add-33a8b54da199--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b1b2b0ac-95ac-4f82-8add-33a8b54da199&quot;&gt;Une fois que l&amp;#39;on a un &lt;code&gt;val prg = IO[Console with Clock, Exception, Unit]&lt;/code&gt; il faut lui fournir d&amp;#39;une manière ou d&amp;#39;une autre un &lt;code&gt;Console with Clock&lt;/code&gt;  &lt;/p&gt;
&lt;!-- end paragraph b1b2b0ac-95ac-4f82-8add-33a8b54da199--&gt;
&lt;!-- begin paragraph 6227f429-ccd3-4999-b1b5-b60c14c84f7e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6227f429-ccd3-4999-b1b5-b60c14c84f7e&quot;&gt;Jusqu&amp;#39;en RC17, ZIO utilisait la Service pattern, ce qui permet de mettre en râteau les différentes parties.&lt;/p&gt;
&lt;!-- end paragraph 6227f429-ccd3-4999-b1b5-b60c14c84f7e--&gt;
&lt;!-- begin code a76410fb-100c-4d66-9d1b-5550fa8875f2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a76410fb-100c-4d66-9d1b-5550fa8875f2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// (Clock, Console) =&amp;gt; Clock with Console
def mix(clock_ : Clock, console_ : Console) = new Clock with Console {
  override val clock   = clock_.clock
  override val console = console_.console
}

def main(args: Array[String]): Unit = {
  val providedPrg: ZIO[Any, Exception, Unit] = 
    prg.provide(mix(Clock.Live, Console.Live))
  
  new DefaultRuntime {}.unsafeRun(providedPrg)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a76410fb-100c-4d66-9d1b-5550fa8875f2--&gt;
&lt;!-- begin paragraph 50dca6f9-c42d-491e-a98a-45dcf53ebdb1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50dca6f9-c42d-491e-a98a-45dcf53ebdb1&quot;&gt;Chaque service est défini en plusieurs morceaux, ce qui le rend utilisable et injectable.&lt;/p&gt;
&lt;!-- end paragraph 50dca6f9-c42d-491e-a98a-45dcf53ebdb1--&gt;
&lt;!-- begin code dd2c5939-48bd-4635-8874-c511d9a16b9b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dd2c5939-48bd-4635-8874-c511d9a16b9b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Clock {
  val clock: Clock.Service[Any]
}

object Clock extends Serializable {

  trait Service[R] {
    def currentTime(unit: TimeUnit): ZIO[R, Nothing, Long]
    def currentDateTime: ZIO[R, Nothing, OffsetDateTime]
    val nanoTime: ZIO[R, Nothing, Long]
    def sleep(duration: Duration): ZIO[R, Nothing, Unit]
  }

  trait Live extends Clock {
    val clock: Clock.Service[Any] = 
      /**
       * ... Implementation
       */
  }

  object Live extends Live
}

package object clock extends Clock.Service[Clock] {
  /**
   * ...
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dd2c5939-48bd-4635-8874-c511d9a16b9b--&gt;
&lt;!-- begin paragraph bb5a7466-c673-4cbb-a7b3-533e31dc00c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bb5a7466-c673-4cbb-a7b3-533e31dc00c0&quot;&gt;Cela a été remis à plat avec la RC18.&lt;/p&gt;
&lt;!-- end paragraph bb5a7466-c673-4cbb-a7b3-533e31dc00c0--&gt;
&lt;!-- begin heading_1 c3995f12-bec0-48c0-889e-ddd1c8901d09--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c3995f12-bec0-48c0-889e-ddd1c8901d09&quot;&gt;Has et ZLayer&lt;/h2&gt;
&lt;!-- end heading_1 c3995f12-bec0-48c0-889e-ddd1c8901d09--&gt;
&lt;!-- begin paragraph f61ff43d-64c5-4381-a1d0-118ceb661e03--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f61ff43d-64c5-4381-a1d0-118ceb661e03&quot;&gt;Le gros changement depuis la RC18, c&amp;#39;est la refonte de la gestion de &lt;code&gt;R&lt;/code&gt;. En tant qu&amp;#39;utilisateur la code de base ne va pas beaucoup changer, on va voir que la composition est meilleure quand on veut fournir les ressources : &lt;/p&gt;
&lt;!-- end paragraph f61ff43d-64c5-4381-a1d0-118ceb661e03--&gt;
&lt;!-- begin code 056a9b7b-8ff4-4f4e-b08b-0c4977b6801e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-056a9b7b-8ff4-4f4e-b08b-0c4977b6801e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val providedPrg: Task[Unit] = prg.provideLayer(Clock.live ++ Console.live)

//prg.provideSomeLayer(Clock.live).provideLayer(Console.live)

zio.Runtime.default.unsafeRun(providedPrg)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 056a9b7b-8ff4-4f4e-b08b-0c4977b6801e--&gt;
&lt;!-- begin paragraph e8ba8f63-2fc9-4eed-942e-2ce60a417c50--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e8ba8f63-2fc9-4eed-942e-2ce60a417c50&quot;&gt;On va même pouvoir fournir pas à pas : &lt;/p&gt;
&lt;!-- end paragraph e8ba8f63-2fc9-4eed-942e-2ce60a417c50--&gt;
&lt;!-- begin code caba5f93-487f-47e3-83cb-76f2bbe7e044--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-caba5f93-487f-47e3-83cb-76f2bbe7e044&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//val prg: ZIO[Console with Clock, Exception, Unit]
val prgWithClock: RIO[Console, Unit] = prg.provideSomeLayer(Clock.live)
val providedPrg: Task[Unit] = prgWithClock.provideLayer(Console.live)


zio.Runtime.default.unsafeRun(providedPrg)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code caba5f93-487f-47e3-83cb-76f2bbe7e044--&gt;
&lt;!-- begin paragraph 472eabd9-e02e-4d79-ac31-650b633daad7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-472eabd9-e02e-4d79-ac31-650b633daad7&quot;&gt;Le gain de composition vient de l&amp;#39;introduction d&amp;#39;un mécanisme de &amp;quot;Tagging&amp;quot; qui permet de composer facilement des types hétérogènes qui représente le set dépendance nécessaire pour exécuter l&amp;#39;effet. Ce mécanisme c&amp;#39;est le &lt;code&gt;Has&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 472eabd9-e02e-4d79-ac31-650b633daad7--&gt;
&lt;!-- begin heading_2 d11d933f-cd63-4a8a-8f77-89b68c119c96--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d11d933f-cd63-4a8a-8f77-89b68c119c96&quot;&gt;Has, le HSet sans les Shapelesseries &lt;/h3&gt;
&lt;!-- end heading_2 d11d933f-cd63-4a8a-8f77-89b68c119c96--&gt;
&lt;!-- begin paragraph fcb31d3d-4de0-42c8-975e-f1c23f1aea9a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fcb31d3d-4de0-42c8-975e-f1c23f1aea9a&quot;&gt;&lt;code&gt;Has[_]&lt;/code&gt;  stocke une &lt;code&gt;Map[TagType, Scala.Any]&lt;/code&gt; : &lt;/p&gt;
&lt;!-- end paragraph fcb31d3d-4de0-42c8-975e-f1c23f1aea9a--&gt;
&lt;!-- begin code 332bee33-c793-45f5-b946-31533a0429e6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-332bee33-c793-45f5-b946-31533a0429e6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;final class Has[A] private (private val map: Map[TagType, scala.Any]) {
  def size: Int = map.size
	
  override def equals(that: Any): Boolean = that match {
    case that: Has[_] =&amp;gt; map == that.map
  }
  
  override def hashCode: Int = map.hashCode

  override def toString: String = map.mkString(&amp;quot;Map(&amp;quot;, &amp;quot;,\n&amp;quot;, &amp;quot;)&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 332bee33-c793-45f5-b946-31533a0429e6--&gt;
&lt;!-- begin paragraph c9fa6310-940b-4a78-9f54-515fbad4b12c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c9fa6310-940b-4a78-9f54-515fbad4b12c&quot;&gt;Ce qui permet de l&amp;#39;utiliser comme une map hétérogène strictement typée avec la méthode &lt;code&gt;get&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph c9fa6310-940b-4a78-9f54-515fbad4b12c--&gt;
&lt;!-- begin code 7eb03a88-d13c-4e16-950a-77d3528f7332--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7eb03a88-d13c-4e16-950a-77d3528f7332&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio.Has

val h1: Has[String] = Has(&amp;quot;A&amp;quot;)
val h2: Has[Int] = Has(1)

type H3 = Has[String] with Has[Int]

val h3: H3 = h1 ++ h2
/*Map(      String    -&amp;gt; A,
               Int    -&amp;gt; 1)

   */

assert(h3.get[Int] == 1)
assert(h3.get[String] == &amp;quot;A&amp;quot;)

//Has.HasSyntax(h3).get[String](evidence,tag) == &amp;quot;A&amp;quot;

type H4 = H3 with Has[Option[Int]] with Has[Option[String]]

val h4: H4 = h3 add Option(2) add Option(&amp;quot;B&amp;quot;)

/*Map(      String    -&amp;gt; A,
               Int    -&amp;gt; 1,
      Option[+Int]    -&amp;gt; Some(2),
      Option[+String] -&amp;gt; Some(B))
   */

assert(h4.get[Option[Int]]    == Option(2))
assert(h4.get[Option[String]] == Option(&amp;quot;B&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7eb03a88-d13c-4e16-950a-77d3528f7332--&gt;
&lt;!-- begin paragraph 6a0c777a-28ac-4c4f-b831-85aa8f8c464a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a0c777a-28ac-4c4f-b831-85aa8f8c464a&quot;&gt;On peut même utiliser des &amp;quot;kinds&amp;quot; comme Option, ce qui pourrait permettre d&amp;#39;utiliser &lt;code&gt;Has&lt;/code&gt; dans des contextes où l&amp;#39;on modélise l&amp;#39;accès les accès aux données. &lt;/p&gt;
&lt;!-- end paragraph 6a0c777a-28ac-4c4f-b831-85aa8f8c464a--&gt;
&lt;!-- begin code 956fe294-8dd1-41e8-8cb3-f30dbb73caaa--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-956fe294-8dd1-41e8-8cb3-f30dbb73caaa&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;type Domain = Has[Repo[Account]] with Has[Repo[Transaction]]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 956fe294-8dd1-41e8-8cb3-f30dbb73caaa--&gt;
&lt;!-- begin paragraph e4a4ca4e-ee2e-45e7-92ad-a7fe05d89d10--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e4a4ca4e-ee2e-45e7-92ad-a7fe05d89d10&quot;&gt;La structure est aussi covariante :&lt;/p&gt;
&lt;!-- end paragraph e4a4ca4e-ee2e-45e7-92ad-a7fe05d89d10--&gt;
&lt;!-- begin code 386df05d-8dd2-4e61-9f86-d898f7607e5b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-386df05d-8dd2-4e61-9f86-d898f7607e5b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;sealed trait A
case object B extends A

val b = Has(B)

assert(b.get[A] == B)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 386df05d-8dd2-4e61-9f86-d898f7607e5b--&gt;
&lt;!-- begin paragraph 161a509d-acf5-495c-a8a5-5cecd57378ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-161a509d-acf5-495c-a8a5-5cecd57378ac&quot;&gt;Néanmoins cela peut devenir non-déterministe sur le résultat&lt;/p&gt;
&lt;!-- end paragraph 161a509d-acf5-495c-a8a5-5cecd57378ac--&gt;
&lt;!-- begin code d9970931-3476-4900-95f9-656b204ad1d8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d9970931-3476-4900-95f9-656b204ad1d8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case object C extends A
val bc = b add C

bc.get[A]
//res1: A = B
bc.get[B.type]
//res2: B.type = B
bc.get[C.type]
//res3: C.type = C&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d9970931-3476-4900-95f9-656b204ad1d8--&gt;
&lt;!-- begin paragraph 902d642b-0c87-4063-9b04-d3aaeb71f042--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-902d642b-0c87-4063-9b04-d3aaeb71f042&quot;&gt;Et cela contient un cache, pour éviter de casser les perfs lors des recherches de la bonne valeur.&lt;/p&gt;
&lt;!-- end paragraph 902d642b-0c87-4063-9b04-d3aaeb71f042--&gt;
&lt;!-- begin paragraph 9e8e5f88-6dc5-48d7-b423-9f60a8095a0f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e8e5f88-6dc5-48d7-b423-9f60a8095a0f&quot;&gt;Donc Has est un Set, avec les super pouvoir du type system de Scala et une garantie que Has est non vide.&lt;/p&gt;
&lt;!-- end paragraph 9e8e5f88-6dc5-48d7-b423-9f60a8095a0f--&gt;
&lt;!-- begin code c930ab15-cb3b-4fc7-8a10-31d7c6f0bfe5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c930ab15-cb3b-4fc7-8a10-31d7c6f0bfe5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio.Has

val set = Set(1, &amp;quot;A&amp;quot;)
set.size // 2

val has = Has.allOf(1, &amp;quot;A&amp;quot;)
has.size // 2

set.exists(_.isInstanceOf[Int]) // true
has.isInstanceOf[Has[Int]] // true

set.collectFirst({ case i: Int =&amp;gt; i }) // Option(1)
has.get[Int] // 1

set.collectFirst({ case i: Long =&amp;gt; i }) // None
has.get[Long] // DON&amp;#39;T COMPILE
//Cannot prove that zio.Has[Int] with zio.Has[String] &amp;lt;:&amp;lt; zio.Has[_ &amp;lt;: Long]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c930ab15-cb3b-4fc7-8a10-31d7c6f0bfe5--&gt;
&lt;!-- begin paragraph c570d6ca-dc70-4138-acdd-502edfb24ccf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c570d6ca-dc70-4138-acdd-502edfb24ccf&quot;&gt;Has est donc un cas solide pour faire le mix-in, à condition de l&amp;#39;utiliser en premier paramètre de ZIO.&lt;/p&gt;
&lt;!-- end paragraph c570d6ca-dc70-4138-acdd-502edfb24ccf--&gt;
&lt;!-- begin code 31f17cc8-26f6-4b5e-a4a6-3ceff1a7e3e8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-31f17cc8-26f6-4b5e-a4a6-3ceff1a7e3e8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def toR[R1 &amp;lt;: Has[_], R2 &amp;lt;: Has[_]](r1: R1, r2: R2): R1 with R2 = r1 ++ r2 &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 31f17cc8-26f6-4b5e-a4a6-3ceff1a7e3e8--&gt;
&lt;!-- begin heading_2 a839adc8-3398-4657-88e8-74b85e00c9d0--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-a839adc8-3398-4657-88e8-74b85e00c9d0&quot;&gt;Revenons à la gestion du R&lt;/h3&gt;
&lt;!-- end heading_2 a839adc8-3398-4657-88e8-74b85e00c9d0--&gt;
&lt;!-- begin paragraph 6fc9f880-6109-4876-8ce8-71d82a13447a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6fc9f880-6109-4876-8ce8-71d82a13447a&quot;&gt;Aussi la définition d&amp;#39;une dépendance est beaucoup plus propre. On va avoir l&amp;#39;object qui tient le tag &lt;code&gt;Has&lt;/code&gt; , le trait pour le Service, l&amp;#39;implémentation et quelques méthodes pour faciliter l&amp;#39;expérience utilisateur comme : &lt;code&gt;Clock.live&lt;/code&gt;, ou les méthodes dans &lt;code&gt;clock&lt;/code&gt; qui retournent des &lt;code&gt;ZIO[Clock, E, A]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6fc9f880-6109-4876-8ce8-71d82a13447a--&gt;
&lt;!-- begin code bd4531b5-b335-4e65-8392-f24bd4c097e2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bd4531b5-b335-4e65-8392-f24bd4c097e2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;package object clock {

  type Clock = Has[Clock.Service]

  object Clock {
    trait Service {
      def currentTime(unit: TimeUnit): UIO[Long]
      def currentDateTime: IO[DateTimeException, OffsetDateTime]
      def nanoTime: UIO[Long]
      def sleep(duration: Duration): UIO[Unit]
    }

    object Service {
      val live: Service = new Service {
        /**
         *
         */
      }
    }
    
    val live: Layer[Nothing, Clock] = ZLayer.succeed(Service.live)
  }
  

  def currentTime(unit: =&amp;gt; TimeUnit): ZIO[Clock, Nothing, Long] =
    ZIO.accessM(_.get.currentTime(unit))
}

 &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bd4531b5-b335-4e65-8392-f24bd4c097e2--&gt;
&lt;!-- begin paragraph 3623502d-face-42fd-99a0-2e10e3e1a103--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3623502d-face-42fd-99a0-2e10e3e1a103&quot;&gt;On peut injecter les ressources mieux qu&amp;#39;avant, vu que le mix in est inclus avec &lt;code&gt;Has&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 3623502d-face-42fd-99a0-2e10e3e1a103--&gt;
&lt;!-- begin code 208b4a3b-3151-46af-9e5b-7ea4ad24b6c8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-208b4a3b-3151-46af-9e5b-7ea4ad24b6c8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val providedPrg: Task[Unit] = prg.provideLayer(Clock.live ++ Console.live)

zio.Runtime.default.unsafeRun(providedPrg)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 208b4a3b-3151-46af-9e5b-7ea4ad24b6c8--&gt;
&lt;!-- begin paragraph abc2ae60-59b9-4b5b-934a-d809ec3c1de7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-abc2ae60-59b9-4b5b-934a-d809ec3c1de7&quot;&gt;Aussi pas à pas&lt;/p&gt;
&lt;!-- end paragraph abc2ae60-59b9-4b5b-934a-d809ec3c1de7--&gt;
&lt;!-- begin code 67813bcb-f758-4f1c-9260-32cfbb3137be--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-67813bcb-f758-4f1c-9260-32cfbb3137be&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val prgWithClock: ZIO[Console, Exception, Unit] = prg
  .provideSome[Console](x =&amp;gt; x ++ Has(Clock.Service.live))

val providedPrg: IO[Exception, Unit] =
  prgWithClock.provide(Has(Console.Service.live))

zio.Runtime.default.unsafeRun(providedPrg)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 67813bcb-f758-4f1c-9260-32cfbb3137be--&gt;
&lt;!-- begin paragraph 76a359fc-cc21-49c4-9147-5dbb93daba1e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-76a359fc-cc21-49c4-9147-5dbb93daba1e&quot;&gt;Grâce à la magic de &lt;code&gt;Has&lt;/code&gt; ! Problème résolu donc, &lt;code&gt;ZIO[R, E, A]&lt;/code&gt; est :&lt;/p&gt;
&lt;!-- end paragraph 76a359fc-cc21-49c4-9147-5dbb93daba1e--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;facilement utilisable, pas de &amp;quot;boilerplate&amp;quot;&lt;/li&gt;&lt;li&gt;facilement extensible, on peut utiliser nos propres ressources, en dehors de Random, Console, Clock, System et Blocking.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph a141204c-c82c-4e6a-bcd2-567cde4e2cde--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a141204c-c82c-4e6a-bcd2-567cde4e2cde&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a141204c-c82c-4e6a-bcd2-567cde4e2cde--&gt;
&lt;!-- begin paragraph f63da8e7-221d-416c-b5e9-e1f5f112adcf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f63da8e7-221d-416c-b5e9-e1f5f112adcf&quot;&gt;Néanmoins ZIO est allé beaucoup loin que &lt;code&gt;Has&lt;/code&gt;, et propose une solution pour résoudre le problème de la construction des dépendances.&lt;/p&gt;
&lt;!-- end paragraph f63da8e7-221d-416c-b5e9-e1f5f112adcf--&gt;
&lt;!-- begin heading_2 8f51c9b2-bbcb-47b9-94f6-2628c758d803--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8f51c9b2-bbcb-47b9-94f6-2628c758d803&quot;&gt;Le ZLayer, c&amp;#39;est la fin du printemps&lt;/h3&gt;
&lt;!-- end heading_2 8f51c9b2-bbcb-47b9-94f6-2628c758d803--&gt;
&lt;!-- begin paragraph 79478e3e-4aa8-4d39-a52b-3f6e5e240fa1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79478e3e-4aa8-4d39-a52b-3f6e5e240fa1&quot;&gt;Le ZLayer est l&amp;#39;injection de ressources pour ZIO, les ressources sont disponibles à l&amp;#39;usage et libérées dès que l&amp;#39;on en a plus besoin.&lt;/p&gt;
&lt;!-- end paragraph 79478e3e-4aa8-4d39-a52b-3f6e5e240fa1--&gt;
&lt;!-- begin paragraph 42d03dff-c9b8-4bb0-b9b8-26e0398f1e93--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42d03dff-c9b8-4bb0-b9b8-26e0398f1e93&quot;&gt;Pour rentrer dans les détails du fonctionnement du ZLayer, il faut d&amp;#39;abord aller voir un autre type, le ZManaged.&lt;/p&gt;
&lt;!-- end paragraph 42d03dff-c9b8-4bb0-b9b8-26e0398f1e93--&gt;
&lt;!-- begin heading_2 ad903781-b921-465f-a48f-a50c8f30eecc--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ad903781-b921-465f-a48f-a50c8f30eecc&quot;&gt;Le ZManaged&lt;/h3&gt;
&lt;!-- end heading_2 ad903781-b921-465f-a48f-a50c8f30eecc--&gt;
&lt;!-- begin paragraph cee7a36e-e7d3-490a-984d-e0cd43c366fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cee7a36e-e7d3-490a-984d-e0cd43c366fb&quot;&gt;Le ZManaged est un type spécifique de ZIO qui permet l&amp;#39;acquisition des ressources de manière composable.&lt;br /&gt;
En Java ou en impératif, nous allons utiliser soit un try/catch/finally ou une syntaxe dédiée pour faire cela. &lt;/p&gt;
&lt;!-- end paragraph cee7a36e-e7d3-490a-984d-e0cd43c366fb--&gt;
&lt;!-- begin code 72e3ae6d-858c-4e51-8266-ffb837f58b66--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-72e3ae6d-858c-4e51-8266-ffb837f58b66&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;import java.io.FileNotFoundException;
import java.io.PrintWriter;

public class PrintHelloFromJava {
    
    public static void main(String[] args) throws FileNotFoundException {

        try(PrintWriter obj = new PrintWriter(&amp;quot;log.txt&amp;quot;)) {
            obj.println(&amp;quot;Hello from Java!&amp;quot;);
        }

        System.exit(0);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 72e3ae6d-858c-4e51-8266-ffb837f58b66--&gt;
&lt;!-- begin paragraph 5a02309b-d571-4a35-837c-cfc925155bfa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a02309b-d571-4a35-837c-cfc925155bfa&quot;&gt;ou avec try finally&lt;/p&gt;
&lt;!-- end paragraph 5a02309b-d571-4a35-837c-cfc925155bfa--&gt;
&lt;!-- begin code 6369d482-8aaa-4abb-a37a-9223161804c3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6369d482-8aaa-4abb-a37a-9223161804c3&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;        PrintWriter obj = new PrintWriter(&amp;quot;log.txt&amp;quot;);
        try {
            obj.println(&amp;quot;Hello from Java!&amp;quot;);
        } finally {
            obj.close();
        }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6369d482-8aaa-4abb-a37a-9223161804c3--&gt;
&lt;!-- begin paragraph fbb6bb83-bb86-40d4-bed1-9cf305bf2cd1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fbb6bb83-bb86-40d4-bed1-9cf305bf2cd1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fbb6bb83-bb86-40d4-bed1-9cf305bf2cd1--&gt;
&lt;!-- begin paragraph 1275c548-ece7-4ea8-9d12-fb4863634b3a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1275c548-ece7-4ea8-9d12-fb4863634b3a&quot;&gt;Avec ZIO on va pouvoir utiliser l&amp;#39;abstraction sur les effets pour faire la même chose avec les fonctions suivantes :&lt;/p&gt;
&lt;!-- end paragraph 1275c548-ece7-4ea8-9d12-fb4863634b3a--&gt;
&lt;!-- begin code 6c8a1533-792e-4a09-a020-e19f86b71b2f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6c8a1533-792e-4a09-a020-e19f86b71b2f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;
//1. la structure
def makeExit[R, E, A](acquire: ZIO[R, E, A])
                     (release: (A, Exit[Any, Any]) =&amp;gt; ZIO[R, Nothing, Any]
  ): ZManaged[R, E, A] = //???


//2. avec un helper pour ne pas gérer l&amp;#39;exit
def make[R, E, A](acquire: ZIO[R, E, A])
                 (release: A =&amp;gt; ZIO[R, Nothing, Any]
  ): ZManaged[R, E, A] = //makeExit(acquire)((a, _) =&amp;gt; release(a))


//3. depuis l&amp;#39;interfaçe AutoCloseable de Java
def fromAutoCloseable[R, E, A &amp;lt;: AutoCloseable](fa: ZIO[R, E, A]
  ): ZManaged[R, E, A] = //make(fa)(a =&amp;gt; UIO(a.close()))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6c8a1533-792e-4a09-a020-e19f86b71b2f--&gt;
&lt;!-- begin paragraph e5299c8e-f832-4db7-a1f9-9d58f82470f5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5299c8e-f832-4db7-a1f9-9d58f82470f5&quot;&gt;L&amp;#39; &lt;code&gt;Exit&lt;/code&gt; est un type algébrique qui représente un &lt;code&gt;Either[Cause[E], A]&lt;/code&gt; (spécialisé):&lt;/p&gt;
&lt;!-- end paragraph e5299c8e-f832-4db7-a1f9-9d58f82470f5--&gt;
&lt;!-- begin code 2eaddf17-6477-4db1-ae5b-9fcf110b8817--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2eaddf17-6477-4db1-ae5b-9fcf110b8817&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Success[+A](value: A)   extends Exit[Nothing, A]
class Failure[+E](cause: Cause[E]) extends Exit[E, Nothing]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2eaddf17-6477-4db1-ae5b-9fcf110b8817--&gt;
&lt;!-- begin paragraph 78426952-8425-4c64-b676-5143647c1992--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78426952-8425-4c64-b676-5143647c1992&quot;&gt;La &lt;code&gt;Cause&lt;/code&gt; est un autre type algébrique (généralisé et récursif) qui représente la sortie d&amp;#39;une exécution sinistre : &lt;/p&gt;
&lt;!-- end paragraph 78426952-8425-4c64-b676-5143647c1992--&gt;
&lt;!-- begin code 677f824e-62d8-49d5-b555-e05f1df4dcaa--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-677f824e-62d8-49d5-b555-e05f1df4dcaa&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case object Empty extends Cause[Nothing]
case class Both[+E](left: Cause[E], right: Cause[E]) extends Cause[E]
case class Die(value: Throwable) extends Cause[Nothing]
case class Fail[+E](value: E) extends Cause[E]
case class Interrupt(fiberId: Fiber.Id) extends Cause[Nothing]
case class Meta[+E](cause: Cause[E], data: Data) extends Cause[E]
case class Then[+E](left: Cause[E], right: Cause[E]) extends Cause[E]
case class Traced[+E](cause: Cause[E], trace: ZTrace) extends Cause[E]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 677f824e-62d8-49d5-b555-e05f1df4dcaa--&gt;
&lt;!-- begin paragraph 30e7c1fa-1abc-4d75-ab0b-1eaad7dbf096--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-30e7c1fa-1abc-4d75-ab0b-1eaad7dbf096&quot;&gt;&lt;code&gt;Cause&lt;/code&gt; permet de gérer les cas d&amp;#39;erreur par exception, erreur en contexte d&amp;#39;exécution parallèle, le suivit de la stack dans un contexte d&amp;#39;exécution concurrente.&lt;/p&gt;
&lt;!-- end paragraph 30e7c1fa-1abc-4d75-ab0b-1eaad7dbf096--&gt;
&lt;!-- begin paragraph dc5ad283-f97a-4d02-b80d-9e5ebbc4632d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dc5ad283-f97a-4d02-b80d-9e5ebbc4632d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph dc5ad283-f97a-4d02-b80d-9e5ebbc4632d--&gt;
&lt;!-- begin paragraph 25abcd29-f723-4f37-9685-367c3f03d4bd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-25abcd29-f723-4f37-9685-367c3f03d4bd&quot;&gt;Donc dans le ZManaged, on va :&lt;/p&gt;
&lt;!-- end paragraph 25abcd29-f723-4f37-9685-367c3f03d4bd--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;indiquer comment on va acquérir une &amp;quot;ressource&amp;quot; &lt;code&gt;A&lt;/code&gt; avec &lt;code&gt;ZIO[R, E, A]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Donner la commande que l&amp;#39;on doit exécuter à la fin de l&amp;#39;utilisation de la ressource &lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph c24f9463-72a8-4247-b1ca-003c0b41e536--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c24f9463-72a8-4247-b1ca-003c0b41e536&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c24f9463-72a8-4247-b1ca-003c0b41e536--&gt;
&lt;!-- begin paragraph 73d616a2-6cb4-4dd6-9245-256ec26ce7a4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-73d616a2-6cb4-4dd6-9245-256ec26ce7a4&quot;&gt;Exemple avec le fichier : &lt;/p&gt;
&lt;!-- end paragraph 73d616a2-6cb4-4dd6-9245-256ec26ce7a4--&gt;
&lt;!-- begin code 5af1624c-f72c-46af-8a57-a1ead7dbdd90--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5af1624c-f72c-46af-8a57-a1ead7dbdd90&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object HelloFromScala extends zio.App {

  //ZIO[zio.ZEnv, Nothing, ExitCode]
  override def run(args: List[String]) = prg.useNow.orDie.exitCode

  //ZManaged[Any, Throwable, Unit]
  lazy val prg = printer.mapEffect(obj =&amp;gt; obj.println(&amp;quot;Hello From ZIO!&amp;quot;))
  
	
  //ZManaged[Any, Throwable, PrintWriter]
  lazy val printer = ZManaged.fromAutoCloseable(ZIO(new PrintWriter(&amp;quot;log.txt&amp;quot;)))
} &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5af1624c-f72c-46af-8a57-a1ead7dbdd90--&gt;
&lt;!-- begin paragraph 365fa03d-54ab-46b7-9e38-0bbc5fe951c6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-365fa03d-54ab-46b7-9e38-0bbc5fe951c6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 365fa03d-54ab-46b7-9e38-0bbc5fe951c6--&gt;
&lt;!-- begin paragraph 014eb59d-e438-4fc0-9139-277a431ff9ba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-014eb59d-e438-4fc0-9139-277a431ff9ba&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 014eb59d-e438-4fc0-9139-277a431ff9ba--&gt;
&lt;!-- begin heading_1 7c3fdbdf-ebe4-413b-a7a1-7af9d4d96839--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-7c3fdbdf-ebe4-413b-a7a1-7af9d4d96839&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 7c3fdbdf-ebe4-413b-a7a1-7af9d4d96839--&gt;
&lt;!-- begin paragraph 9e2186ec-cc32-4df0-8dff-ab114f4b831d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e2186ec-cc32-4df0-8dff-ab114f4b831d&quot;&gt;ZIO a revu complètement la façon de composer R et de gérer les ressources pour sortir une version 1.0 avec une ergonomie intéressante.&lt;br /&gt;
&lt;br /&gt;
On a pu voir, avec quelques exemples, comment gérer R, et la capture des ressources.&lt;/p&gt;
&lt;!-- end paragraph 9e2186ec-cc32-4df0-8dff-ab114f4b831d--&gt;
&lt;!-- begin paragraph b1c582d2-4fff-43c8-99eb-cf677686ce36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b1c582d2-4fff-43c8-99eb-cf677686ce36&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b1c582d2-4fff-43c8-99eb-cf677686ce36--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:5f62dccede8c4280b9310059e6d1da32</id>
    <title>Emit-on-change avec Kafka Streams</title>
    <updated>2021-06-15T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/emit-on-change-avec-kafka-streams.html"/>
    <!--summary Dans Kafka Streams, la sémantique emit-on-change fait partie des fonctionnalités attendues du framework. Cependant, la tentative d&#039;implémentation par les contributeurs du projet s&#039;accompagne de risques de perte de données et elle a donc été retirée du framework. Est-il possible de bénéficier de cette sémantique néanmoins ?-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Kafka"></category>    <category term="Kafka Streams"></category>    <category term="Emit on change"></category>    <category term="Stream processing"></category>    <content type="html">
&lt;!-- begin paragraph e3d93788-32a1-4467-a4bb-18d5889d8580--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e3d93788-32a1-4467-a4bb-18d5889d8580&quot;&gt;Imaginez : vous constituez une topologie pour votre service Kafka Streams. Pour éviter de surcharger un consommateur (parce qu&amp;#39;il est un peu lent) ou de surcharger Kafka (parce que son cluster n&amp;#39;a pas la dimension qui convient), sachant que vous avez des messages qui se ressemblent d&amp;#39;un point de vue métier, vous ne souhaitez émettre des messages en sortie que lorsqu&amp;#39;il y a une véritable modification dans les données.&lt;/p&gt;
&lt;!-- end paragraph e3d93788-32a1-4467-a4bb-18d5889d8580--&gt;
&lt;!-- begin paragraph a2d05b5c-5ad7-42e9-9801-edbcf9810770--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a2d05b5c-5ad7-42e9-9801-edbcf9810770&quot;&gt;Pour cela, d&amp;#39;un point de vue code, on pourrait bénéficier d&amp;#39;une méthode &lt;code&gt;withEmitOnChange&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph a2d05b5c-5ad7-42e9-9801-edbcf9810770--&gt;
&lt;!-- begin code a296d721-638d-4a00-b980-ae68a64ddd88--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a296d721-638d-4a00-b980-ae68a64ddd88&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val builder = new StreamsBuilder()

builder.stream(inputStream)
  .withEmitOnChange
  .to(outputStream)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a296d721-638d-4a00-b980-ae68a64ddd88--&gt;
&lt;!-- begin paragraph c20f9982-6984-4c68-925a-6538ee1a7154--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c20f9982-6984-4c68-925a-6538ee1a7154&quot;&gt;Cette fonctionnalité a été imaginée dans Kafka Streams et est référencée dans le document &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-557%3A+Add+emit+on+change+support+for+Kafka+Streams&quot;&gt;KIP-557: Add emit on change support for Kafka Streams&lt;/a&gt;. La solution proposée se base par contre sur une comparaison binaire des données sérialisées pour détecter les modifications.&lt;/p&gt;
&lt;!-- end paragraph c20f9982-6984-4c68-925a-6538ee1a7154--&gt;
&lt;!-- begin paragraph af8931ae-5768-46ae-b0f3-8a7e8897e911--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-af8931ae-5768-46ae-b0f3-8a7e8897e911&quot;&gt;La fonctionnalité a été livrée dans la version 2.6 de Kafka Streams... Puis a été retirée, parce que des pertes de données ont été constatées. Le Confluence du projet Kafka sur la partie &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/Kafka+Streams#KafkaStreams-Adopted&quot;&gt;Adopted&lt;/a&gt; de la page Kafka Streams indique :&lt;/p&gt;
&lt;!-- end paragraph af8931ae-5768-46ae-b0f3-8a7e8897e911--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;KIP-557: Add emit on change support for Kafka Streams (partially implemented in v2.6 reverted again in 2.8.0, 2.7.1, and 2.6.2 due to potential data loss)&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph d13dd2c1-2590-4ca6-a280-0023c4f586bd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d13dd2c1-2590-4ca6-a280-0023c4f586bd&quot;&gt;Mais, commençons avec un exemple.&lt;/p&gt;
&lt;!-- end paragraph d13dd2c1-2590-4ca6-a280-0023c4f586bd--&gt;
&lt;!-- begin heading_1 9c3cb009-30d6-49b7-803d-a48077c1dc4e--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9c3cb009-30d6-49b7-803d-a48077c1dc4e&quot;&gt;Gestion de stock magasin&lt;/h2&gt;
&lt;!-- end heading_1 9c3cb009-30d6-49b7-803d-a48077c1dc4e--&gt;
&lt;!-- begin paragraph 63f0db1e-6dc1-40c7-af1c-0a81e7bea4d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-63f0db1e-6dc1-40c7-af1c-0a81e7bea4d1&quot;&gt;Je gère un magasin et dans ce magasin, je gère le stock des produits que je vends sur mon site e-commerce.&lt;/p&gt;
&lt;!-- end paragraph 63f0db1e-6dc1-40c7-af1c-0a81e7bea4d1--&gt;
&lt;!-- begin paragraph 7def8af8-85e5-4847-a46c-2590057cd586--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7def8af8-85e5-4847-a46c-2590057cd586&quot;&gt;Je peux représenter mon stock produit sous la forme d&amp;#39;une case class.&lt;/p&gt;
&lt;!-- end paragraph 7def8af8-85e5-4847-a46c-2590057cd586--&gt;
&lt;!-- begin code 574888d8-8f22-4486-9a2f-c47faa40f325--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-574888d8-8f22-4486-9a2f-c47faa40f325&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class ProductKey(storeId: String, id: String)
case class Stock(productId: ProductKey, quantity: Double)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 574888d8-8f22-4486-9a2f-c47faa40f325--&gt;
&lt;!-- begin paragraph d16ad9c9-a019-4673-8b82-65eee9fa4b1b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d16ad9c9-a019-4673-8b82-65eee9fa4b1b&quot;&gt;J&amp;#39;ai besoin d&amp;#39;un endpoint qui m&amp;#39;indique si un produit est disponible ou non. Voici la représentation de l&amp;#39;information.&lt;/p&gt;
&lt;!-- end paragraph d16ad9c9-a019-4673-8b82-65eee9fa4b1b--&gt;
&lt;!-- begin code 0c355463-6c27-4982-aeac-101c7c65c701--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0c355463-6c27-4982-aeac-101c7c65c701&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class ProductAvailability(productId: ProductKey, isAvailable: Boolean)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0c355463-6c27-4982-aeac-101c7c65c701--&gt;
&lt;!-- begin paragraph c987c83d-207f-4acf-9bf5-5f9330ec865e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c987c83d-207f-4acf-9bf5-5f9330ec865e&quot;&gt;Ici, le champ &lt;code&gt;isAvailable&lt;/code&gt; est &lt;code&gt;true&lt;/code&gt; que lorsque le stock strictement positif.&lt;/p&gt;
&lt;!-- end paragraph c987c83d-207f-4acf-9bf5-5f9330ec865e--&gt;
&lt;!-- begin paragraph 21ac9e76-9dfb-4d10-9e56-ec10338de547--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-21ac9e76-9dfb-4d10-9e56-ec10338de547&quot;&gt;Nous avons donc là un flux de type &lt;code&gt;KStream[ProductKey, ProductAvailability&lt;/code&gt;&lt;code&gt;]&lt;/code&gt;, en supposant que la structure ProductKey sert ici de clé dans le flux.&lt;/p&gt;
&lt;!-- end paragraph 21ac9e76-9dfb-4d10-9e56-ec10338de547--&gt;
&lt;!-- begin paragraph fef57b6b-e3b6-4599-99df-e53bc9e6bc36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fef57b6b-e3b6-4599-99df-e53bc9e6bc36&quot;&gt;Donc, un nouveau produit avec un stock à 10.0 donnera une disponibilité à &lt;code&gt;true&lt;/code&gt; et j&amp;#39;ai message qui est créé et qui est envoyé en direction de mon endpoint. Si le stock de ce produit passe à 5.0, la disponibilité reste à &lt;code&gt;true&lt;/code&gt;, mais je produis néanmoins un nouveau message pour le endpoint. Si le stock passe à 0.0, alors la disponibilité passe à &lt;code&gt;false&lt;/code&gt; et je produis encore une fois un nouveau message. Ici, seule le deuxième message n&amp;#39;est pas utile.&lt;/p&gt;
&lt;!-- end paragraph fef57b6b-e3b6-4599-99df-e53bc9e6bc36--&gt;
&lt;!-- begin paragraph 6d55184d-2ebc-4376-92be-30f78607b5a3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d55184d-2ebc-4376-92be-30f78607b5a3&quot;&gt;Ce que nous devrions avoir, c&amp;#39;est ce diagramme :&lt;/p&gt;
&lt;!-- end paragraph 6d55184d-2ebc-4376-92be-30f78607b5a3--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5f62dcce-de8c-4280-b931-0059e6d1da32/screenshot_2021-06-10_at_13.34.12.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph e9d65725-c623-4090-95eb-664cdadd0059--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e9d65725-c623-4090-95eb-664cdadd0059&quot;&gt;Le but est de n&amp;#39;émettre un message que lorsque la disponibilité passe de &lt;i&gt;oui&lt;/i&gt; à &lt;i&gt;non&lt;/i&gt; ou de &lt;i&gt;non&lt;/i&gt; à &lt;i&gt;oui&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph e9d65725-c623-4090-95eb-664cdadd0059--&gt;
&lt;!-- begin heading_1 0decb8b7-1bbd-4d38-814c-a97ae8fe94be--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-0decb8b7-1bbd-4d38-814c-a97ae8fe94be&quot;&gt;Peut-on contourner l&amp;#39;absence de sémantique &lt;i&gt;emit-on-change&lt;/i&gt; ?&lt;/h2&gt;
&lt;!-- end heading_1 0decb8b7-1bbd-4d38-814c-a97ae8fe94be--&gt;
&lt;!-- begin paragraph 8c379a4f-5999-4f8e-bb1f-383f5dea9225--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c379a4f-5999-4f8e-bb1f-383f5dea9225&quot;&gt;Kafka Streams propose deux stratégies d&amp;#39;émission de message :&lt;/p&gt;
&lt;!-- end paragraph 8c379a4f-5999-4f8e-bb1f-383f5dea9225--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;emit-on-update&lt;/li&gt;&lt;li&gt;emit-on-window-close&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph a8cb5351-875a-4a19-90e8-279dd3b88077--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a8cb5351-875a-4a19-90e8-279dd3b88077&quot;&gt;&lt;i&gt;emit-on-update&lt;/i&gt; est une stratégie qui va se traduire par une émission de message pour tout message reçu. On a donc une relation 1..1 entre les données entrantes et les données sortantes.&lt;/p&gt;
&lt;!-- end paragraph a8cb5351-875a-4a19-90e8-279dd3b88077--&gt;
&lt;!-- begin paragraph e81bc5a8-5d04-4adf-a040-f7eb35398ede--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e81bc5a8-5d04-4adf-a040-f7eb35398ede&quot;&gt;&lt;i&gt;emit-on-window-close&lt;/i&gt; est une stratégie qui consiste à rassembler un ensemble de messages sur des fenêtres de temps et de les agréger pour produire un message en sortie. On a ici une relation *..1 entre les données entrantes et les données sortantes.&lt;/p&gt;
&lt;!-- end paragraph e81bc5a8-5d04-4adf-a040-f7eb35398ede--&gt;
&lt;!-- begin paragraph f64b0327-49e7-411c-a72a-119ca508a77e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f64b0327-49e7-411c-a72a-119ca508a77e&quot;&gt;La stratégie &lt;i&gt;emit-on-window-close&lt;/i&gt; pourrait être utilisée pour mettre en place une stratégie se rapprochant (mais de loin) de la stratégie &lt;i&gt;emit-on-change&lt;/i&gt;, en émettant seulement le dernier message de chaque fenêtre (pour des fenêtres fixes), si la véritable problématique provient d&amp;#39;un consommateur trop lent. Il faudra alors trouver une taille de fenêtre adéquate permettant de ne pas trop envoyer de message et en même temps ne pas perdre trop d&amp;#39;information.&lt;/p&gt;
&lt;!-- end paragraph f64b0327-49e7-411c-a72a-119ca508a77e--&gt;
&lt;!-- begin heading_1 fb178f11-57c4-4473-829f-e64c79c5326c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-fb178f11-57c4-4473-829f-e64c79c5326c&quot;&gt;Mais peut-on faire mieux ?&lt;/h2&gt;
&lt;!-- end heading_1 fb178f11-57c4-4473-829f-e64c79c5326c--&gt;
&lt;!-- begin paragraph 35fd4bf7-7ce1-41ab-bfb1-f4dd09ae863e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35fd4bf7-7ce1-41ab-bfb1-f4dd09ae863e&quot;&gt;L&amp;#39;idée de base de la stratégie &lt;i&gt;emit-on-change&lt;/i&gt; est de n&amp;#39;émettre un message que si on observe une modification dans les données en entrée.&lt;/p&gt;
&lt;!-- end paragraph 35fd4bf7-7ce1-41ab-bfb1-f4dd09ae863e--&gt;
&lt;!-- begin paragraph 03d77e42-d9f1-4260-8dfd-0977252e7fad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-03d77e42-d9f1-4260-8dfd-0977252e7fad&quot;&gt;Puisque la stratégie &lt;i&gt;emit-on-window-close&lt;/i&gt; ne permet pas vraiment d&amp;#39;approcher de la stratégie &lt;i&gt;emit-on-change&lt;/i&gt;, nous allons partir de la stratégie &lt;i&gt;emit-on-update&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 03d77e42-d9f1-4260-8dfd-0977252e7fad--&gt;
&lt;!-- begin paragraph 1a2b3fc0-164e-4b0d-a6bd-b4bbbf6ce1f1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1a2b3fc0-164e-4b0d-a6bd-b4bbbf6ce1f1&quot;&gt;Nous aurons aussi besoin d&amp;#39;un outil de comparaison permettant de détecter un changement dans la donnée. Il nous faut aussi un stockage pour conserver la dernière version de la donnée, qui servira comme base pour la comparaison avec la donnée arrivant en entrée.&lt;/p&gt;
&lt;!-- end paragraph 1a2b3fc0-164e-4b0d-a6bd-b4bbbf6ce1f1--&gt;
&lt;!-- begin paragraph 41db6608-e4b8-49a6-af11-3fca5ed60e1a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-41db6608-e4b8-49a6-af11-3fca5ed60e1a&quot;&gt;Pour créer notre stockage, nous allons pouvoir nous baser sur un KTable qui sera créé à partir d&amp;#39;un KStream fournit en entrée. Pour cela, nous allons nous baser sur &lt;code&gt;.groupByKey&lt;/code&gt;, puis nous avons le choix entre &lt;code&gt;reduce&lt;/code&gt; et &lt;code&gt;aggregate&lt;/code&gt;. Cependant, &lt;code&gt;reduce&lt;/code&gt; ne permet pas de faire la distinction entre ce qui provient du stream entrant et les données qui proviennent du stockage dans la fonction qu&amp;#39;on lui passe en paramètre. Ce qui n&amp;#39;est pas le cas de l&amp;#39;opération &lt;code&gt;aggregate&lt;/code&gt;. Nous allons nous baser sur cette opération pour générer un KTable.&lt;/p&gt;
&lt;!-- end paragraph 41db6608-e4b8-49a6-af11-3fca5ed60e1a--&gt;
&lt;!-- begin paragraph 5d7e125c-994e-478b-bd78-e7d8783fbd04--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d7e125c-994e-478b-bd78-e7d8783fbd04&quot;&gt;À chaque message qui arrive au niveau d&amp;#39;&lt;code&gt;aggregate&lt;/code&gt;, un message en sort, du fait de la sémantique &lt;i&gt;emit-on-update&lt;/i&gt;. Pour changer de sémantique, nous allons utiliser un filtrage qui permettra d&amp;#39;avoir une sémantique &lt;i&gt;emit-on-change&lt;/i&gt;. Mais pour ça, nous devons ajouter une information, un flag qui indiquera si le message doit être émis ou non.&lt;/p&gt;
&lt;!-- end paragraph 5d7e125c-994e-478b-bd78-e7d8783fbd04--&gt;
&lt;!-- begin paragraph 916ef838-65a0-4c7e-a9b8-da62785f033f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-916ef838-65a0-4c7e-a9b8-da62785f033f&quot;&gt;OK, on va revenir à notre exemple de stock disponible, pour pouvoir mieux concevoir tout ça.&lt;/p&gt;
&lt;!-- end paragraph 916ef838-65a0-4c7e-a9b8-da62785f033f--&gt;
&lt;!-- begin heading_1 b0acf5e8-c979-4afb-97d2-dd2176bc906f--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-b0acf5e8-c979-4afb-97d2-dd2176bc906f&quot;&gt;Emit-on-change sans emit-on-change&lt;/h2&gt;
&lt;!-- end heading_1 b0acf5e8-c979-4afb-97d2-dd2176bc906f--&gt;
&lt;!-- begin paragraph 7a8c9865-0499-4e80-a0c3-da6cc3917240--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7a8c9865-0499-4e80-a0c3-da6cc3917240&quot;&gt;Si nous gardons notre structure ProductAvailability tel que, il n&amp;#39;y a rien qui permettra au filtre de déterminer s&amp;#39;il faut émettre ou non le message. Pour cela il faudrait ajouter un flag qui sera positionné au sein de l&amp;#39;opération &lt;code&gt;aggregate&lt;/code&gt;, qui représente le seul endroit dans la topologie, où il est possible de constater une variation de valeur.&lt;/p&gt;
&lt;!-- end paragraph 7a8c9865-0499-4e80-a0c3-da6cc3917240--&gt;
&lt;!-- begin code eca022e3-64fd-4dbc-a5db-6d97c7998b73--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-eca022e3-64fd-4dbc-a5db-6d97c7998b73&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class ProductAvailabilityState(
  state: ProductAvailability,
  shouldEmit: Boolean
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code eca022e3-64fd-4dbc-a5db-6d97c7998b73--&gt;
&lt;!-- begin paragraph be53e72e-1dd3-4b86-931f-1f3d0610abdc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be53e72e-1dd3-4b86-931f-1f3d0610abdc&quot;&gt;Nous avons besoin ici d&amp;#39;une valeur &lt;code&gt;empty&lt;/code&gt;, car &lt;code&gt;aggregate&lt;/code&gt; a besoin d&amp;#39;une valeur initiale.&lt;/p&gt;
&lt;!-- end paragraph be53e72e-1dd3-4b86-931f-1f3d0610abdc--&gt;
&lt;!-- begin paragraph f7b482fd-abbe-4768-ae5a-d2953491345f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f7b482fd-abbe-4768-ae5a-d2953491345f&quot;&gt;Les règles prises en compte par la fonction passé en paramètre d&amp;#39;&lt;code&gt;aggregate&lt;/code&gt; sont :&lt;/p&gt;
&lt;!-- end paragraph f7b482fd-abbe-4768-ae5a-d2953491345f--&gt;
&lt;!-- begin code 31624f42-7927-47e1-86e0-863546948a62--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-31624f42-7927-47e1-86e0-863546948a62&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val changeLogic:(ProductAvailability, ProductAvailabilityState) =&amp;gt; ProductAvailabilityState = {
  case (availability, null) =&amp;gt;
    ProductAvailabilityState(availability, true)

  case (availability, ProductAvailabilityState(storedValue, _))
      if availability == storedValue =&amp;gt; // &amp;lt;-- the comparison is here
    ProductAvailabilityState(availability, false)

  case (availability, ProductAvailabilityState(_, _)) =&amp;gt;
    ProductAvailabilityState(availability, true)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 31624f42-7927-47e1-86e0-863546948a62--&gt;
&lt;!-- begin paragraph 767f7c0b-1d8b-4d9a-811a-e09a6a3a7f90--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-767f7c0b-1d8b-4d9a-811a-e09a6a3a7f90&quot;&gt;Ici, pour la comparaison, on utilise l&amp;#39;égalité de Scala (comparaison récursive champ par champ).&lt;/p&gt;
&lt;!-- end paragraph 767f7c0b-1d8b-4d9a-811a-e09a6a3a7f90--&gt;
&lt;!-- begin paragraph 28a7add7-4edb-4cd8-aec9-ec6cc381bf22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-28a7add7-4edb-4cd8-aec9-ec6cc381bf22&quot;&gt;En sorti d&amp;#39;&lt;code&gt;aggregate&lt;/code&gt;, nous avons une &lt;code&gt;KTable[ProductKey, ProductAvailabilityState]&lt;/code&gt; que nous pouvons passer au filtre. Ce filtre va simplement vérifier que le flag &lt;code&gt;shouldEmit&lt;/code&gt; de notre structure est à &lt;code&gt;true&lt;/code&gt;. Après quoi, avec un &lt;code&gt;mapValues&lt;/code&gt;, on récupère le champ &lt;code&gt;state&lt;/code&gt;. Ce qui nous donne en sortie un flux de type &lt;code&gt;KStream[ProductKey, ProductAvailability]&lt;/code&gt; avec une sémantique &lt;i&gt;emit-on-change&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 28a7add7-4edb-4cd8-aec9-ec6cc381bf22--&gt;
&lt;!-- begin paragraph e40c4a44-59e6-44c8-8611-9fd814ecd050--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e40c4a44-59e6-44c8-8611-9fd814ecd050&quot;&gt;Voici le code correspondant pour la topologie.&lt;/p&gt;
&lt;!-- end paragraph e40c4a44-59e6-44c8-8611-9fd814ecd050--&gt;
&lt;!-- begin code 9fb54ea9-8eef-489f-9d10-c76fad8cd548--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9fb54ea9-8eef-489f-9d10-c76fad8cd548&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;availabilityStream
  .groupByKey
  .aggregate(null: ProductAvailabilityState) {
    case (availability, null) =&amp;gt;
      ProductAvailabilityState(availability, true)

    case (availability, ProductAvailabilityState(storedValue, _))
        if availability == storedValue =&amp;gt;
      ProductAvailabilityState(availability, false)

    case (availability, ProductAvailabilityState(_, _)) =&amp;gt;
      ProductAvailabilityState(availability, true)
  }
  .filter(_.shouldEmit)   // apply emit-on-change semantic
  .mapValues(_.state.get) // due to filter we are sure to have an instance of Some&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9fb54ea9-8eef-489f-9d10-c76fad8cd548--&gt;
&lt;!-- begin paragraph 35888708-717c-43e5-8cc2-e9ec203909ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35888708-717c-43e5-8cc2-e9ec203909ae&quot;&gt;Ce qui forme la topologie suivante :&lt;/p&gt;
&lt;!-- end paragraph 35888708-717c-43e5-8cc2-e9ec203909ae--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5f62dcce-de8c-4280-b931-0059e6d1da32/screenshot_2021-06-09_at_17.04.42.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 97d59911-6288-4130-bfef-eabcd4a85055--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-97d59911-6288-4130-bfef-eabcd4a85055&quot;&gt;Cette topologie met en œuvre le workflow ci-dessous.&lt;/p&gt;
&lt;!-- end paragraph 97d59911-6288-4130-bfef-eabcd4a85055--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5f62dcce-de8c-4280-b931-0059e6d1da32/screenshot_2021-06-09_at_11.41.01.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 9904cd5d-283b-48b0-995d-bd280a27b1ab--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9904cd5d-283b-48b0-995d-bd280a27b1ab&quot;&gt;Généralisons&lt;/h2&gt;
&lt;!-- end heading_1 9904cd5d-283b-48b0-995d-bd280a27b1ab--&gt;
&lt;!-- begin paragraph e5399a72-757f-4fac-ae64-6c70b9e808af--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5399a72-757f-4fac-ae64-6c70b9e808af&quot;&gt;Il est possible de généraliser ce que nous venons de voir à n&amp;#39;importe quel type d&amp;#39;entité utilisée dans vos topologies Kafka Streams.&lt;/p&gt;
&lt;!-- end paragraph e5399a72-757f-4fac-ae64-6c70b9e808af--&gt;
&lt;!-- begin code a3af5867-ef26-4b85-8ce5-63c3a7ea7bb0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a3af5867-ef26-4b85-8ce5-63c3a7ea7bb0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class State[A: Equiv](data: A, shouldEmit: Boolean) {
  def changeBasedOn(newData: A): State[A] =
    State(newData, shouldEmit = !Equiv[A].equiv(data, newData))
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a3af5867-ef26-4b85-8ce5-63c3a7ea7bb0--&gt;
&lt;!-- begin paragraph 3c588576-16ad-46e3-9f5d-712e1442f0ba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3c588576-16ad-46e3-9f5d-712e1442f0ba&quot;&gt;La contrainte Equiv (provenant du package scala.math), implique d&amp;#39;apporter pour le type A un prédicat permettant de vérifier l&amp;#39;équivalence entre deux éléments du type A.&lt;/p&gt;
&lt;!-- end paragraph 3c588576-16ad-46e3-9f5d-712e1442f0ba--&gt;
&lt;!-- begin paragraph 68c73e2c-6b01-4e14-be73-8174fc0966a1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-68c73e2c-6b01-4e14-be73-8174fc0966a1&quot;&gt;On peut aussi ajouter aux KStream une opération indiquant explicitement que vous passez en sémantique &lt;i&gt;emit-on-change&lt;/i&gt;. Il faudra néanmoins fournir une serde pour les entités de type &lt;code&gt;State[A]&lt;/code&gt;. Pour on peut utiliser les méthodes d&amp;#39;extension de Scala.&lt;/p&gt;
&lt;!-- end paragraph 68c73e2c-6b01-4e14-be73-8174fc0966a1--&gt;
&lt;!-- begin code ec99413f-fd3d-41e1-b5f7-9f426568a5d6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ec99413f-fd3d-41e1-b5f7-9f426568a5d6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit class KStreamEmitOnChange[K, V: Equiv](ks: KStream[K, V]) {
  def withEmitOnChange(implicit
        keySerde: Serde[K],
        serde: Serde[V],
        stateSerde: Serde[State[V]]): KStream[K, V] =
    ks
      .groupByKey(Grouped.`with`(keySerde, serde))
      .aggregate(null: State[V]) {
        case (_, data, null) =&amp;gt; State(data, shouldEmit = true)
        case (_, data, state) =&amp;gt; state.changeBasedOn(data)
      }(
        Materialized.`with`(keySerde, stateSerde)
      )
      .toStream
      .filter { case (_, state) =&amp;gt; state.shouldEmit }
      .mapValues(_.data.get)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ec99413f-fd3d-41e1-b5f7-9f426568a5d6--&gt;
&lt;!-- begin paragraph 616d7ef0-d9ac-4a2c-a066-de2e003ac566--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-616d7ef0-d9ac-4a2c-a066-de2e003ac566&quot;&gt;Ce qui dans notre exemple donnerait&lt;/p&gt;
&lt;!-- end paragraph 616d7ef0-d9ac-4a2c-a066-de2e003ac566--&gt;
&lt;!-- begin code b01a5248-a9a0-488c-822b-3e6505027b1c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b01a5248-a9a0-488c-822b-3e6505027b1c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit val stateSerde: Serde[ProductAvailability] = ???

val builder = new StreamsBuilder()

builder.stream(inputStream)
  .withEmitOnChange
  .to(outputStream)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b01a5248-a9a0-488c-822b-3e6505027b1c--&gt;
&lt;!-- begin heading_1 6aa2e1ae-032c-4b67-bc62-5b4660414bcd--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6aa2e1ae-032c-4b67-bc62-5b4660414bcd&quot;&gt;Parlons performance et volume&lt;/h2&gt;
&lt;!-- end heading_1 6aa2e1ae-032c-4b67-bc62-5b4660414bcd--&gt;
&lt;!-- begin paragraph 10c929d1-82d5-4a0f-9e87-9291b63013ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-10c929d1-82d5-4a0f-9e87-9291b63013ee&quot;&gt;La sémantique &lt;i&gt;emit-on-change&lt;/i&gt; présentée ici implique de stocker des données dans un &lt;i&gt;state store&lt;/i&gt; local au service Kafka Streams exécuté. Ce volume est relatif à la taille de la structure gérée (dans notre exemple, il s&amp;#39;agit de ProductAvailability) à laquelle il faut ajouter la taille utilisée pour le flag (selon le format de sérialisation, c&amp;#39;est soit une chaîne de caractères de taille 4 (&lt;code&gt;true&lt;/code&gt;) ou 5 (&lt;code&gt;false&lt;/code&gt;), soit un entier sur un octet (8 bit), soit un bit). Cette valeur est à multiplier par le nombre de clé possible.&lt;/p&gt;
&lt;!-- end paragraph 10c929d1-82d5-4a0f-9e87-9291b63013ee--&gt;
&lt;!-- begin paragraph 80293dc7-e33a-40f7-9b88-ef25eaf29db7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-80293dc7-e33a-40f7-9b88-ef25eaf29db7&quot;&gt;Si nous reprenons notre exemple avec un format de sérialisation Avro (sans ajout d&amp;#39;en-tête), en supposant que les produits et les magasins sont encodés sur 5 caractères, nous avons donc 13 octets par instance de ProductAvailability (productId et storeId font 2 * (5 octets + 1 octet pour la taille) et le flag fait 1 octet). Si nous avons 5 magasins et 1000 produits par magasin, il faudra prévoir un espace de 65 Ko juste pour la partie data, sachant, qu&amp;#39;il faut ajouter l&amp;#39;ensemble des métadonnées gérées par la couche de persistance RocksDB et plus si vous faites de la réplication.&lt;/p&gt;
&lt;!-- end paragraph 80293dc7-e33a-40f7-9b88-ef25eaf29db7--&gt;
&lt;!-- begin paragraph dce12759-4691-4ab8-9321-3ca5987aad6a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dce12759-4691-4ab8-9321-3ca5987aad6a&quot;&gt;A priori, dans notre code, nous n&amp;#39;introduisons pas de changement de clé. Il n&amp;#39;y aura pas d&amp;#39;échange de données si vous multipliez les instances de votre service Kafka Streams. Donc la solution devrait &lt;b&gt;scaler&lt;/b&gt;. L&amp;#39;exception à cette règle est lorsque &lt;code&gt;withEmitOnChange&lt;/code&gt; est précédée d&amp;#39;une opération &lt;i&gt;lazy&lt;/i&gt; dans laquelle un changement de clé effectué, ce que permet par exemple &lt;code&gt;.map&lt;/code&gt;. Dans ce cas, l&amp;#39;opération &lt;code&gt;.groupByKey&lt;/code&gt; va exécuter un re-partitionnement des données. J&amp;#39;écris cependant au conditionnel ici, car je me fis à la documentation Kafka Streams et à l&amp;#39;expérience que j&amp;#39;ai sur les opérations Kafka Streams que j&amp;#39;utilise dans le code. Mais je n&amp;#39;ai actuellement pas de retour suffisamment parlant pour la solution &lt;i&gt;emit-on-change&lt;/i&gt; présentée dans cet article.&lt;/p&gt;
&lt;!-- end paragraph dce12759-4691-4ab8-9321-3ca5987aad6a--&gt;
&lt;!-- begin heading_1 d15b52cf-a9eb-44b1-9d08-fda3c7e38401--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d15b52cf-a9eb-44b1-9d08-fda3c7e38401&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 d15b52cf-a9eb-44b1-9d08-fda3c7e38401--&gt;
&lt;!-- begin paragraph d7d1bb18-42b0-4b94-8928-ffb758980cfd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d7d1bb18-42b0-4b94-8928-ffb758980cfd&quot;&gt;Nous avons vu dans cet article que la sémantique &lt;i&gt;emit-on-change&lt;/i&gt; apporte un moyen de ne pas surcharger Kafka dans de consommateur trop lent ou d&amp;#39;un cluster sous-dimensionné. Il y a une volonté chez les contributeurs Kafka Streams de mettre en place une sémantique &lt;i&gt;emit-on-change&lt;/i&gt;, mais cette fonctionnalité rencontre des problèmes de perte de données et est donc retirée.&lt;/p&gt;
&lt;!-- end paragraph d7d1bb18-42b0-4b94-8928-ffb758980cfd--&gt;
&lt;!-- begin paragraph b831a158-4067-4d03-91e3-b4794c05cc2a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b831a158-4067-4d03-91e3-b4794c05cc2a&quot;&gt;Nous proposons ainsi une solution intégrant une sémantique &lt;i&gt;emit-on-change&lt;/i&gt; qui se base sur les opérations du DSL Kafka Streams. Cette solution est assez simple et n&amp;#39;entraverait pas la scalabilité de l&amp;#39;application.&lt;/p&gt;
&lt;!-- end paragraph b831a158-4067-4d03-91e3-b4794c05cc2a--&gt;
&lt;!-- begin paragraph bd0cd0ee-2ff4-4cd3-8245-a14c1a8dfb67--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd0cd0ee-2ff4-4cd3-8245-a14c1a8dfb67&quot;&gt;Il est intéressant de voir qu&amp;#39;avec Kafka Streams il est possible d&amp;#39;ajouter des fonctionnalités au framework en se basant sur son propre DSL. Il est ainsi possible d&amp;#39;imaginer et de cataloguer des patterns de topologie pour faciliter la mise en place sémantiques qui ne sont pas directement couverts par Kafka Streams.&lt;/p&gt;
&lt;!-- end paragraph bd0cd0ee-2ff4-4cd3-8245-a14c1a8dfb67--&gt;
&lt;!-- begin paragraph a8593475-e91e-4265-9a4e-58a23d9ec909--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a8593475-e91e-4265-9a4e-58a23d9ec909&quot;&gt;Une proposition alternative est présentée &lt;a href=&quot;https://stackoverflow.com/a/61348248/645535&quot;&gt;dans cette réponse dans Stack Overflow&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph a8593475-e91e-4265-9a4e-58a23d9ec909--&gt;
&lt;!-- begin paragraph 5b04bd9f-518e-49a4-941c-71a144768795--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5b04bd9f-518e-49a4-941c-71a144768795&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5b04bd9f-518e-49a4-941c-71a144768795--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:555bb25f8ecf46da90ee568eab8046bf</id>
    <title>JAVA 16: Premier contact avec l&#039;API de vectorisation</title>
    <updated>2021-06-14T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/java-16-premier-contact-avec-l-api-de-vectorisation.html"/>
    <!--summary Dans cet article on va regarder comment exécuter des opérations vectorielles avec l&#039;API de Java 16-->    <author>
      <name>Bastien Guihard</name>
      <uri>https://univalence.io/blog/auteurs/bastien-guihard.html</uri>
    </author>        <category term="Java"></category>    <category term="débutant"></category>    <category term="Vectorisation"></category>    <category term="loom"></category>    <category term="Java 16"></category>    <content type="html">
&lt;!-- begin heading_2 36681a41-9c74-492e-86b0-01fc6f6df6d7--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-36681a41-9c74-492e-86b0-01fc6f6df6d7&quot;&gt;Introduction&lt;/h3&gt;
&lt;!-- end heading_2 36681a41-9c74-492e-86b0-01fc6f6df6d7--&gt;
&lt;!-- begin paragraph 444a2cd1-4564-42a4-b9f4-489f7cd14ffa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-444a2cd1-4564-42a4-b9f4-489f7cd14ffa&quot;&gt;Java 16 viens d&amp;#39;être annoncé par Oracle avec un certain nombre de nouveautés intéressantes à &lt;a href=&quot;https://www.oracle.com/fr/news/announcement/oracle-announces-java-16-2021-03-16.html&quot;&gt;voir en détail ici&lt;/a&gt;. L&amp;#39;une d&amp;#39;entre elles est la possibilité d&amp;#39;écrire des opérations qui utilisent la pleine puissance des processeurs vectoriels.&lt;/p&gt;
&lt;!-- end paragraph 444a2cd1-4564-42a4-b9f4-489f7cd14ffa--&gt;
&lt;!-- begin paragraph 492523e5-aa7c-4e34-a97c-acde2ac30398--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-492523e5-aa7c-4e34-a97c-acde2ac30398&quot;&gt;On va regarder ensemble rapidement ce qu&amp;#39;il en est et comment utiliser cette API.&lt;/p&gt;
&lt;!-- end paragraph 492523e5-aa7c-4e34-a97c-acde2ac30398--&gt;
&lt;!-- begin heading_2 8f84f281-a174-45b7-83b0-f39da658b241--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8f84f281-a174-45b7-83b0-f39da658b241&quot;&gt;Intérêt et cas d&amp;#39;usage&lt;/h3&gt;
&lt;!-- end heading_2 8f84f281-a174-45b7-83b0-f39da658b241--&gt;
&lt;!-- begin paragraph 1caeb968-620a-4327-af7b-5d51573b69ba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1caeb968-620a-4327-af7b-5d51573b69ba&quot;&gt;Quand on parle de vectorisation, on parle effectivement de parallélisme, mais pas n&amp;#39;importe lequel. En effet, on pourrait paralléliser un calcul sur plusieurs machines. On parlerait alors d&amp;#39;une architecture en &lt;b&gt;cluster.  &lt;/b&gt;On pourrait paralléliser un calcul sur plusieurs cœurs d&amp;#39;un même processeur. On parlerait alors de parallélisation &lt;b&gt;inter-coeur.&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 1caeb968-620a-4327-af7b-5d51573b69ba--&gt;
&lt;!-- begin paragraph 0776eaac-0aec-4273-9f91-b8ef643a763a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0776eaac-0aec-4273-9f91-b8ef643a763a&quot;&gt;Dans notre cas, on va chercher à effectuer une parallélisation &lt;b&gt;intra-coeur&lt;/b&gt;, c&amp;#39;est-à-dire qu&amp;#39;on va paralléliser un calcul au sein d&amp;#39;un seul cœur à l&amp;#39;aide d&amp;#39;instructions particulières.&lt;/p&gt;
&lt;!-- end paragraph 0776eaac-0aec-4273-9f91-b8ef643a763a--&gt;
&lt;!-- begin paragraph f8322e34-9dc6-4b77-aaf7-20cd69068035--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f8322e34-9dc6-4b77-aaf7-20cd69068035&quot;&gt;On est alors en mesure de se poser la question suivante : &amp;quot;Pourquoi diable voudrait-on s&amp;#39;infliger ça&amp;quot; ?&lt;/p&gt;
&lt;!-- end paragraph f8322e34-9dc6-4b77-aaf7-20cd69068035--&gt;
&lt;!-- begin paragraph f45a5d97-993d-40b5-855a-edfcefe6c219--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f45a5d97-993d-40b5-855a-edfcefe6c219&quot;&gt;Les unités de calcul vectoriel sont très présentes dans les GPU, mais on en trouve aussi dans la plupart des processeurs grand public. Avant ça, son usage était réservé aux supercalculateurs (ie. la grande époque des Cray).&lt;/p&gt;
&lt;!-- end paragraph f45a5d97-993d-40b5-855a-edfcefe6c219--&gt;
&lt;!-- begin paragraph e2eb97aa-1ea9-424d-8918-d5f5796e835a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2eb97aa-1ea9-424d-8918-d5f5796e835a&quot;&gt;L’intérêt du calcul vectoriel est qu&amp;#39;il permet de réduire le nombre de chargements d&amp;#39;instructions. En effet, avec une approche traditionnelle, l&amp;#39;augmentation du nombre de cycles par instruction accélère l&amp;#39;horloge du CPU. Utiliser une approche vectorielle garantie des performances intéressantes comme nous le verrons plus tard dans cet article.&lt;/p&gt;
&lt;!-- end paragraph e2eb97aa-1ea9-424d-8918-d5f5796e835a--&gt;
&lt;!-- begin paragraph f2c7e9cf-7333-4e1f-bb4d-2e0c47dd8830--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f2c7e9cf-7333-4e1f-bb4d-2e0c47dd8830&quot;&gt;L&amp;#39;optimisation vectorielle permet alors aux processeurs de consommer moins d’électricité et par effet, de prendre plus soin de la planète💡🌎. &lt;/p&gt;
&lt;!-- end paragraph f2c7e9cf-7333-4e1f-bb4d-2e0c47dd8830--&gt;
&lt;!-- begin paragraph e2a53fc2-6977-42c3-9be3-18806cc83321--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2a53fc2-6977-42c3-9be3-18806cc83321&quot;&gt;&lt;b&gt;Cas d&amp;#39;usage&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph e2a53fc2-6977-42c3-9be3-18806cc83321--&gt;
&lt;!-- begin paragraph f9b390bf-f209-4f23-9899-062b84fa5871--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9b390bf-f209-4f23-9899-062b84fa5871&quot;&gt;Typiquement, on peut utiliser la vectorisation pour toutes les opérations faites sur un tableau d’élément.&lt;/p&gt;
&lt;!-- end paragraph f9b390bf-f209-4f23-9899-062b84fa5871--&gt;
&lt;!-- begin paragraph a46e7f2a-aeba-429f-9dca-b2bdcc158934--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a46e7f2a-aeba-429f-9dca-b2bdcc158934&quot;&gt;Prenons l&amp;#39;exemple du calcul de la distance euclidienne entre deux points.&lt;/p&gt;
&lt;!-- end paragraph a46e7f2a-aeba-429f-9dca-b2bdcc158934--&gt;
&lt;!-- begin paragraph 0ede1607-2a97-47c4-b375-ad263b0945f5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0ede1607-2a97-47c4-b375-ad263b0945f5&quot;&gt;On pourrait modéliser ce problème sous forme de calcul vectoriel comme suit :&lt;/p&gt;
&lt;!-- end paragraph 0ede1607-2a97-47c4-b375-ad263b0945f5--&gt;
&lt;!-- begin paragraph 634b78b0-69de-427f-82b2-f1d97a6a081f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-634b78b0-69de-427f-82b2-f1d97a6a081f&quot;&gt;Pensons deux vecteurs A et B avec :&lt;/p&gt;
&lt;!-- end paragraph 634b78b0-69de-427f-82b2-f1d97a6a081f--&gt;&lt;!-- begin equation ada8c8f8-43f1-4c53-848c-9b073098c54b--&gt;
&lt;!-- \vec{A} = Xa,Ya,Za\newline
\vec{B} = Xb,Yb,Zb--&gt;
&lt;section class=&quot;block-equation level-0&quot; id=&quot;e-ada8c8f8-43f1-4c53-848c-9b073098c54b&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot;&gt;&lt;span class=&quot;mjx-math&quot; style=&quot;width: 100%;&quot; aria-label=&quot;\vec{A} = Xa,Ya,Za\newline
\vec{B} = Xb,Yb,Zb&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; style=&quot;width: 100%;&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-stack&quot; style=&quot;width: 100%; vertical-align: -1.677em;&quot;&gt;&lt;span class=&quot;mjx-block&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-box&quot;&gt;&lt;span class=&quot;mjx-texatom&quot;&gt;&lt;span class=&quot;mjx-mrow&quot;&gt;&lt;span class=&quot;mjx-munderover&quot;&gt;&lt;span class=&quot;mjx-stack&quot;&gt;&lt;span class=&quot;mjx-over&quot; style=&quot;height: 0.248em; padding-bottom: 0.06em; padding-left: 0.264em;&quot;&gt;&lt;span class=&quot;mjx-mo&quot; style=&quot;vertical-align: top;&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-vec-R&quot; style=&quot;padding-top: 0.519em; padding-bottom: 0.225em;&quot;&gt;→&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-op&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.519em; padding-bottom: 0.298em;&quot;&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mo MJXc-space3&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.077em; padding-bottom: 0.298em;&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi MJXc-space3&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.024em;&quot;&gt;X&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.225em; padding-bottom: 0.298em;&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;margin-top: -0.144em; padding-bottom: 0.519em;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi MJXc-space1&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.182em;&quot;&gt;Y&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.225em; padding-bottom: 0.298em;&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;margin-top: -0.144em; padding-bottom: 0.519em;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi MJXc-space1&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.04em;&quot;&gt;Z&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.225em; padding-bottom: 0.298em;&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-block&quot; style=&quot;text-align: center; padding-top: 0.442em;&quot;&gt;&lt;span class=&quot;mjx-box&quot;&gt;&lt;span class=&quot;mjx-mspace&quot; style=&quot;width: 0px; height: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mjx-texatom&quot;&gt;&lt;span class=&quot;mjx-mrow&quot;&gt;&lt;span class=&quot;mjx-munderover&quot;&gt;&lt;span class=&quot;mjx-stack&quot;&gt;&lt;span class=&quot;mjx-over&quot; style=&quot;height: 0.248em; padding-bottom: 0.06em; padding-left: 0.213em;&quot;&gt;&lt;span class=&quot;mjx-mo&quot; style=&quot;vertical-align: top;&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-vec-R&quot; style=&quot;padding-top: 0.519em; padding-bottom: 0.225em;&quot;&gt;→&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-op&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;B&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mo MJXc-space3&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.077em; padding-bottom: 0.298em;&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi MJXc-space3&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.024em;&quot;&gt;X&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;margin-top: -0.144em; padding-bottom: 0.519em;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi MJXc-space1&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.182em;&quot;&gt;Y&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;margin-top: -0.144em; padding-bottom: 0.519em;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi MJXc-space1&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.04em;&quot;&gt;Z&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/section&gt;&lt;!-- end equation ada8c8f8-43f1-4c53-848c-9b073098c54b--&gt;
&lt;!-- begin table cbd3f839-f499-4142-89d9-635e2a4d51ff--&gt;
&lt;table class=&quot;block-table level-&quot; id=&quot;t-cbd3f839-f499-4142-89d9-635e2a4d51ff&quot;&gt;  &lt;thead&gt;
    &lt;tr&gt;      &lt;th scope=&quot;col&quot;&gt;lane 0&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;lane 1&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;lane 2&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;nbr opérations&lt;/th&gt;    &lt;/tr&gt;
  &lt;/thead&gt;  &lt;tbody&gt;
                    &lt;tr&gt;
                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Xa&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.024em;&quot;&gt;X&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.225em; padding-bottom: 0.298em;&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Ya&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.182em;&quot;&gt;Y&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.225em; padding-bottom: 0.298em;&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Za&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.04em;&quot;&gt;Z&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.225em; padding-bottom: 0.298em;&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;-&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.298em; padding-bottom: 0.446em;&quot;&gt;−&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;-&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.298em; padding-bottom: 0.446em;&quot;&gt;−&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;-&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.298em; padding-bottom: 0.446em;&quot;&gt;−&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;1&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mn&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.372em; padding-bottom: 0.372em;&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Xb&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.024em;&quot;&gt;X&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Yb&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.182em;&quot;&gt;Y&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Zb&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.04em;&quot;&gt;Z&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;*&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.151em; padding-bottom: 0.298em;&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;*&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.151em; padding-bottom: 0.298em;&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;*&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.151em; padding-bottom: 0.298em;&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;2&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mn&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.372em; padding-bottom: 0.372em;&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;reduce lanes (ADD)&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Yb&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.182em;&quot;&gt;Y&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;Zb&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em; padding-right: 0.04em;&quot;&gt;Z&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mjx-mi&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-math-I&quot; style=&quot;padding-top: 0.446em; padding-bottom: 0.298em;&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;3&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mn&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.372em; padding-bottom: 0.372em;&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;√&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-texatom&quot;&gt;&lt;span class=&quot;mjx-mrow&quot;&gt;&lt;span class=&quot;mjx-mo&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.519em; padding-bottom: 0.519em;&quot;&gt;√&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;                  &lt;td&gt;&lt;!-- begin equation--&gt;&lt;span class=&quot;equation&quot;&gt;&lt;span class=&quot;mjx-chtml MJXc-display&quot; style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;mjx-math&quot; aria-label=&quot;4&quot;&gt;&lt;span class=&quot;mjx-mrow&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;mjx-mn&quot;&gt;&lt;span class=&quot;mjx-char MJXc-TeX-main-R&quot; style=&quot;padding-top: 0.372em; padding-bottom: 0.372em;&quot;&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!-- end equation --&gt;&lt;/td&gt;          &lt;/tr&gt;      &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- end table cbd3f839-f499-4142-89d9-635e2a4d51ff--&gt;
&lt;!-- begin paragraph 6a015e24-88a8-4264-bfb1-e00574d40d48--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a015e24-88a8-4264-bfb1-e00574d40d48&quot;&gt;On remarque que si le nombre de dimensions de notre vecteur augmente - &lt;i&gt;dans une certaine mesure&lt;/i&gt; - le nombre d&amp;#39;operations de notre calcul ne change pas.&lt;/p&gt;
&lt;!-- end paragraph 6a015e24-88a8-4264-bfb1-e00574d40d48--&gt;
&lt;!-- begin heading_2 502a1488-8ec0-4c36-b729-537c55d8120e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-502a1488-8ec0-4c36-b729-537c55d8120e&quot;&gt;Prérequis&lt;/h3&gt;
&lt;!-- end heading_2 502a1488-8ec0-4c36-b729-537c55d8120e--&gt;
&lt;!-- begin heading_3 a882b4b5-3e31-41e4-a43f-8811afe43f38--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-a882b4b5-3e31-41e4-a43f-8811afe43f38&quot;&gt;IDE&lt;/h4&gt;
&lt;!-- end heading_3 a882b4b5-3e31-41e4-a43f-8811afe43f38--&gt;
&lt;!-- begin paragraph 5dc8ea7c-2ec7-4277-9fc6-69564560186a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5dc8ea7c-2ec7-4277-9fc6-69564560186a&quot;&gt;Pour pouvoir utiliser un IDE avec Java 16, il vous faudra certainement le mettre à jour :&lt;/p&gt;
&lt;!-- end paragraph 5dc8ea7c-2ec7-4277-9fc6-69564560186a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;IntelliJ&lt;/b&gt;:&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 0579baeb-b6f5-4c26-85a2-95b516b8eeb0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0579baeb-b6f5-4c26-85a2-95b516b8eeb0&quot;&gt;Il vous faudra une version supérieure ou égale à &lt;a href=&quot;https://www.jetbrains.com/idea/download/#section=mac&quot;&gt;2021.1.1&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 0579baeb-b6f5-4c26-85a2-95b516b8eeb0--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Eclipse&lt;/b&gt;:&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 5597fcb1-7734-47bd-b7c8-592f9d1b9b31--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5597fcb1-7734-47bd-b7c8-592f9d1b9b31&quot;&gt;Il vous faudra une version supérieure ou égale à &lt;a href=&quot;http://download.eclipse.org/eclipse/downloads/drops4/S-4.20M1-202104071800/&quot;&gt;4.20M1&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 5597fcb1-7734-47bd-b7c8-592f9d1b9b31--&gt;
&lt;!-- begin heading_3 cf18601c-7ea5-4cf2-99a7-e2dfe6221617--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-cf18601c-7ea5-4cf2-99a7-e2dfe6221617&quot;&gt;Installer Java 16&lt;/h4&gt;
&lt;!-- end heading_3 cf18601c-7ea5-4cf2-99a7-e2dfe6221617--&gt;
&lt;!-- begin paragraph 47ad00a7-aa13-4078-b027-ad2f339b12a2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-47ad00a7-aa13-4078-b027-ad2f339b12a2&quot;&gt;Le JDK 16 est disponible sur le site officiel d&amp;#39;oracle vous pouvez &lt;a href=&quot;https://www.oracle.com/java/technologies/javase-jdk16-downloads.html&quot;&gt;le trouver ici&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 47ad00a7-aa13-4078-b027-ad2f339b12a2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Distributions linux&lt;/b&gt; :&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 121d944d-d748-483f-8a62-f4c3023da4b6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-121d944d-d748-483f-8a62-f4c3023da4b6&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo dnf install java-latest-openjdk.x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 121d944d-d748-483f-8a62-f4c3023da4b6--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;OSX&lt;/b&gt; :&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 3dbc94d3-bd75-4d96-941d-c6c7877e2170--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3dbc94d3-bd75-4d96-941d-c6c7877e2170&quot;&gt;Installation manuelle :&lt;/p&gt;
&lt;!-- end paragraph 3dbc94d3-bd75-4d96-941d-c6c7877e2170--&gt;
&lt;!-- begin code 2184a546-de5d-4cfe-966a-cbc711d1be37--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2184a546-de5d-4cfe-966a-cbc711d1be37&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# extract your JDK here
cd /Library/Java/JavaVirtualMachines/
# Change your JAVA_HOME to /Library/Java/JavaVirtualMachines/jdk-16.jdk/Contents/Home&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2184a546-de5d-4cfe-966a-cbc711d1be37--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Si vous n&amp;#39;utilisez pas déjà Jenv &lt;/b&gt;:&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code d7d62e44-3d08-459e-9aa6-ab11785a7fd8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d7d62e44-3d08-459e-9aa6-ab11785a7fd8&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# installez jenv : https://www.jenv.be/
jenv add /System/Library/Java/JavaVirtualMachines/jdk-16.jdk/Contents/Home
jenv global 16&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d7d62e44-3d08-459e-9aa6-ab11785a7fd8--&gt;
&lt;!-- begin heading_3 b64cf566-776b-44d9-ae86-1d795e0c8658--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-b64cf566-776b-44d9-ae86-1d795e0c8658&quot;&gt;Configuration d&amp;#39;IntelliJ &lt;/h4&gt;
&lt;!-- end heading_3 b64cf566-776b-44d9-ae86-1d795e0c8658--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Pour les flemmards&lt;/b&gt; :&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 0005b500-327a-4d5b-9726-d4b9c28d7423--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0005b500-327a-4d5b-9726-d4b9c28d7423&quot;&gt;Je vous ai préparé un projet maven &lt;i&gt;out of the box&lt;/i&gt; à lancer dans votre IDE &lt;a href=&quot;https://github.com/guihardbastien/MAVEN-JAVA16-INTELLIJ-BOILERPLATE&quot;&gt;ici&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 0005b500-327a-4d5b-9726-d4b9c28d7423--&gt;
&lt;!-- begin paragraph d3f239a3-2cc4-47ed-83c6-eb6110a5cdcf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3f239a3-2cc4-47ed-83c6-eb6110a5cdcf&quot;&gt;Au passage voici un petit &lt;i&gt;tips&lt;/i&gt; de productivité : vous pouvez lancer vos projets dans IntelliJ directement depuis le terminal en ajoutant un petit alias a votre &lt;b&gt;.zshrc&lt;/b&gt; comme ceci&lt;/p&gt;
&lt;!-- end paragraph d3f239a3-2cc4-47ed-83c6-eb6110a5cdcf--&gt;
&lt;!-- begin code 931fff44-f568-4e15-91f3-6b86e78b2338--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-931fff44-f568-4e15-91f3-6b86e78b2338&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# open current dir in intelliJ
 alias intellijize=&amp;quot;open -na &amp;#39;IntelliJ IDEA CE.app&amp;#39; --args &amp;#39;./&amp;#39;&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 931fff44-f568-4e15-91f3-6b86e78b2338--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Config manuelle&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;pom.xml:&lt;ul class=&quot;block-bulleted_list_item level-2&quot;&gt;&lt;li&gt;dependencies : &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 03b6e02d-ccc2-4041-84bc-c9d69ad3c802--&gt;
&lt;pre class=&quot;block-code level-2&quot; id=&quot;c-03b6e02d-ccc2-4041-84bc-c9d69ad3c802&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;dependencies&amp;gt;
&amp;lt;!--Unit testing--&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;junit-jupiter-api&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;5.7.1&amp;lt;/version&amp;gt;
      &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;junit-jupiter-params&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;5.7.1&amp;lt;/version&amp;gt;
      &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;junit-jupiter-engine&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;5.7.1&amp;lt;/version&amp;gt;
      &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;!--Benchmarking tools for perf review--&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.openjdk.jmh&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;jmh-core&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;1.28&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.openjdk.jmh&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;jmh-generator-annprocess&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;1.28&amp;lt;/version&amp;gt;
      &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
  &amp;lt;/dependencies&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 03b6e02d-ccc2-4041-84bc-c9d69ad3c802--&gt;&lt;ul class=&quot;block-bulleted_list_item level-2&quot;&gt;&lt;li&gt;Build:&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 0b7b5a90-cc7f-47cf-80e6-1dcc00924657--&gt;
&lt;pre class=&quot;block-code level-2&quot; id=&quot;c-0b7b5a90-cc7f-47cf-80e6-1dcc00924657&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.8.1&amp;lt;/version&amp;gt;
                &amp;lt;configuration&amp;gt;
                    &amp;lt;release&amp;gt;16&amp;lt;/release&amp;gt;
                    &amp;lt;compilerArgs&amp;gt;
                        &amp;lt;compilerArg&amp;gt;--add-modules&amp;lt;/compilerArg&amp;gt;
                        &amp;lt;compilerArg&amp;gt;jdk.incubator.vector&amp;lt;/compilerArg&amp;gt;
                    &amp;lt;/compilerArgs&amp;gt;
                    &amp;lt;annotationProcessorPaths&amp;gt;
                        &amp;lt;path&amp;gt;
                            &amp;lt;groupId&amp;gt;org.openjdk.jmh&amp;lt;/groupId&amp;gt;
                            &amp;lt;artifactId&amp;gt;jmh-generator-annprocess&amp;lt;/artifactId&amp;gt;
                            &amp;lt;version&amp;gt;1.27&amp;lt;/version&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/annotationProcessorPaths&amp;gt;
                &amp;lt;/configuration&amp;gt;
            &amp;lt;/plugin&amp;gt;

            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-surefire-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.0.0-M5&amp;lt;/version&amp;gt;
                &amp;lt;configuration&amp;gt;
                    &amp;lt;argLine&amp;gt;--add-modules jdk.incubator.vector&amp;lt;/argLine&amp;gt;
                &amp;lt;/configuration&amp;gt;
            &amp;lt;/plugin&amp;gt;

            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-shade-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.2.4&amp;lt;/version&amp;gt;
                &amp;lt;executions&amp;gt;
                    &amp;lt;execution&amp;gt;
                        &amp;lt;phase&amp;gt;package&amp;lt;/phase&amp;gt;
                        &amp;lt;goals&amp;gt;
                            &amp;lt;goal&amp;gt;shade&amp;lt;/goal&amp;gt;
                        &amp;lt;/goals&amp;gt;
                        &amp;lt;configuration&amp;gt;
                            &amp;lt;finalName&amp;gt;benchmarks&amp;lt;/finalName&amp;gt;
                            &amp;lt;transformers&amp;gt;
                                &amp;lt;transformer
                                        implementation=&amp;quot;org.apache.maven.plugins.shade.resource.ManifestResourceTransformer&amp;quot;&amp;gt;
                                    &amp;lt;mainClass&amp;gt;org.openjdk.jmh.Main&amp;lt;/mainClass&amp;gt;
                                &amp;lt;/transformer&amp;gt;
                            &amp;lt;/transformers&amp;gt;
                            &amp;lt;filters&amp;gt;
                                &amp;lt;filter&amp;gt;
                                    &amp;lt;artifact&amp;gt;*:*&amp;lt;/artifact&amp;gt;
                                    &amp;lt;excludes&amp;gt;
                                        &amp;lt;exclude&amp;gt;**/module-info.class&amp;lt;/exclude&amp;gt;
                                        &amp;lt;exclude&amp;gt;META-INF/MANIFEST.MF&amp;lt;/exclude&amp;gt;
                                    &amp;lt;/excludes&amp;gt;
                                &amp;lt;/filter&amp;gt;
                            &amp;lt;/filters&amp;gt;
                        &amp;lt;/configuration&amp;gt;
                    &amp;lt;/execution&amp;gt;
                &amp;lt;/executions&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0b7b5a90-cc7f-47cf-80e6-1dcc00924657--&gt;&lt;/li&gt;&lt;li&gt;config intellij:&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/555bb25f-8ecf-46da-90ee-568eab8046bf/capture_decran_2021-05-31_a_11.09.14.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/555bb25f-8ecf-46da-90ee-568eab8046bf/capture_decran_2021-05-31_a_11.09.41.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/555bb25f-8ecf-46da-90ee-568eab8046bf/capture_decran_2021-05-31_a_11.10.31.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph d9248594-4d33-4747-95b7-c811a9a2d69c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d9248594-4d33-4747-95b7-c811a9a2d69c&quot;&gt;Vérifiez que les dépendences de &lt;b&gt;Modules&lt;/b&gt; sont bien en java 16&lt;/p&gt;
&lt;!-- end paragraph d9248594-4d33-4747-95b7-c811a9a2d69c--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/555bb25f-8ecf-46da-90ee-568eab8046bf/capture_decran_2021-05-31_a_11.09.54.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_2 8183738c-ec33-4fa0-b3d8-f54f06cf5d56--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8183738c-ec33-4fa0-b3d8-f54f06cf5d56&quot;&gt;Un coup d’œil aux CPU &amp;quot;modernes&amp;quot;&lt;/h3&gt;
&lt;!-- end heading_2 8183738c-ec33-4fa0-b3d8-f54f06cf5d56--&gt;
&lt;!-- begin paragraph 02d5744d-6b47-4bb5-a769-6f8cb86a10d9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-02d5744d-6b47-4bb5-a769-6f8cb86a10d9&quot;&gt;L&amp;#39;apparition d&amp;#39;instructions SIMD (lire : Single instruction, Multiple Data) sur les processeurs grand public date des années 90 avec l’extension MMX sur Intel Pentium. On parle plus généralement de processeurs vectoriels.&lt;/p&gt;
&lt;!-- end paragraph 02d5744d-6b47-4bb5-a769-6f8cb86a10d9--&gt;
&lt;!-- begin paragraph 3d8e25e6-a955-4552-8254-fbd4828be6c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d8e25e6-a955-4552-8254-fbd4828be6c1&quot;&gt;La page Wikipédia des processeurs vectoriels est très exhaustive. Je vous suggère de la consulter. Pour aller plus loin vous pouvez aussi consulter &lt;a href=&quot;http://p-fb.net/fileadmin/ParallelismeII/2016_2017/Cours%20Parallelisme%20II%202016-2017.pdf&quot;&gt;ce lien&lt;/a&gt; et &lt;a href=&quot;https://tel.archives-ouvertes.fr/tel-02406318/document&quot;&gt;celui ci &lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 3d8e25e6-a955-4552-8254-fbd4828be6c1--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Registres &amp;amp; Lanes&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f8ea4c96-b6d7-49db-a5fd-dccf5658906b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f8ea4c96-b6d7-49db-a5fd-dccf5658906b&quot;&gt;Les registres vectorisés ont une taille supérieur aux registres normaux. Sur les CPU Intel on trouve des registres spécialisés (xmm0, ymm0, zmm0.. cf : &lt;a href=&quot;https://fr.wikipedia.org/wiki/Advanced_Vector_Extensions&quot;&gt;https://fr.wikipedia.org/wiki/Advanced_Vector_Extensions&lt;/a&gt;) qui effectuent une même opération sur plusieurs lanes&lt;/p&gt;
&lt;!-- end paragraph f8ea4c96-b6d7-49db-a5fd-dccf5658906b--&gt;
&lt;!-- begin table f5fac5bb-72cf-4046-af39-f886e8cc379a--&gt;
&lt;table class=&quot;block-table level-&quot; id=&quot;t-f5fac5bb-72cf-4046-af39-f886e8cc379a&quot;&gt;  &lt;tbody&gt;
                                                      &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- end table f5fac5bb-72cf-4046-af39-f886e8cc379a--&gt;
&lt;!-- begin paragraph e29db9f9-3771-45e3-ac52-affaeef7bb27--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e29db9f9-3771-45e3-ac52-affaeef7bb27&quot;&gt;Une opération SIMD indique l&amp;#39;opération, la taille d&amp;#39;une lane et la taille des registres. &lt;/p&gt;
&lt;!-- end paragraph e29db9f9-3771-45e3-ac52-affaeef7bb27--&gt;
&lt;!-- begin paragraph 22dec38e-a09b-4401-bb19-b70f78c92df8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-22dec38e-a09b-4401-bb19-b70f78c92df8&quot;&gt;Pour l&amp;#39;exemple précédent on a : &lt;b&gt;PADDW xmm0, xmm2, xmm3&lt;/b&gt; &lt;/p&gt;
&lt;!-- end paragraph 22dec38e-a09b-4401-bb19-b70f78c92df8--&gt;
&lt;!-- begin paragraph 3beb3f63-2711-46ec-9c1d-589f0b1cb8b2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3beb3f63-2711-46ec-9c1d-589f0b1cb8b2&quot;&gt;avec &lt;/p&gt;
&lt;!-- end paragraph 3beb3f63-2711-46ec-9c1d-589f0b1cb8b2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Préfixe &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 75d4f262-5f90-4c3c-8e1e-49314dfb4669--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75d4f262-5f90-4c3c-8e1e-49314dfb4669&quot;&gt;&lt;b&gt;P&lt;/b&gt; pour 128 bits &lt;/p&gt;
&lt;!-- end paragraph 75d4f262-5f90-4c3c-8e1e-49314dfb4669--&gt;
&lt;!-- begin paragraph 4204003d-5e54-40cb-8a9f-1f04f2d81968--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4204003d-5e54-40cb-8a9f-1f04f2d81968&quot;&gt;&lt;b&gt;VEX VP&lt;/b&gt; pour 256 bits &lt;/p&gt;
&lt;!-- end paragraph 4204003d-5e54-40cb-8a9f-1f04f2d81968--&gt;
&lt;!-- begin paragraph 16907252-863f-4fb5-ac72-613ce0ffa9c8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-16907252-863f-4fb5-ac72-613ce0ffa9c8&quot;&gt;&lt;b&gt;EVEX VP&lt;/b&gt; pour 512 bits&lt;/p&gt;
&lt;!-- end paragraph 16907252-863f-4fb5-ac72-613ce0ffa9c8--&gt;
&lt;!-- begin paragraph 509436a3-9336-4f34-9d69-87fbb582a8f5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-509436a3-9336-4f34-9d69-87fbb582a8f5&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 509436a3-9336-4f34-9d69-87fbb582a8f5--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Opération&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 6edbe303-5476-45c8-9842-43825537eb7f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6edbe303-5476-45c8-9842-43825537eb7f&quot;&gt;&lt;b&gt;ADD&lt;/b&gt; pour l’opération&lt;/p&gt;
&lt;!-- end paragraph 6edbe303-5476-45c8-9842-43825537eb7f--&gt;
&lt;!-- begin paragraph 73f6e9aa-7e16-4f7a-a748-e6c901849370--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-73f6e9aa-7e16-4f7a-a748-e6c901849370&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 73f6e9aa-7e16-4f7a-a748-e6c901849370--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Taille d’une ligne &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 00865b6b-ef72-40d5-8090-578813b1e8cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-00865b6b-ef72-40d5-8090-578813b1e8cf&quot;&gt;&lt;b&gt;B&lt;/b&gt; (byte) = 8 bits &lt;/p&gt;
&lt;!-- end paragraph 00865b6b-ef72-40d5-8090-578813b1e8cf--&gt;
&lt;!-- begin paragraph 28da2e71-e1aa-4e45-b863-53855f1560a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-28da2e71-e1aa-4e45-b863-53855f1560a9&quot;&gt;&lt;b&gt;W&lt;/b&gt; (word) = 32bits &lt;/p&gt;
&lt;!-- end paragraph 28da2e71-e1aa-4e45-b863-53855f1560a9--&gt;
&lt;!-- begin paragraph 78e7e551-29a2-43af-839f-95ea03ec7c27--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78e7e551-29a2-43af-839f-95ea03ec7c27&quot;&gt;&lt;b&gt;D&lt;/b&gt; (double) = 64bits &lt;/p&gt;
&lt;!-- end paragraph 78e7e551-29a2-43af-839f-95ea03ec7c27--&gt;
&lt;!-- begin paragraph 5f983bb5-88d2-4945-a4ab-3d96f638134e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f983bb5-88d2-4945-a4ab-3d96f638134e&quot;&gt;&lt;b&gt;Q&lt;/b&gt; (quad) = 128bits&lt;/p&gt;
&lt;!-- end paragraph 5f983bb5-88d2-4945-a4ab-3d96f638134e--&gt;
&lt;!-- begin heading_2 c586c09f-760c-4891-98ff-61f09fed5532--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c586c09f-760c-4891-98ff-61f09fed5532&quot;&gt;Les éléments principaux de l&amp;#39;API&lt;/h3&gt;
&lt;!-- end heading_2 c586c09f-760c-4891-98ff-61f09fed5532--&gt;
&lt;!-- begin paragraph 932f7ddb-8c5f-4c70-9995-74a9c29a39c4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-932f7ddb-8c5f-4c70-9995-74a9c29a39c4&quot;&gt;&lt;b&gt;Pour lancer une classe avec les bons modules :&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 932f7ddb-8c5f-4c70-9995-74a9c29a39c4--&gt;
&lt;!-- begin code c79b344f-6ea5-4e37-81d7-0aa11c6220f1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c79b344f-6ea5-4e37-81d7-0aa11c6220f1&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;java --add-modules jdk.incubator.vector fr.guihardbastien.boilerplate.Main&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c79b344f-6ea5-4e37-81d7-0aa11c6220f1--&gt;
&lt;!-- begin heading_3 6502c8df-6d33-44ce-baaa-34c2c7b37a3a--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-6502c8df-6d33-44ce-baaa-34c2c7b37a3a&quot;&gt;Premiers pas&lt;/h4&gt;
&lt;!-- end heading_3 6502c8df-6d33-44ce-baaa-34c2c7b37a3a--&gt;
&lt;!-- begin paragraph ff6d0338-18e2-4d92-b9b9-7d646cd7bbaa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff6d0338-18e2-4d92-b9b9-7d646cd7bbaa&quot;&gt;Pour étudier les éléments important de l&amp;#39;API prenons des exemples simple:&lt;/p&gt;
&lt;!-- end paragraph ff6d0338-18e2-4d92-b9b9-7d646cd7bbaa--&gt;
&lt;!-- begin code d659f89f-a4d8-4b9a-938f-fcf386252452--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d659f89f-a4d8-4b9a-938f-fcf386252452&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;package com.univalence.boilerplate;

import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorSpecies;

public class Main {
    public static void main(String[] args) {
        VectorSpecies&amp;lt;Integer&amp;gt; species = IntVector.SPECIES_PREFERRED;
        IntVector v1 = IntVector.broadcast(species, 20);
        IntVector v2 = IntVector.broadcast(species, 22);
        IntVector v3 = v1.add(v2);
        for (var i = 0; i &amp;lt; v3.length(); i++) {
            System.out.println(v3.lane(i));
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d659f89f-a4d8-4b9a-938f-fcf386252452--&gt;
&lt;!-- begin paragraph c067d281-4f6d-47eb-8847-a05195851f5f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c067d281-4f6d-47eb-8847-a05195851f5f&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c067d281-4f6d-47eb-8847-a05195851f5f--&gt;
&lt;!-- begin paragraph c680112f-51cb-47af-9984-d9d3e05729eb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c680112f-51cb-47af-9984-d9d3e05729eb&quot;&gt;&lt;b&gt;VectorSpecies&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph c680112f-51cb-47af-9984-d9d3e05729eb--&gt;
&lt;!-- begin paragraph bd5d9069-fc8a-4f47-a5cb-ea6affbbd648--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd5d9069-fc8a-4f47-a5cb-ea6affbbd648&quot;&gt;Un VectorSpecies indique un type (en l’occurrence Integer) et une &lt;i&gt;shape&lt;/i&gt; (taille en bits).&lt;/p&gt;
&lt;!-- end paragraph bd5d9069-fc8a-4f47-a5cb-ea6affbbd648--&gt;
&lt;!-- begin paragraph 1b8644c1-7998-4fb7-8326-1d610ade1b30--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1b8644c1-7998-4fb7-8326-1d610ade1b30&quot;&gt;On peut faire référence à IntVector.SPECIES_[64|128|256|512] pour obtenir la shape.&lt;/p&gt;
&lt;!-- end paragraph 1b8644c1-7998-4fb7-8326-1d610ade1b30--&gt;
&lt;!-- begin paragraph 1e28c3d8-4710-431a-86ea-2dfeb83b0609--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1e28c3d8-4710-431a-86ea-2dfeb83b0609&quot;&gt;Ici on fait référence a &lt;i&gt;SPECIES_PREFERRED&lt;/i&gt; pour - d&amp;#39;après la doc - :&lt;/p&gt;
&lt;!-- end paragraph 1e28c3d8-4710-431a-86ea-2dfeb83b0609--&gt;
&lt;!-- begin callout 95ab5373-55a5-495a-87ca-6aec6c312b7d--&gt;
&lt;section class=&quot;block-callout level-0&quot; id=&quot;b-95ab5373-55a5-495a-87ca-6aec6c312b7d&quot;&gt;
  &lt;div class=&quot;block-callout-emoji&quot;&gt;☕&lt;/div&gt;
  &lt;div class=&quot;block-callout-content&quot;&gt;make the code dynamically adapt to optimal shape for the platform on which it runs&lt;/div&gt;
&lt;/section&gt;
&lt;!-- end callout 95ab5373-55a5-495a-87ca-6aec6c312b7d--&gt;
&lt;!-- begin paragraph 8b9be8f2-22f0-40b8-83b6-cbba4ac64a22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8b9be8f2-22f0-40b8-83b6-cbba4ac64a22&quot;&gt;&lt;b&gt;IntVector v1 = IntVector.broadcast(species, 20);&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 8b9be8f2-22f0-40b8-83b6-cbba4ac64a22--&gt;
&lt;!-- begin paragraph 0c72f66a-0e2d-429e-946f-3087f1216c41--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0c72f66a-0e2d-429e-946f-3087f1216c41&quot;&gt;C&amp;#39;est ici qu&amp;#39;on &amp;quot;rempli&amp;quot; nos lanes décrites par le &lt;b&gt;VectorSpecies&lt;/b&gt; avec la valeur 20.&lt;/p&gt;
&lt;!-- end paragraph 0c72f66a-0e2d-429e-946f-3087f1216c41--&gt;
&lt;!-- begin paragraph 51ee7fce-22ea-4bc2-87eb-a8e8dd540013--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-51ee7fce-22ea-4bc2-87eb-a8e8dd540013&quot;&gt;&lt;b&gt;Opération&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 51ee7fce-22ea-4bc2-87eb-a8e8dd540013--&gt;
&lt;!-- begin paragraph fd604808-7da1-4fac-82f2-9d7c7ecd7ccc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fd604808-7da1-4fac-82f2-9d7c7ecd7ccc&quot;&gt;On effectue une addition des deux registres vectorisés : &lt;i&gt;IntVector v3 = v1.add(v2);&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph fd604808-7da1-4fac-82f2-9d7c7ecd7ccc--&gt;
&lt;!-- begin paragraph 274d1f90-b0a3-4854-9a22-5f93676716e9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-274d1f90-b0a3-4854-9a22-5f93676716e9&quot;&gt;&lt;b&gt;Sortie&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 274d1f90-b0a3-4854-9a22-5f93676716e9--&gt;
&lt;!-- begin paragraph 09aaf180-50f1-4aa6-a034-e43912f4bfd3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-09aaf180-50f1-4aa6-a034-e43912f4bfd3&quot;&gt;Sur ma machine on peut voir la sortie suivante : &lt;/p&gt;
&lt;!-- end paragraph 09aaf180-50f1-4aa6-a034-e43912f4bfd3--&gt;
&lt;!-- begin code 411e38cc-4471-4812-9d5b-b26b587822f6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-411e38cc-4471-4812-9d5b-b26b587822f6&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;WARNING: Using incubator modules: jdk.incubator.vector
42
42
42
42
42
42
42
42&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 411e38cc-4471-4812-9d5b-b26b587822f6--&gt;
&lt;!-- begin paragraph aae28294-b458-4595-b828-e58deb3cfe74--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aae28294-b458-4595-b828-e58deb3cfe74&quot;&gt;Les huit affichages nous indiquent que mes registres vectorisés mesurent 256 bits (on additionne 8 int encodés sur 32 bits simultanément)&lt;/p&gt;
&lt;!-- end paragraph aae28294-b458-4595-b828-e58deb3cfe74--&gt;
&lt;!-- begin heading_3 52fcbef8-bf1a-4ae1-82e7-64f1f086300b--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-52fcbef8-bf1a-4ae1-82e7-64f1f086300b&quot;&gt;Deuxième exemple&lt;/h4&gt;
&lt;!-- end heading_3 52fcbef8-bf1a-4ae1-82e7-64f1f086300b--&gt;
&lt;!-- begin code 978c58c4-de56-4914-9160-efd51488a005--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-978c58c4-de56-4914-9160-efd51488a005&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;public class Main {
    public static void main(String[] args) {
        VectorSpecies&amp;lt;Integer&amp;gt; species = IntVector.SPECIES_PREFERRED;
        var arr = new int[]{0, 1, 2, 3, 4, 5, 6, 7};
        var b = new int[8];

        IntVector vectorizedArray = IntVector.fromArray(species, arr, 0);
        IntVector conditionVector = IntVector.broadcast(species, 6);
        IntVector addTermVector = IntVector.broadcast(species, 10);
        VectorMask&amp;lt;Integer&amp;gt; mask =  vectorizedArray.lt(conditionVector); // lt -&amp;gt; lower than

        // on ajoute 10 respectivement au mask
	IntVector vb = vectorizedArray.add(addTermVector, mask);
        vb.intoArray(b, 0);
        System.out.println(Arrays.toString(b));

    }
}

/* output

WARNING: Using incubator modules: jdk.incubator.vector
[10, 11, 12, 13, 14, 15, 6, 7]

*/&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 978c58c4-de56-4914-9160-efd51488a005--&gt;
&lt;!-- begin paragraph e9702775-58fb-4944-a2c7-8bfe9442c92e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e9702775-58fb-4944-a2c7-8bfe9442c92e&quot;&gt;Concrètement ici, on effectue une condition relative a un vecteur en créant un masque. En gros, on fait un if vectorisé. Dans notre exemple le masque ressemble à ça : TTTTTTFF.&lt;/p&gt;
&lt;!-- end paragraph e9702775-58fb-4944-a2c7-8bfe9442c92e--&gt;
&lt;!-- begin paragraph 8d5d00e8-4875-48e3-a552-8dfa619f2c61--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d5d00e8-4875-48e3-a552-8dfa619f2c61&quot;&gt;Pour toutes les lanes qui sont true sur le masque, on leur ajoute 10.&lt;/p&gt;
&lt;!-- end paragraph 8d5d00e8-4875-48e3-a552-8dfa619f2c61--&gt;
&lt;!-- begin heading_3 bdd926f5-26f3-42a8-a8f5-4061cc3c4d4e--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-bdd926f5-26f3-42a8-a8f5-4061cc3c4d4e&quot;&gt;Post-loop &amp;amp; Mask &lt;/h4&gt;
&lt;!-- end heading_3 bdd926f5-26f3-42a8-a8f5-4061cc3c4d4e--&gt;
&lt;!-- begin paragraph 5a270f95-08f6-4039-9b8a-4fc82cb85682--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a270f95-08f6-4039-9b8a-4fc82cb85682&quot;&gt;Pour l&amp;#39;exemple suivant, l&amp;#39;idée est de manipuler un array de manière vectorisée et du finir avec un reduce directement sur les vecteurs.&lt;/p&gt;
&lt;!-- end paragraph 5a270f95-08f6-4039-9b8a-4fc82cb85682--&gt;
&lt;!-- begin paragraph 28c842a3-faa4-44d1-a562-5f6f22fb1d76--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-28c842a3-faa4-44d1-a562-5f6f22fb1d76&quot;&gt;L&amp;#39;explication du code est faite directement en commentaire.&lt;/p&gt;
&lt;!-- end paragraph 28c842a3-faa4-44d1-a562-5f6f22fb1d76--&gt;
&lt;!-- begin code 49342828-11c1-4db7-9f09-547d0df8941f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-49342828-11c1-4db7-9f09-547d0df8941f&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;private static final VectorSpecies&amp;lt;Integer&amp;gt; SPECIES = IntVector.SPECIES_PREFERRED;

    public static int sum(int[] array) {
        var length = array.length;

	// loopBound permet de savoir jusqu&amp;#39;ou on peut vectoriser l&amp;#39;array
	// On se servira plus tard d&amp;#39;une &amp;quot;post-loop&amp;quot; pour finir le travail
        var loopBound = length - length % SPECIES.length();

	// la methode .zero est equivalente à 
	// IntVector.broadcast(species, 0);
        var v0 = IntVector.zero(SPECIES);
        var i = 0;
        for (; i &amp;lt; loopBound; i += SPECIES.length()) {
	    // chunk vector of array
            var v = IntVector.fromArray(SPECIES, array, i);
	    // on additionne le contenu des lanes entre eux
            v0 = v0.add(v);
        }
	// ici on on additionne les lanes entres elles
        int sum = v0.reduceLanes(VectorOperators.ADD);

	// La post-loop qui nous permet de finir le traitement complet de l&amp;#39;array
        for (; i &amp;lt; length; i++) {
            sum += array[i];
        }
        return sum;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 49342828-11c1-4db7-9f09-547d0df8941f--&gt;
&lt;!-- begin paragraph 9a3417cf-3034-43e9-a930-448feba702c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9a3417cf-3034-43e9-a930-448feba702c1&quot;&gt;Voici un équivalent avec un masque : &lt;/p&gt;
&lt;!-- end paragraph 9a3417cf-3034-43e9-a930-448feba702c1--&gt;
&lt;!-- begin code 93827b33-4e9e-4741-b56c-e3e6d112d95c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-93827b33-4e9e-4741-b56c-e3e6d112d95c&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;public static int sumMask(int[] array) {
        var length = array.length;
        var loopBound = length - length % SPECIES.length();
        var v0 = IntVector.zero(SPECIES);
        var i = 0;
        for (; i &amp;lt; loopBound; i += SPECIES.length()) {
            var v = IntVector.fromArray(SPECIES, array, i);
            v0 = v0.add(v);
        }
        int sum = v0.reduceLanes(VectorOperators.ADD);

        var mask = SPECIES.indexInRange(i, length);
        var vMask = IntVector.fromArray(SPECIES, array, i, mask);
        int sumMask = vMask.reduceLanes(VectorOperators.ADD);

        return sum + sumMask;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 93827b33-4e9e-4741-b56c-e3e6d112d95c--&gt;
&lt;!-- begin heading_2 2126e3e1-a1dc-446c-bbe2-279021e32392--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-2126e3e1-a1dc-446c-bbe2-279021e32392&quot;&gt;Test avec benchmark&lt;/h3&gt;
&lt;!-- end heading_2 2126e3e1-a1dc-446c-bbe2-279021e32392--&gt;
&lt;!-- begin heading_3 6a87b5c0-7d67-405e-8e12-839069136270--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-6a87b5c0-7d67-405e-8e12-839069136270&quot;&gt;Classe VectorComputation&lt;/h4&gt;
&lt;!-- end heading_3 6a87b5c0-7d67-405e-8e12-839069136270--&gt;
&lt;!-- begin code 825eec7f-1228-4941-ae76-62f6fc88f8b6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-825eec7f-1228-4941-ae76-62f6fc88f8b6&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;

public class VectorComputation {
    private static final VectorSpecies&amp;lt;Integer&amp;gt; SPECIES = IntVector.SPECIES_PREFERRED;

    public static int sum(int[] array) {
        var length = array.length;
        var loopBound = length - length % SPECIES.length();
        var v0 = IntVector.zero(SPECIES);
        var i = 0;
        for (; i &amp;lt; loopBound; i += SPECIES.length()) {
            var v = IntVector.fromArray(SPECIES, array, i);
            v0 = v0.add(v);
        }
        int sum = v0.reduceLanes(VectorOperators.ADD);
        for (; i &amp;lt; length; i++) {
            sum += array[i];
        }
        return sum;
    }

    public static int sumMask(int[] array) {
        var length = array.length;
        var loopBound = length - length % SPECIES.length();
        var v0 = IntVector.zero(SPECIES);
        var i = 0;
        for (; i &amp;lt; loopBound; i += SPECIES.length()) {
            var v = IntVector.fromArray(SPECIES, array, i);
            v0 = v0.add(v);
        }
        int sum = v0.reduceLanes(VectorOperators.ADD);

        var mask = SPECIES.indexInRange(i, length);
        var vMask = IntVector.fromArray(SPECIES, array, i, mask);
        int sumMask = vMask.reduceLanes(VectorOperators.ADD);

        return sum + sumMask;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 825eec7f-1228-4941-ae76-62f6fc88f8b6--&gt;
&lt;!-- begin heading_3 12329ae9-d8bd-4c48-8c7c-fd5e684a7133--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-12329ae9-d8bd-4c48-8c7c-fd5e684a7133&quot;&gt;VectorComputationBenchmark&lt;/h4&gt;
&lt;!-- end heading_3 12329ae9-d8bd-4c48-8c7c-fd5e684a7133--&gt;
&lt;!-- begin code bb60918c-3799-4aa3-b0e4-6e1a31afe7b4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bb60918c-3799-4aa3-b0e4-6e1a31afe7b4&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Fork(value = 1, jvmArgs = {&amp;quot;--add-modules&amp;quot;, &amp;quot;jdk.incubator.vector&amp;quot;})
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
@SuppressWarnings(&amp;quot;static-method&amp;quot;)
public class VectorComputationBenchMark {
    private final int[] array = new Random(0).ints(100_000, 0, 1_000).toArray();

    @Benchmark
    public int sum_loop_array(Blackhole blackhole) {
        var sum = 0;
        for (var value : array) {
            sum += value;
        }
        return sum;
    }

    @Benchmark
    public int sum_vector_array(Blackhole blackhole) {
        return VectorComputation.sum(array);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bb60918c-3799-4aa3-b0e4-6e1a31afe7b4--&gt;
&lt;!-- begin heading_3 9226e34d-9c92-4df0-9591-099784bd1c9c--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-9226e34d-9c92-4df0-9591-099784bd1c9c&quot;&gt;Benchmark&lt;/h4&gt;
&lt;!-- end heading_3 9226e34d-9c92-4df0-9591-099784bd1c9c--&gt;
&lt;!-- begin paragraph 32736c80-22b4-4b07-adb2-e2b0b721f182--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-32736c80-22b4-4b07-adb2-e2b0b721f182&quot;&gt;&lt;b&gt;Lancer le benchmark&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 32736c80-22b4-4b07-adb2-e2b0b721f182--&gt;
&lt;!-- begin paragraph 6e7996e4-31ad-4434-9a84-77299117ca24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e7996e4-31ad-4434-9a84-77299117ca24&quot;&gt;Branchez votre machine sur secteur puis :&lt;/p&gt;
&lt;!-- end paragraph 6e7996e4-31ad-4434-9a84-77299117ca24--&gt;
&lt;!-- begin code 0aa4f08b-b315-4b84-b9f3-3dd05489da06--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0aa4f08b-b315-4b84-b9f3-3dd05489da06&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ./maven-loom-intellij-boilerplate
mvn clean install
cd ./lab1/target
java -jar ./benchmarks.jar&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0aa4f08b-b315-4b84-b9f3-3dd05489da06--&gt;
&lt;!-- begin heading_2 33675cef-852c-4e0d-b454-c1d1792d673c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-33675cef-852c-4e0d-b454-c1d1792d673c&quot;&gt;Conclusions&lt;/h3&gt;
&lt;!-- end heading_2 33675cef-852c-4e0d-b454-c1d1792d673c--&gt;
&lt;!-- begin code 6d5ad677-187e-471b-b508-e05564a0909d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6d5ad677-187e-471b-b508-e05564a0909d&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                    Mode  Cnt   Score   Error  Units
VectorComputationBenchMark.sum_loop_array    avgt    5  23,589 ± 2,136  us/op
VectorComputationBenchMark.sum_vector_array  avgt    5   5,612 ± 0,284  us/op&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6d5ad677-187e-471b-b508-e05564a0909d--&gt;
&lt;!-- begin heading_2 55cf2bb0-1c7a-48fe-8c74-7d4903df1270--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-55cf2bb0-1c7a-48fe-8c74-7d4903df1270&quot;&gt;Ressources&lt;/h3&gt;
&lt;!-- end heading_2 55cf2bb0-1c7a-48fe-8c74-7d4903df1270--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://mkyong.com/java/how-to-set-java_home-environment-variable-on-mac-os-x/&quot;&gt;https://mkyong.com/java/how-to-set-java_home-environment-variable-on-mac-os-x/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Processeur_vectoriel&quot;&gt;https://fr.wikipedia.org/wiki/Processeur_vectoriel&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Advanced_Vector_Extensions&quot;&gt;https://fr.wikipedia.org/wiki/Advanced_Vector_Extensions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://tel.archives-ouvertes.fr/tel-02406318/document&quot;&gt;https://tel.archives-ouvertes.fr/tel-02406318/document&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://p-fb.net/fileadmin/ParallelismeII/2016_2017/Cours%20Parallelisme%20II%202016-2017.pdf&quot;&gt;http://p-fb.net/fileadmin/ParallelismeII/2016_2017/Cours Parallelisme II 2016-2017.pdf&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.irisa.fr/archi09/defour.pdf&quot;&gt;https://www.irisa.fr/archi09/defour.pdf&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 63135e39-fbdb-4b79-91de-301c899fe7f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-63135e39-fbdb-4b79-91de-301c899fe7f9&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 63135e39-fbdb-4b79-91de-301c899fe7f9--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:4af8d29177844ab39e9e924d43984d57</id>
    <title>sbt-dynver : un script de release</title>
    <updated>2021-05-30T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/sbt-dynver-un-script-de-release.html"/>
    <!--summary Nous proposons ici un petit script de release qui accompagnera volontier sbt-dynver.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Release"></category>    <category term="sbt-dynver"></category>    <category term="Outil"></category>    <content type="html">
&lt;!-- begin paragraph 3a12065d-cfdb-456b-99db-6f1a330fa662--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3a12065d-cfdb-456b-99db-6f1a330fa662&quot;&gt;Nous avons vu dans un &lt;a href=&quot;https://univalence.io/blog/articles/sbt-dynver-la-gestion-dynamique-des-numeros-de-version/&quot;&gt;précédent article le plugin sbt-dynver&lt;/a&gt; qui permet de versionner votre code en se basant sur les tags Git. Il manquait un processus de release automatisé tout comme sbt-release tout en restant plus léger.&lt;/p&gt;
&lt;!-- end paragraph 3a12065d-cfdb-456b-99db-6f1a330fa662--&gt;
&lt;!-- begin paragraph c4549204-4a6c-42b5-ae72-f9636f69f7cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c4549204-4a6c-42b5-ae72-f9636f69f7cb&quot;&gt;Voici un script permettant d&amp;#39;effectuer ce processus de release. Il effectue les tâches suivantes :&lt;/p&gt;
&lt;!-- end paragraph c4549204-4a6c-42b5-ae72-f9636f69f7cb--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Vérifier que vous êtes sur la branche prévue pour les releases.&lt;/li&gt;&lt;li&gt;Vérifier s&amp;#39;il n&amp;#39;y a pas de modifications non commitées.&lt;/li&gt;&lt;li&gt;Lire et vérifier le nouveau numéro de version (en affichant la version actuelle).&lt;/li&gt;&lt;li&gt;Création du tag en se basant sur le nouveau numéro de version.&lt;/li&gt;&lt;li&gt;Pousser toutes les dernières modifications (tag inclus) sur le repo distant&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin code ffc2bb5d-0de7-4da6-9933-80bee0133ff8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ffc2bb5d-0de7-4da6-9933-80bee0133ff8&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;#!/bin/sh
# Release script
#
# This script asks the user for the new version tag and push the
# code with the new tag.
#
# You should:
# - have release based on Git tags
# - be in your main branch
# - have a tag that starts with &amp;#39;v&amp;#39; and the rest looks like
#   [semver](https://semver.org/)

release_branch=master
remote_repository=origin
version_tag_regexp=&amp;#39;v[0-9]+(\.[0-9]+)+(-.*)?&amp;#39;

# check current branch
current_branch=$(git branch --show-current)
if [ &amp;quot;$current_branch&amp;quot; != &amp;quot;$release_branch&amp;quot; ]; then
  echo &amp;quot;current branch is not $release_branch (current: $current_branch)&amp;quot;
  exit 1
fi

# check there are no uncommitted files
modified_files=$(git ls-files --modified --deleted --exclude-standard --others)
if [ &amp;quot;$modified_files&amp;quot; != &amp;quot;&amp;quot; ]; then
  echo &amp;quot;those files have been modified, deleted, and/or added:&amp;quot;
  echo &amp;quot;$modified_files&amp;quot;
  exit 1
fi

# get the last available tag
last_tag=$(git tag --list --sort=-v:refname | grep -E &amp;quot;$version_tag_regexp&amp;quot; | head -n 1)

# read and check the new tag
read -r -p &amp;quot;Enter new version (prefix with &amp;#39;v&amp;#39;) [last version:$last_tag]: &amp;quot; new_tag
if [ &amp;quot;$new_tag&amp;quot; = &amp;quot;&amp;quot; ]; then
  echo &amp;quot;the tag input is empty&amp;quot;
  exit 1
fi
if [ &amp;quot;$(echo &amp;quot;$new_tag&amp;quot; | grep -E &amp;quot;$version_tag_regexp&amp;quot;)&amp;quot; = &amp;quot;&amp;quot; ]; then
  echo &amp;quot;bad tag format: $new_tag&amp;quot;
  exit 1
fi

# proceed the new release

echo &amp;quot;set version to $new_tag&amp;quot;
git tag &amp;quot;$new_tag&amp;quot; -m &amp;quot;Set version to $new_tag&amp;quot;

echo &amp;quot;push new tag and last commit to remote&amp;quot;
git push --atomic &amp;quot;$remote_repository&amp;quot; &amp;quot;$release_branch&amp;quot; &amp;quot;$new_tag&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ffc2bb5d-0de7-4da6-9933-80bee0133ff8--&gt;
&lt;!-- begin paragraph a1e3bac8-58fd-4532-8aaf-11acfd120912--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a1e3bac8-58fd-4532-8aaf-11acfd120912&quot;&gt;Mettez ce script dans un fichier nommé &lt;code&gt;release.sh&lt;/code&gt;. Rendez-le exécutable et ajoutez son répertoire dans votre PATH ou déposez-le dans un sous-répertoire dédié aux scripts dans votre projet.&lt;/p&gt;
&lt;!-- end paragraph a1e3bac8-58fd-4532-8aaf-11acfd120912--&gt;
&lt;!-- begin paragraph 03ff2f1d-b8f8-49d6-909d-40660e92a06b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-03ff2f1d-b8f8-49d6-909d-40660e92a06b&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 03ff2f1d-b8f8-49d6-909d-40660e92a06b--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:87ce5653536a4df3b420d911b20e95a8</id>
    <title>Scala 3 : un nouveau langage ?</title>
    <updated>2021-05-17T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/scala-3-un-nouveau-langage.html"/>
    <!--summary -->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="Scala 3"></category>    <category term="Changelog"></category>    <content type="html">
&lt;!-- begin paragraph cf451d7f-d20f-495c-9c85-c9884284b46f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf451d7f-d20f-495c-9c85-c9884284b46f&quot;&gt;TL;DR : Oui.&lt;/p&gt;
&lt;!-- end paragraph cf451d7f-d20f-495c-9c85-c9884284b46f--&gt;
&lt;!-- begin paragraph c72a6ddf-8c0a-483c-b866-44ddfb126eb3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c72a6ddf-8c0a-483c-b866-44ddfb126eb3&quot;&gt;Scala 3 vient tout juste de sortir.&lt;/p&gt;
&lt;!-- end paragraph c72a6ddf-8c0a-483c-b866-44ddfb126eb3--&gt;&lt;div class=&quot;block-embed level-0&quot;&gt;&lt;div class=&quot;player twitter&quot;&gt;&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/scala_lang/status/1393139114535444484?s=21&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph e920c46d-ec5f-4219-84af-a42c14e4261b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e920c46d-ec5f-4219-84af-a42c14e4261b&quot;&gt;Il succède à Scala 2.13, dont &lt;a href=&quot;https://univalence.io/blog/articles/scala-2-13-0/&quot;&gt;nous avons parlé&lt;/a&gt; il y a deux ans. De cette nouvelle version, qui jusque-là apparaissait comme le projet &lt;a href=&quot;http://dotty.epfl.ch/&quot;&gt;Dotty&lt;/a&gt;, que devons-nous attendre ? Qu&amp;#39;apportent les améliorations ? Quel sera le coût de la migration depuis la version 2 ?&lt;/p&gt;
&lt;!-- end paragraph e920c46d-ec5f-4219-84af-a42c14e4261b--&gt;
&lt;!-- begin paragraph f7cde39b-d557-4cb2-8e91-62baa494bdea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f7cde39b-d557-4cb2-8e91-62baa494bdea&quot;&gt;Nous allons voir parmi ces nouveautés, celles qui sont les plus impactantes pour le développeur Scala usuel et discuter de l&amp;#39;aspect migration.&lt;/p&gt;
&lt;!-- end paragraph f7cde39b-d557-4cb2-8e91-62baa494bdea--&gt;
&lt;!-- begin heading_1 c7d2dc0e-f2f1-4add-8468-bc457fc67422--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c7d2dc0e-f2f1-4add-8468-bc457fc67422&quot;&gt;Syntaxe&lt;/h2&gt;
&lt;!-- end heading_1 c7d2dc0e-f2f1-4add-8468-bc457fc67422--&gt;
&lt;!-- begin paragraph b1d1f380-6d59-4fa3-99e9-0824eb24c001--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b1d1f380-6d59-4fa3-99e9-0824eb24c001&quot;&gt;Scala 3 propose une nouvelle syntaxe pour écrire le code. Elle me fait penser à un mix entre l&amp;#39;ancienne syntaxe Scala, celle de Python et celle de Pascal et Ruby. Cette nouvelle syntaxe est optionnelle et il est toujours possible d&amp;#39;utiliser l&amp;#39;ancienne syntaxe (avec quelques restrictions), voire de mixer des deux.&lt;/p&gt;
&lt;!-- end paragraph b1d1f380-6d59-4fa3-99e9-0824eb24c001--&gt;
&lt;!-- begin heading_2 f43a1e07-aaa6-429d-a9cf-065c042bbf73--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f43a1e07-aaa6-429d-a9cf-065c042bbf73&quot;&gt;Structure de contrôle&lt;/h3&gt;
&lt;!-- end heading_2 f43a1e07-aaa6-429d-a9cf-065c042bbf73--&gt;
&lt;!-- begin paragraph 324ec068-9794-4ae1-9044-0f664cb19d4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-324ec068-9794-4ae1-9044-0f664cb19d4d&quot;&gt;Au niveau structure de contrôle, voici le &lt;code&gt;if&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 324ec068-9794-4ae1-9044-0f664cb19d4d--&gt;
&lt;!-- begin code 9f18dea3-6f3d-46c7-9258-719cdc2956ed--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9f18dea3-6f3d-46c7-9258-719cdc2956ed&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// 1 - old Scala syntax (available), braces are optional here
if (age &amp;gt;= 18) {
  &amp;quot;adult&amp;quot;
} else {
  &amp;quot;kid&amp;quot;
}

// 2 - new Scala syntax (end if), semi-indent sensitive
if age &amp;gt;= 18 then
  &amp;quot;adult&amp;quot;
else
  &amp;quot;kid&amp;quot;

// 3 - new Scala syntax (Pascal/Ruby-style)
if age &amp;gt;= 18 then
  &amp;quot;adult&amp;quot;
else
  &amp;quot;kid&amp;quot;
end if&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9f18dea3-6f3d-46c7-9258-719cdc2956ed--&gt;
&lt;!-- begin paragraph 67dcd4ad-042a-4f28-8bcc-b1f9273a5210--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67dcd4ad-042a-4f28-8bcc-b1f9273a5210&quot;&gt;S&amp;#39;ajoute ainsi le &lt;code&gt;then&lt;/code&gt; et la perte des parenthèses pour la condition. Un marqueur de fin peut être ajouté en fin de structure. On retrouve une syntaxe équivalente avec &lt;code&gt;while...do&lt;/code&gt;, avec &lt;code&gt;for...do/yield&lt;/code&gt; et avec &lt;code&gt;try...catch&lt;/code&gt; et le pattern matching.&lt;/p&gt;
&lt;!-- end paragraph 67dcd4ad-042a-4f28-8bcc-b1f9273a5210--&gt;
&lt;!-- begin heading_2 e867b6e5-6b0b-4fb6-b474-5044a243e52e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e867b6e5-6b0b-4fb6-b474-5044a243e52e&quot;&gt;Structure de données&lt;/h3&gt;
&lt;!-- end heading_2 e867b6e5-6b0b-4fb6-b474-5044a243e52e--&gt;
&lt;!-- begin paragraph 87cf3d69-ee86-42d8-8394-370f1af3a469--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-87cf3d69-ee86-42d8-8394-370f1af3a469&quot;&gt;Pour les structures de données, voici la nouvelle syntaxe côté classe.&lt;/p&gt;
&lt;!-- end paragraph 87cf3d69-ee86-42d8-8394-370f1af3a469--&gt;
&lt;!-- begin code f465a989-15ae-4da0-9c82-111602ec8c36--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f465a989-15ae-4da0-9c82-111602ec8c36&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// 1 - old syntax (available), braces are optional here
class Message1(message: String) {
  def display: Unit = {
    println(message)
  }
}

// 2 - new syntax (Python-style), indent sensitive
class Message2(message: String):
  def display: Unit =
    println(message)

// 3 - new syntax, indent sensitive
class Message3(message: String):
  def display: Unit =
    println(message)
  end display
end Message3&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f465a989-15ae-4da0-9c82-111602ec8c36--&gt;
&lt;!-- begin paragraph 796bae1d-75a2-41b3-bcd6-4d4e95eed8d0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-796bae1d-75a2-41b3-bcd6-4d4e95eed8d0&quot;&gt;Sur la deuxième version de l&amp;#39;exemple ci-dessus, l&amp;#39;inspiration de Python est très présente. Il y avait déjà le &lt;code&gt;def&lt;/code&gt;, le rappel du nom des paramètres à l&amp;#39;appel et les valeurs par défaut pour les paramètres. Maintenant, les accolades ont fait place aux deux-points en fin de déclaration. Et c&amp;#39;est l&amp;#39;indentation qui détermine si les déclarations qui suivent font partie de la classe ou si elles sont en dehors de son scope. Si vous souhaitez délimiter les blocs sans accolades, utilisez alors la troisième version dans l&amp;#39;exemple.&lt;/p&gt;
&lt;!-- end paragraph 796bae1d-75a2-41b3-bcd6-4d4e95eed8d0--&gt;
&lt;!-- begin heading_2 b436a91c-adc2-4efc-938c-8248d2aa5360--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b436a91c-adc2-4efc-938c-8248d2aa5360&quot;&gt;Toplevel function&lt;/h3&gt;
&lt;!-- end heading_2 b436a91c-adc2-4efc-938c-8248d2aa5360--&gt;
&lt;!-- begin paragraph f795f76f-d88a-4523-a3e8-acb3646679de--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f795f76f-d88a-4523-a3e8-acb3646679de&quot;&gt;En aparté, vous pouvez déclarer des fonctions qui ne sont rattachés à aucune classe ou structure équivalente directement dans un fichier. Ce qui rappelle l&amp;#39;une des possibilités offertes par Kotlin. Cette approche remplace la syntaxe &lt;code&gt;package object&lt;/code&gt; de la version 2 de Scala.&lt;/p&gt;
&lt;!-- end paragraph f795f76f-d88a-4523-a3e8-acb3646679de--&gt;
&lt;!-- begin heading_2 9baf1e93-98a6-4835-80f5-258aaab09d8e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-9baf1e93-98a6-4835-80f5-258aaab09d8e&quot;&gt;Main&lt;/h3&gt;
&lt;!-- end heading_2 9baf1e93-98a6-4835-80f5-258aaab09d8e--&gt;
&lt;!-- begin paragraph c9e672d8-49a2-42ac-abbd-65e88771fbc6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c9e672d8-49a2-42ac-abbd-65e88771fbc6&quot;&gt;Du côté du &lt;code&gt;main&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph c9e672d8-49a2-42ac-abbd-65e88771fbc6--&gt;
&lt;!-- begin code 7f3daa52-607c-47be-91aa-84ee6c1b190b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7f3daa52-607c-47be-91aa-84ee6c1b190b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// 1 - old syntax
object Main {
  def main(args: Array[String]): Unit =
    println(&amp;quot;You&amp;#39;re the best!!!&amp;quot;)
}

// 2 - new, no parameter
@main def mySuperProgram: Unit:
  println(&amp;quot;You&amp;#39;re the best!!!&amp;quot;)

// 3 - new, with parameter
@main def hello(message: String): Unit:
  println(s&amp;quot;hello $message&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7f3daa52-607c-47be-91aa-84ee6c1b190b--&gt;
&lt;!-- begin paragraph 657d3b43-573e-43ad-a85f-e893aace39e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-657d3b43-573e-43ad-a85f-e893aace39e1&quot;&gt;L&amp;#39;entrée de votre application n&amp;#39;est pas nécessairement une méthode appelée &lt;code&gt;main&lt;/code&gt;, mais elle peut être une fonction ayant n&amp;#39;importe quel nom. Il faut qu&amp;#39;elle soit précédée de l&amp;#39;annotation &lt;code&gt;@main&lt;/code&gt;. Et plutôt que de passer en paramètre un conteneur récupérant les paramètres, vous devez ici préciser les paramètres dont vous avez besoin exactement en précisant leur type (String, Int, Boolean...). Si vous ne connaissez pas par avance le type et la quantité de paramètres, il est possible d&amp;#39;indiquer &lt;code&gt;String*&lt;/code&gt; ou de revenir à l&amp;#39;ancienne syntaxe.&lt;/p&gt;
&lt;!-- end paragraph 657d3b43-573e-43ad-a85f-e893aace39e1--&gt;
&lt;!-- begin paragraph 73f64ca9-63cf-4427-9116-0b74bd0473f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-73f64ca9-63cf-4427-9116-0b74bd0473f9&quot;&gt;Lorsque vous utilisez la nouvelle syntaxe pour déclarer l&amp;#39;entrée de votre application, Scala crée une classe JVM dont le nom correspond au nom de la fonction annotée &lt;code&gt;@main&lt;/code&gt;, dans laquelle il ajoute la méthode statique &lt;code&gt;main&lt;/code&gt;. Cette méthode &lt;code&gt;main&lt;/code&gt; fait alors appel à un analyseur de paramètre avant de passer la main au code de la fonction. Vous pouvez ainsi avoir plusieurs fonctions &lt;code&gt;@main&lt;/code&gt; dans le même fichier.&lt;/p&gt;
&lt;!-- end paragraph 73f64ca9-63cf-4427-9116-0b74bd0473f9--&gt;
&lt;!-- begin heading_2 3923ec99-da99-49db-97dd-ff64ac9b50a1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3923ec99-da99-49db-97dd-ff64ac9b50a1&quot;&gt;Package&lt;/h3&gt;
&lt;!-- end heading_2 3923ec99-da99-49db-97dd-ff64ac9b50a1--&gt;
&lt;!-- begin paragraph ca3c49a1-f676-411b-a294-6a052a3aa10a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ca3c49a1-f676-411b-a294-6a052a3aa10a&quot;&gt;Enfin, pour les packages :&lt;/p&gt;
&lt;!-- end paragraph ca3c49a1-f676-411b-a294-6a052a3aa10a--&gt;
&lt;!-- begin code 4c76f407-4faf-4e8b-8304-4be8ed7d86a9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4c76f407-4faf-4e8b-8304-4be8ed7d86a9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def f: String = p1.p2.hello

package p1:
  package p2:
    def hello: String = &amp;quot;hello world&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4c76f407-4faf-4e8b-8304-4be8ed7d86a9--&gt;
&lt;!-- begin heading_1 1df67163-79cb-4113-88d0-88d9335c2952--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1df67163-79cb-4113-88d0-88d9335c2952&quot;&gt;Enum&lt;/h2&gt;
&lt;!-- end heading_1 1df67163-79cb-4113-88d0-88d9335c2952--&gt;
&lt;!-- begin paragraph 8f9e74b3-bbc0-4cc6-8b81-92e8c1a1963a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8f9e74b3-bbc0-4cc6-8b81-92e8c1a1963a&quot;&gt;Ça fait bien longtemps que les développeurs Scala l&amp;#39;attendait : une déclaration d&amp;#39;enum. Il y a déjà une telle déclaration du côté Java depuis la version 5, qui permet de déclarer des constantes. Mais il manquait un équivalent aussi pratique du côté Scala pour pouvoir déclarer des ADT et des GADT. Une tentative a été mise au point, mais l&amp;#39;implémentation bien qu&amp;#39;encore présente est loin de valoir le confort offert par les enums Java. La meilleure façon de mettre en place un ADT en Scala en version 2 est d&amp;#39;utiliser des &lt;i&gt;sealed trait&lt;/i&gt; et des &lt;i&gt;case class&lt;/i&gt;. Par exemple, voici un ADT permettant de représenter des expressions arithmétiques :&lt;/p&gt;
&lt;!-- end paragraph 8f9e74b3-bbc0-4cc6-8b81-92e8c1a1963a--&gt;
&lt;!-- begin code bb71d995-465f-4b88-87b9-48c3dc08fe8a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bb71d995-465f-4b88-87b9-48c3dc08fe8a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;sealed trait Expression
case class Const(value: Double) extends Expression
case class Add(left: Expression, right: Expression) extends Expression
case class Mul(left: Expression, right: Expression) extends Expression&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bb71d995-465f-4b88-87b9-48c3dc08fe8a--&gt;
&lt;!-- begin paragraph 69584886-8cef-4db9-8fbd-584ea1becda2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69584886-8cef-4db9-8fbd-584ea1becda2&quot;&gt;Scala 3 introduit une forme de déclaration d&amp;#39;enum qui est plus similaire aux enum Java, en ayant la possibilité en plus de déclarer des ADT et des GADT.&lt;/p&gt;
&lt;!-- end paragraph 69584886-8cef-4db9-8fbd-584ea1becda2--&gt;
&lt;!-- begin code e6a80a6c-f76e-4129-9ed3-03fd1c762bb9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e6a80a6c-f76e-4129-9ed3-03fd1c762bb9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// series of constant
enum MusicalMode:
  case Ionian, Dorian, Phrygian, Lydian, Mixolydian, Aeolian, Locrian

// ADT: parameterized symbols
enum Expression:
  case Const(value: Double)
  case Add(Left: Expression, right: Expression)
  case Mul(Left: Expression, right: Expression)
  case Variable

// GADT to represent computations
enum Computation[+A]:
  def map[B](f: A =&amp;gt; B): Computation[B] =
    Computation.Map(this, f)

  def filter(p: A =&amp;gt; Boolean): Computation[A] =
    Computation.Filter(this, p)

  case Effect(a: () =&amp;gt; A) extends Computation[A]
  case Map[A, B](computation: Computation[A], f: A =&amp;gt; B) extends Computation[B]
  case Filter[A](computation: Computation[A], p: A =&amp;gt; Boolean) extends Computation[A]

object Computation:
  def apply[A](a: =&amp;gt;A): Computation[A] = Computation.Effect(() =&amp;gt; a)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e6a80a6c-f76e-4129-9ed3-03fd1c762bb9--&gt;
&lt;!-- begin paragraph e9f98712-7259-43f7-a8d8-6ebb6c8368cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e9f98712-7259-43f7-a8d8-6ebb6c8368cb&quot;&gt;L&amp;#39;instanciation d&amp;#39;un élément de l&amp;#39;enum sera typé par rapport à l&amp;#39;enum. Ainsi, que le type de &lt;code&gt;Expression.Const(2.0)&lt;/code&gt; est &lt;code&gt;Expression&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph e9f98712-7259-43f7-a8d8-6ebb6c8368cb--&gt;
&lt;!-- begin paragraph 5ae6ac27-1ea4-4b30-b1b6-58fc1e0db3eb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ae6ac27-1ea4-4b30-b1b6-58fc1e0db3eb&quot;&gt;Sur des enums de type liste de constantes, Scala fournit des fonctions d&amp;#39;exploration, comme &lt;code&gt;valueOf&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; ou &lt;code&gt;ordinal&lt;/code&gt;, que nous avons déjà côté Java.&lt;/p&gt;
&lt;!-- end paragraph 5ae6ac27-1ea4-4b30-b1b6-58fc1e0db3eb--&gt;
&lt;!-- begin paragraph abe93f4e-2749-40f6-a89b-647020f95397--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-abe93f4e-2749-40f6-a89b-647020f95397&quot;&gt;D&amp;#39;ailleurs, pour avoir des enum compatibles avec Java, il suffit d&amp;#39;étendre &lt;code&gt;java.lang.Enum&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph abe93f4e-2749-40f6-a89b-647020f95397--&gt;
&lt;!-- begin code 1326d736-81b1-4530-8358-173da901fcbb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1326d736-81b1-4530-8358-173da901fcbb&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;enum MusicalMode1 extends Enum[MusicalMode1]:
  case Ionian, Dorian, Phrygian, Lydian, Mixolydian, Aeolian, Locrian

enum MusicalMode2(degree: Int) extends Enum[MusicalMode2]:
  case Ionian     extends MusicalMode2(1)
  case Dorian     extends MusicalMode2(2)
  case Phrygian   extends MusicalMode2(3)
  case Lydian     extends MusicalMode2(4)
  case Mixolydian extends MusicalMode2(5)
  case Aeolian    extends MusicalMode2(6)
  case Locrian    extends MusicalMode2(7)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1326d736-81b1-4530-8358-173da901fcbb--&gt;
&lt;!-- begin heading_1 22736c92-ad68-41c8-a5e2-62dba96b449e--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-22736c92-ad68-41c8-a5e2-62dba96b449e&quot;&gt;Les méthodes d&amp;#39;extension&lt;/h2&gt;
&lt;!-- end heading_1 22736c92-ad68-41c8-a5e2-62dba96b449e--&gt;
&lt;!-- begin paragraph 581b9eeb-0a94-4cd9-9029-9538c7f6d0bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-581b9eeb-0a94-4cd9-9029-9538c7f6d0bb&quot;&gt;Les méthodes d&amp;#39;extension ne sont pas exactement une nouveauté en Scala. Mais il faut en Scala version 2 passer par une syntaxe verbeuse et peu explicite. En Kotlin et en C#, cette fonctionnalité existe avec une syntaxe plus explicite.&lt;/p&gt;
&lt;!-- end paragraph 581b9eeb-0a94-4cd9-9029-9538c7f6d0bb--&gt;
&lt;!-- begin paragraph 9ddfdb5a-807f-428a-9536-d4c9bbde9ad7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9ddfdb5a-807f-428a-9536-d4c9bbde9ad7&quot;&gt;Par exemple, on reçoit en entrée une série d&amp;#39;événements sur des stocks de produits indiquant leur quantité constatée et l&amp;#39;heure à laquelle le constat a été fait. Le problème est que notre classe représentant ces événements a été générée automatiquement (par exemple, depuis une représentation en Avro, Thrift ou Protobuf). Il n&amp;#39;est pas envisageable de modifier cette classe pour ajouter une méthode permettant de composer deux événements de stock.&lt;/p&gt;
&lt;!-- end paragraph 9ddfdb5a-807f-428a-9536-d4c9bbde9ad7--&gt;
&lt;!-- begin code fda49e3f-a8e4-4bb2-9728-6162e5de3310--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fda49e3f-a8e4-4bb2-9728-6162e5de3310&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// AUTO-GENERATED
case class StockEvent(product: String, quantity: Double, timestamp: Instant)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fda49e3f-a8e4-4bb2-9728-6162e5de3310--&gt;
&lt;!-- begin paragraph 234b718d-cbac-412c-9ef5-0e696db9e542--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-234b718d-cbac-412c-9ef5-0e696db9e542&quot;&gt;Pour pallier ce problème en Scala 2, il faut créer une classe implicite qui pourra être utilisée dès que la méthode de composition sera appelée sur des événements de stock. Et pas que...&lt;/p&gt;
&lt;!-- end paragraph 234b718d-cbac-412c-9ef5-0e696db9e542--&gt;
&lt;!-- begin code e2710f98-91c5-4212-91f6-1bfdd4e88509--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e2710f98-91c5-4212-91f6-1bfdd4e88509&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit class RichStockEvent(event: StockEvent) {
  def combineWith(other: StockEvent): StockEvent = ???
  def isEmpty: Boolean = event.quantity == 0
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e2710f98-91c5-4212-91f6-1bfdd4e88509--&gt;
&lt;!-- begin paragraph d790b35a-cb31-422d-949b-3900a025477b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d790b35a-cb31-422d-949b-3900a025477b&quot;&gt; En Scala 3, la syntaxe est bien plus explicite.&lt;/p&gt;
&lt;!-- end paragraph d790b35a-cb31-422d-949b-3900a025477b--&gt;
&lt;!-- begin code 05212ac0-52a3-4a53-bc7b-cce3e2ae33d6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-05212ac0-52a3-4a53-bc7b-cce3e2ae33d6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;extension (event: StockEvent)
  def combineWith(other: StockEvent): StockEvent = ???
  def isEmpty: Boolean = event.quantity == 0&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 05212ac0-52a3-4a53-bc7b-cce3e2ae33d6--&gt;
&lt;!-- begin paragraph 50119fe1-bbca-4fa3-925e-f0dfda5ba437--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50119fe1-bbca-4fa3-925e-f0dfda5ba437&quot;&gt;Dans ce cas, la syntaxe est un peu plus légère et est comprise comme le fait d&amp;#39;ajouter une nouvelle méthode à une classe existante. De plus, il n&amp;#39;est pas nécessaire de trouver à cette structure un nom qui ne sera pas utilisé.&lt;/p&gt;
&lt;!-- end paragraph 50119fe1-bbca-4fa3-925e-f0dfda5ba437--&gt;
&lt;!-- begin heading_1 84e53336-b5fc-490c-a3a8-16b215f6e8af--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-84e53336-b5fc-490c-a3a8-16b215f6e8af&quot;&gt;Import&lt;/h2&gt;
&lt;!-- end heading_1 84e53336-b5fc-490c-a3a8-16b215f6e8af--&gt;
&lt;!-- begin paragraph 1b4f068d-fe25-4143-9d57-470fb85e9ad4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1b4f068d-fe25-4143-9d57-470fb85e9ad4&quot;&gt;La syntaxe des imports change quelque peu sur des aspects spécifiques. Pour importer le contenu d&amp;#39;un module, on utilisera désormais le symbole &lt;code&gt;*&lt;/code&gt; à la place de &lt;code&gt;_&lt;/code&gt;. Par exemple : &lt;code&gt;import java.time.*&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 1b4f068d-fe25-4143-9d57-470fb85e9ad4--&gt;
&lt;!-- begin paragraph f76ac70c-4767-4148-9acf-e0fd3c0ae950--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f76ac70c-4767-4148-9acf-e0fd3c0ae950&quot;&gt;Si vous souhaitez renommer un import, il faudra utiliser le mot-clé &lt;code&gt;as&lt;/code&gt; à la place du symbole &lt;code&gt;=&amp;gt;&lt;/code&gt;. Par exemple &lt;code&gt;import java.lang.{Double as JDouble}&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph f76ac70c-4767-4148-9acf-e0fd3c0ae950--&gt;
&lt;!-- begin heading_1 e6615871-adfe-41f4-9235-a970b33285b7--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e6615871-adfe-41f4-9235-a970b33285b7&quot;&gt;Ce qui disparaît&lt;/h2&gt;
&lt;!-- end heading_1 e6615871-adfe-41f4-9235-a970b33285b7--&gt;
&lt;!-- begin paragraph 219254a6-f58a-42ee-996c-2561d361c336--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-219254a6-f58a-42ee-996c-2561d361c336&quot;&gt;La syntaxe procédurale (le fait d&amp;#39;écrire &lt;code&gt;def f() {...}&lt;/code&gt;) n&amp;#39;est plus permise en Scala 3. Il vous faudra écrire plus explicitement &lt;code&gt;def f() = {...}&lt;/code&gt; ou &lt;code&gt;def f(): Unit = {...}&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 219254a6-f58a-42ee-996c-2561d361c336--&gt;
&lt;!-- begin paragraph 518aff52-69dd-4b7b-ab2f-b1400b7d9c40--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-518aff52-69dd-4b7b-ab2f-b1400b7d9c40&quot;&gt;La structure &lt;code&gt;do...while&lt;/code&gt; disparaît notamment parce qu&amp;#39;elle est rarement utilisée, qu&amp;#39;il y a des alternatives et que le &lt;code&gt;do&lt;/code&gt; est utilisable par d&amp;#39;autres structures, comme le &lt;code&gt;for...do&lt;/code&gt; et le &lt;code&gt;while...do&lt;/code&gt;. Ceci évite la confusion.&lt;/p&gt;
&lt;!-- end paragraph 518aff52-69dd-4b7b-ab2f-b1400b7d9c40--&gt;
&lt;!-- begin paragraph 9b3075b4-b0b9-4025-aaf6-fdaabbcc89bf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b3075b4-b0b9-4025-aaf6-fdaabbcc89bf&quot;&gt;Il n&amp;#39;y a plus de limite à 22 paramètres pour les fonctions et les tuples. Dans les faits, il y a une représentation directe jusqu&amp;#39;à 22 paramètres — eg. pour les fonctions, on a les types &lt;code&gt;scala.Function1&lt;/code&gt;, &lt;code&gt;.scala.function2&lt;/code&gt;, ..., &lt;code&gt;scala.Function22&lt;/code&gt;. Au-delà, on a les types génériques &lt;code&gt;scala.runtime.FunctionXXL&lt;/code&gt; pour les fonctions et &lt;code&gt;scala.runtime.TupleXXL&lt;/code&gt; pour les tuples.&lt;/p&gt;
&lt;!-- end paragraph 9b3075b4-b0b9-4025-aaf6-fdaabbcc89bf--&gt;
&lt;!-- begin paragraph be7174a0-ac6c-43e9-a2ea-0c598b803fe2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be7174a0-ac6c-43e9-a2ea-0c598b803fe2&quot;&gt;Disparaissent aussi&lt;/p&gt;
&lt;!-- end paragraph be7174a0-ac6c-43e9-a2ea-0c598b803fe2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Les &lt;i&gt;package object&lt;/i&gt; (les déclarations de cet ordre se font directement dans le fichier).&lt;/li&gt;&lt;li&gt;Les littéraux de type symbole (eg. &lt;code&gt;`mySymbol&lt;/code&gt;), qu&amp;#39;il faudra remplacer par des appels à &lt;code&gt;Symbol&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;L&amp;#39;initialisation par joker (eg. &lt;code&gt;var v: Int = _&lt;/code&gt;). Il faudra utiliser &lt;code&gt;scala.compiletime.uninitialized&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 11997711-78cf-4738-8493-1b429bb9c66c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-11997711-78cf-4738-8493-1b429bb9c66c&quot;&gt;Tooling&lt;/h2&gt;
&lt;!-- end heading_1 11997711-78cf-4738-8493-1b429bb9c66c--&gt;
&lt;!-- begin paragraph f96f5a2a-8539-4c25-b5db-4548b0450c0f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f96f5a2a-8539-4c25-b5db-4548b0450c0f&quot;&gt;Le compilateur fournit des options (exclusives) permettant de choisir entre 4 styles de syntaxe :&lt;/p&gt;
&lt;!-- end paragraph f96f5a2a-8539-4c25-b5db-4548b0450c0f--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;-old-syntax&lt;/code&gt; vérifie que les structures de contrôle suivent l&amp;#39;ancienne syntaxe (eg. &lt;code&gt;if (cond) ... else ...&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;&lt;code&gt;-new-syntax&lt;/code&gt; vérifie que les structures de contrôle suivent la nouvelle syntaxe (eg. &lt;code&gt;if cond then ... else ...&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;&lt;code&gt;-indent&lt;/code&gt; permet les indentations significatives (eg. &lt;code&gt;class MyClass: ...&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;&lt;code&gt;-noindent&lt;/code&gt; ne permet que les accolades délimiter les blocs (eg. &lt;code&gt;class MyClass { ... }&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 68556518-1457-4865-969c-999255c21994--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-68556518-1457-4865-969c-999255c21994&quot;&gt;Avec l&amp;#39;option &lt;code&gt;-rewrite&lt;/code&gt;, le compilateur Scala réécrit le code par rapport au style de syntaxe sélectionné. Ce qui permet d&amp;#39;avoir une homogénéité de style de syntaxe sur l&amp;#39;ensemble du code d&amp;#39;un projet.&lt;/p&gt;
&lt;!-- end paragraph 68556518-1457-4865-969c-999255c21994--&gt;
&lt;!-- begin paragraph d2e269fb-58bd-41ff-a5f5-1c5309c45a1c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d2e269fb-58bd-41ff-a5f5-1c5309c45a1c&quot;&gt;Une &lt;a href=&quot;https://contributors.scala-lang.org/t/should-the-compiler-help-with-inserting-end-markes/5074&quot;&gt;discussion&lt;/a&gt; est en cours pour l&amp;#39;ajout d&amp;#39;une option qui permettrait de gérer les marqueurs de fin de bloc basé sur la syntaxe &lt;code&gt;end &amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph d2e269fb-58bd-41ff-a5f5-1c5309c45a1c--&gt;
&lt;!-- begin paragraph b65f3f11-8568-4997-99fa-6ee6d43a2aac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b65f3f11-8568-4997-99fa-6ee6d43a2aac&quot;&gt;En dehors des options du compilateur, SBT et Scalafmt sont compatibles avec Scala 3. De leur côté, le plugin Scala pour Maven et Scalafix sont en cours de développement pour cette nouvelle version.&lt;/p&gt;
&lt;!-- end paragraph b65f3f11-8568-4997-99fa-6ee6d43a2aac--&gt;
&lt;!-- begin heading_1 212996d4-fbb0-43b5-a203-be7ab6aa0b58--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-212996d4-fbb0-43b5-a203-be7ab6aa0b58&quot;&gt;Migration de Scala 2 vers Scala 3&lt;/h2&gt;
&lt;!-- end heading_1 212996d4-fbb0-43b5-a203-be7ab6aa0b58--&gt;
&lt;!-- begin paragraph 72bd3804-88e1-4128-8adc-455b045d70b8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-72bd3804-88e1-4128-8adc-455b045d70b8&quot;&gt;C&amp;#39;est certainement le sujet le plus sensible ici. Bien évidemment, si vous avez la possibilité de commencer un nouveau projet, ça peut valoir le coup de passer à Scala 3 ou de l&amp;#39;intégrer de manière sporadique sur un petit projet ou un module plus ou moins séparé. Encore faut-il que les bibliothèques, les frameworks et le reste de l&amp;#39;écosystème est suivi...&lt;/p&gt;
&lt;!-- end paragraph 72bd3804-88e1-4128-8adc-455b045d70b8--&gt;
&lt;!-- begin paragraph 712e5b66-a198-4dde-b434-80c4d531767c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-712e5b66-a198-4dde-b434-80c4d531767c&quot;&gt;Concernant le code, le fait d&amp;#39;ajouter l&amp;#39;option de compilation &lt;code&gt;-source:3.0-migration&lt;/code&gt;, fait passer le compilateur en mode migration. Il est alors plus laxiste sur certains aspects syntaxiques et fait alors apparaître des warnings sur les fonctionnalités retirées dans Scala 3. Cette option peut être utilisée conjointement avec l&amp;#39;option &lt;code&gt;-rewrite&lt;/code&gt; pour que le compilateur réécrive le code dans un format plus en adéquation avec Scala 3. Néanmoins, il est vivement conseillé de procéder par étape afin d&amp;#39;éviter de se trouver dans une impasse résultant par l&amp;#39;apparition de bug dans vos applications, car la réécriture n&amp;#39;est que techniquement sûre : 1/ compiler sans l&amp;#39;option &lt;code&gt;-rewrite&lt;/code&gt; 2/ commiter le projet pour conserver une version avant réécriture 3/ utiliser l&amp;#39;option &lt;code&gt;-rewrite&lt;/code&gt;. Pour faciliter, la compréhension des warnings, les options de compilation &lt;code&gt;-explain&lt;/code&gt; et &lt;code&gt;-explain-types&lt;/code&gt; devrait pouvoir vous aider.&lt;/p&gt;
&lt;!-- end paragraph 712e5b66-a198-4dde-b434-80c4d531767c--&gt;
&lt;!-- begin paragraph 1caa9579-7ab3-4a86-90f0-474d4c719d4f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1caa9579-7ab3-4a86-90f0-474d4c719d4f&quot;&gt;Sur le plan des dépendances, il y a un certain nombre de bibliothèques et de frameworks qui sont déjà disponibles pour Scala 3 parmi les plus populaires et les plus actifs. Mais ce n&amp;#39;est pas le cas de tous (par exemple, nous attendons avec impatience une version de Spark fonctionnant avec Scala 3). Néanmoins, les contributeurs Scala avaient annoncé une interopérabilité entre Scala 3 et Scala 2, et plus spécifiquement avec la version 2.13. Ainsi, la bibliothèque officielle de Scala 3 est la bibliothèque de Scala 2.13.&lt;/p&gt;
&lt;!-- end paragraph 1caa9579-7ab3-4a86-90f0-474d4c719d4f--&gt;
&lt;!-- begin paragraph 6bdf1969-6afb-4f19-92aa-7c2c0a3a4b6d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6bdf1969-6afb-4f19-92aa-7c2c0a3a4b6d&quot;&gt;Du côté du développement Scala quotidien, si vous travaillez avec Scala 3 et que vous avez besoin d&amp;#39;une dépendance en 2.13, il vous suffira d&amp;#39;ajout dans vos fichiers SBT l&amp;#39;option &lt;code&gt;CrossVersion.for3Use2_13&lt;/code&gt; sur vos dépendances (eg. &lt;code&gt;libraryDependencies += (&amp;quot;org.bar&amp;quot; %% &amp;quot;bar&amp;quot; % &amp;quot;1.0.0&amp;quot;).cross(CrossVersion.for3Use2_13)&lt;/code&gt;), s&amp;#39;il s&amp;#39;agit d&amp;#39;une dépendance externe. Pour une dépendance locale, il n&amp;#39;y a rien à faire à part de l&amp;#39;indiquer comme d&amp;#39;habitude (eg. &lt;code&gt;.dependsOn(my_scala3_module)&lt;/code&gt;). À l&amp;#39;inverse, si vous travaillez en Scala 2.13 et que vous avez besoin d&amp;#39;une dépendance en version 3, vous avez l&amp;#39;option &lt;code&gt;-Ytasty-reader&lt;/code&gt; à ajouter au niveau des options de compilation. Dans ce cas, si vous avez une dépendance externe, vous devez ajouter l&amp;#39;option &lt;code&gt;CrossVersion.for2_13Use3&lt;/code&gt; à votre dépendance. Il n&amp;#39;y a rien de plus à faire pour une dépendance interne.&lt;/p&gt;
&lt;!-- end paragraph 6bdf1969-6afb-4f19-92aa-7c2c0a3a4b6d--&gt;
&lt;!-- begin paragraph 3b58ca7b-6975-43c1-9b7b-418e796ca142--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b58ca7b-6975-43c1-9b7b-418e796ca142&quot;&gt;Le projet Scala fournit une documentation assez complète sur la &lt;a href=&quot;https://docs.scala-lang.org/scala3/guides/migration/tutorial-intro.html&quot;&gt;migration vers Scala 3&lt;/a&gt;. Une &lt;a href=&quot;https://docs.scala-lang.org/scala3/guides/migration/incompatibility-table.html&quot;&gt;matrice des incompatibilités&lt;/a&gt; et des &lt;a href=&quot;https://docs.scala-lang.org/scala3/guides/migration/options-lookup.html&quot;&gt;modifications d&amp;#39;option de compilation&lt;/a&gt; sont aussi consultables.&lt;/p&gt;
&lt;!-- end paragraph 3b58ca7b-6975-43c1-9b7b-418e796ca142--&gt;
&lt;!-- begin heading_1 d0e2d2ae-0b59-41de-aa33-1b31917fc0bb--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d0e2d2ae-0b59-41de-aa33-1b31917fc0bb&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 d0e2d2ae-0b59-41de-aa33-1b31917fc0bb--&gt;
&lt;!-- begin paragraph a1706776-50ef-49f6-ac6a-e5a1ed9246a8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a1706776-50ef-49f6-ac6a-e5a1ed9246a8&quot;&gt;Scala 3 arrive avec une syntaxe assez différente de Scala 2. Ce qui fait de Scala 3 un langage différent, mais avec des compatibilités vis-à-vis de l&amp;#39;ancienne version. Du côté écosystème, les contributeurs font émerger des mises à jour de leur projet basé sur Scala au fur et à mesure.&lt;/p&gt;
&lt;!-- end paragraph a1706776-50ef-49f6-ac6a-e5a1ed9246a8--&gt;
&lt;!-- begin paragraph 061423c1-bbfe-42ad-afc1-cf4a51fcd2d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-061423c1-bbfe-42ad-afc1-cf4a51fcd2d7&quot;&gt;Néanmoins, le spectre douloureux de la migration Python 2 vers Python 3 qui s&amp;#39;est étalé sur une décennie est relativement présent autour de l&amp;#39;adoption de Scala 3. La difficulté de cette migration fait qu&amp;#39;il est apparu un mur de la honte indiquant les bibliothèques n&amp;#39;étant pas encore passées à Python 3 et qu&amp;#39;il a été imposé une date de fin de maintenance de la version 2.&lt;/p&gt;
&lt;!-- end paragraph 061423c1-bbfe-42ad-afc1-cf4a51fcd2d7--&gt;
&lt;!-- begin paragraph 8ac87059-106b-47d8-8079-c58426f7c80b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8ac87059-106b-47d8-8079-c58426f7c80b&quot;&gt;De son côté, la communauté Scala (en dehors de ses dramas) semble vouloir avancer dans le même sens pour l&amp;#39;adoption de Scala 3. Elle est tout à fait consciente des difficultés de la migration Python 2 vers Python 3. Néanmoins, Scala n&amp;#39;est pas Python. Il n&amp;#39;a ni son organisation ni le même périmètre d&amp;#39;utilisation. Le projet sur lequel repose Scala 3 existe depuis 8 ans. Des annonces ont régulièrement été faites pour montrer les nouveautés à venir et donner une roadmap de plus en plus précise. Les fonctionnalités de Scala 3 pouvaient être testées bien avant ça sortie. Les principales bibliothèques ont régulièrement sorti des releases sur les pré-versions de Scala 3. Des workshops ont été organisés sur Scala 3. Les pré-versions de Scala 3 ont été automatiquement testées pour tenter de compiler un ensemble de projets Scala OSS. On peut s&amp;#39;attendre ainsi à une migration relativement simple entre Scala 2 et Scala 3, même si en réalité ce n&amp;#39;est jamais simple.&lt;/p&gt;
&lt;!-- end paragraph 8ac87059-106b-47d8-8079-c58426f7c80b--&gt;
&lt;!-- begin paragraph 77f20443-bb84-41ff-885e-a8ffa7835e62--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77f20443-bb84-41ff-885e-a8ffa7835e62&quot;&gt;Photographie par &lt;a href=&quot;https://unsplash.com/@jacktthunter?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Jack Hunter&lt;/a&gt; sur &lt;a href=&quot;https://unsplash.com/s/photos/three?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 77f20443-bb84-41ff-885e-a8ffa7835e62--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:8c7667af951f4dec8a8fd30b89215a71</id>
    <title>Retour d&#039;expérience : optimisation de traitement Spark - Le piège du tout DataFrame</title>
    <updated>2021-02-16T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/retour-d-experience-optimisation-de-traitement-spark-le-piege-du-tout-dataframe.html"/>
    <!--summary Dans ce retour d&#039;expérience, nous allons voir que les dataframes de Spark SQL et son API SQL ne permettent pas de répondre à tous les besoins.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Spark"></category>    <category term="SparkSQL"></category>    <category term="Optimisation"></category>    <content type="html">
&lt;!-- begin heading_1 2281d122-e218-4859-8b6e-057de55435ec--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2281d122-e218-4859-8b6e-057de55435ec&quot;&gt;Le traitement&lt;/h2&gt;
&lt;!-- end heading_1 2281d122-e218-4859-8b6e-057de55435ec--&gt;
&lt;!-- begin paragraph 992ede0f-d466-4161-a958-47b2efaae965--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-992ede0f-d466-4161-a958-47b2efaae965&quot;&gt;Nous avons en entrée une structure comme celle-ci&lt;/p&gt;
&lt;!-- end paragraph 992ede0f-d466-4161-a958-47b2efaae965--&gt;
&lt;!-- begin code 0ae285cc-bba1-4a4f-8410-6327378778bf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0ae285cc-bba1-4a4f-8410-6327378778bf&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;id&amp;quot;: &amp;quot;1234&amp;quot;,
  &amp;quot;created&amp;quot;: &amp;quot;2021-01-01T00:00:00&amp;quot;,
  &amp;quot;criteriaA_periods&amp;quot;: [
    { &amp;quot;begin&amp;quot;: &amp;quot;2021-01-03T00:00:00&amp;quot;, &amp;quot;end&amp;quot;: &amp;quot;2021-01-05T00:00:00&amp;quot; },
    { &amp;quot;begin&amp;quot;: &amp;quot;2021-01-07T00:00:00&amp;quot;, &amp;quot;end&amp;quot;: &amp;quot;2021-01-08T00:00:00&amp;quot; },
  ],
  &amp;quot;criteriaB_periods&amp;quot;: [],
  &amp;quot;criteriaC_periods&amp;quot;: [],
  &amp;quot;criteriaD_periods&amp;quot;: [
    { &amp;quot;value&amp;quot;: 12,
      &amp;quot;begin&amp;quot;: &amp;quot;2021-01-02T00:00:00&amp;quot;, &amp;quot;end&amp;quot;: &amp;quot;2021-01-05T00:00:00&amp;quot; }
  ],
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0ae285cc-bba1-4a4f-8410-6327378778bf--&gt;
&lt;!-- begin paragraph 9be070db-7dab-4c62-953a-6820dcd64be1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9be070db-7dab-4c62-953a-6820dcd64be1&quot;&gt;Elle contient des périodes d&amp;#39;application de divers critères A, B, C, et D. En période d&amp;#39;application, on utilise la valeur 1, sinon c&amp;#39;est 0. Pour le critère D, en période d&amp;#39;application on utilise la valeur fournit dans le critère, sinon c&amp;#39;est une valeur par défaut.&lt;/p&gt;
&lt;!-- end paragraph 9be070db-7dab-4c62-953a-6820dcd64be1--&gt;
&lt;!-- begin paragraph a58841d1-0452-44be-8758-82eb89557f50--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a58841d1-0452-44be-8758-82eb89557f50&quot;&gt;Par transformation Spark, on veut une structure plus linéaire qui au 1er janvier 2021 ressemble à&lt;/p&gt;
&lt;!-- end paragraph a58841d1-0452-44be-8758-82eb89557f50--&gt;
&lt;!-- begin code e69266a1-c9dc-4899-9f0f-cde701faee56--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e69266a1-c9dc-4899-9f0f-cde701faee56&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;id&amp;quot;: &amp;quot;1234&amp;quot;,
  &amp;quot;created&amp;quot;: &amp;quot;2021-01-01T00:00:00&amp;quot;,
  &amp;quot;applicationDatetime&amp;quot;: &amp;quot;2021-01-01T00:00:00&amp;quot;,
  &amp;quot;criteriaA_availability&amp;quot;: [ 0,  0,  1,  1,  1,  0,  1,  1],
  &amp;quot;criteriaB_availability&amp;quot;: [ 0,  0,  0,  0,  0,  0,  0,  0],
  &amp;quot;criteriaC_availability&amp;quot;: [ 0,  0,  0,  0,  0,  0,  0,  0],
  &amp;quot;criteriaD_availability&amp;quot;: [30, 12, 12, 12, 12, 30, 30, 30],
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e69266a1-c9dc-4899-9f0f-cde701faee56--&gt;
&lt;!-- begin paragraph 4e6f37d0-becc-4ff3-a9e0-2c982b3d34f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e6f37d0-becc-4ff3-a9e0-2c982b3d34f2&quot;&gt;Dans la structure de sortie, chaque critère contient une liste dans laquelle l&amp;#39;indice (commençant à 0) représente le décalage par rapport à un jour donné (ie. l&amp;#39;indice 0 correspond à J, 1 à J+1, 2 à J+2...). &lt;/p&gt;
&lt;!-- end paragraph 4e6f37d0-becc-4ff3-a9e0-2c982b3d34f2--&gt;
&lt;!-- begin paragraph f6067dde-a973-41eb-962a-e3e7820463e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f6067dde-a973-41eb-962a-e3e7820463e4&quot;&gt;Les données d&amp;#39;entrée sont stockées dans une base et nous devons utiliser Spark pour les récupérer, calculer la nouvelle structure et les intégrer dans une partie d&amp;#39;un autre traitement Spark.&lt;/p&gt;
&lt;!-- end paragraph f6067dde-a973-41eb-962a-e3e7820463e4--&gt;
&lt;!-- begin paragraph 5638dee2-32bb-4248-a603-f9cc28505844--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5638dee2-32bb-4248-a603-f9cc28505844&quot;&gt;Deux solutions s&amp;#39;offrent à nous avec Spark SQL : API SQL ou API fonctionnelle. Et il y a une contrainte : c&amp;#39;est du Spark 2.2.&lt;/p&gt;
&lt;!-- end paragraph 5638dee2-32bb-4248-a603-f9cc28505844--&gt;
&lt;!-- begin heading_1 c5ac5619-1e39-4d5e-8180-c84fe5bf0bd8--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c5ac5619-1e39-4d5e-8180-c84fe5bf0bd8&quot;&gt;Avec l&amp;#39;API SQL&lt;/h2&gt;
&lt;!-- end heading_1 c5ac5619-1e39-4d5e-8180-c84fe5bf0bd8--&gt;
&lt;!-- begin paragraph 14b09b7a-62f2-4ac7-8549-a40105fdf0c4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-14b09b7a-62f2-4ac7-8549-a40105fdf0c4&quot;&gt;J&amp;#39;ai l&amp;#39;habitude de penser que par défaut il faut utiliser Spark SQL et les fonctions que fournit le module. Ainsi, nous pouvons bénéficier des optimisations proposées par Catalyst, sachant qu&amp;#39;il y a de fortes chances que Catalyst se débrouille mieux que nous pour obtenir des jobs performants.&lt;/p&gt;
&lt;!-- end paragraph 14b09b7a-62f2-4ac7-8549-a40105fdf0c4--&gt;
&lt;!-- begin paragraph 5440d447-de92-4942-9222-a661685742d0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5440d447-de92-4942-9222-a661685742d0&quot;&gt;Le code récupéré permettant de résoudre le problème présenté ici est en effet basé sur l&amp;#39;API SQL de Spark SQL. Sur un problème qui semble linéaire, je me suis retrouvé avec des requêtes Spark SQL utilisant des explodes, divers filtres et des jointures effectuées sur le même datasource. Le DAG de la requête est assez monstrueux, avec 5 scans (LocaTableScan) des mêmes données !&lt;/p&gt;
&lt;!-- end paragraph 5440d447-de92-4942-9222-a661685742d0--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8c7667af-951f-4dec-8a8f-d30b89215a71/screenshot_2021-02-02_at_11.36.52.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 200fb148-0959-441c-9cb9-847bedf4e59f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-200fb148-0959-441c-9cb9-847bedf4e59f&quot;&gt;On a un total de 5 jobs et 10 stages.&lt;/p&gt;
&lt;!-- end paragraph 200fb148-0959-441c-9cb9-847bedf4e59f--&gt;
&lt;!-- begin paragraph c7d36b70-c28a-40cf-a0ab-2ec1ec46af0e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c7d36b70-c28a-40cf-a0ab-2ec1ec46af0e&quot;&gt;La base du calcul est représentée par cette fonction qui pour chaque critère (représenté par la colonne en paramètre) va créer un &lt;i&gt;array&lt;/i&gt; de flag.&lt;/p&gt;
&lt;!-- end paragraph c7d36b70-c28a-40cf-a0ab-2ec1ec46af0e--&gt;
&lt;!-- begin code 0567eaeb-0f5a-4ba5-a51f-dfa495245c14--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0567eaeb-0f5a-4ba5-a51f-dfa495245c14&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def availability(dataset: DataFrame,
                 criteriaColumn: Column,
                 dayCount: Int,
                 currentDate: String,
                 value: Option[Int],
                 defaultValue: Int): DataFrame =
  (0 to dayCount)
    .foldLeft(dataset.withColumn(&amp;quot;interval&amp;quot;, explode(criteriaColumn))) {
      case (ds, i) =&amp;gt;
        ds.withColumn(s&amp;quot;int_$i&amp;quot;,
          when(
            date_add(lit(currentDate), days=i)
              .between($&amp;quot;interval.begin&amp;quot;, $&amp;quot;interval.end&amp;quot;),
            value
              .map(c =&amp;gt; col(s&amp;quot;interval.$c&amp;quot;))
              .getOrElse(1)
          ).otherwise(defaultValue)
        )
    }
    .groupBy(&amp;quot;id&amp;quot;)
    .agg(array(
      (0 to dayCount).map { i =&amp;gt; max(s&amp;quot;int_$i&amp;quot;) }: _*
    ) as &amp;quot;ints&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0567eaeb-0f5a-4ba5-a51f-dfa495245c14--&gt;
&lt;!-- begin paragraph d15a7418-4393-464f-b107-4365d2f80291--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d15a7418-4393-464f-b107-4365d2f80291&quot;&gt;Cette fonction est donc appelée 4 fois (1 fois par critère) sur le même dataframe. Chaque appel fournit un dataframe. Ces dataframes sont ensuite regroupés par des jointures successives basées sur l&amp;#39;id. Un filtrage est aussi réalisé sur le dataframe de base. On arrive ainsi à 5 scans !&lt;/p&gt;
&lt;!-- end paragraph d15a7418-4393-464f-b107-4365d2f80291--&gt;
&lt;!-- begin paragraph 54571b06-d631-4380-8d00-1ab4c9adcc4a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-54571b06-d631-4380-8d00-1ab4c9adcc4a&quot;&gt;L&amp;#39;une des raisons de cette complexité est due au fait que l&amp;#39;API SQL de Spark SQL ne permet pas de travailler finement sur les données prises lignes par lignes, sauf à utiliser éventuellement des UDF. Ça ne veut pas dire que cette approche est mauvaise. Sur des requêtes ayant des formes plus classiques (du simple SELECT à une série de jointures sur des formats divers impliquant des transformations usuelles de colonnes) Spark SQL aura probablement de meilleures performances que ce que vous pourrez faire avec Spark Core ou l&amp;#39;API fonctionnel des Datasets. Le problème ici n&amp;#39;est pas classique d&amp;#39;un point de vue Spark SQL et l&amp;#39;effet est à peu près le même que d&amp;#39;utiliser une formule 1 sur de l&amp;#39;herbe : ça ne va pas aller très vite parce que ce n&amp;#39;est pas prévu pour ça.&lt;/p&gt;
&lt;!-- end paragraph 54571b06-d631-4380-8d00-1ab4c9adcc4a--&gt;
&lt;!-- begin heading_1 9c5dfc53-1a15-4451-94cd-145aec1227f2--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9c5dfc53-1a15-4451-94cd-145aec1227f2&quot;&gt;Vers l&amp;#39;API fonctionnelle&lt;/h2&gt;
&lt;!-- end heading_1 9c5dfc53-1a15-4451-94cd-145aec1227f2--&gt;
&lt;!-- begin paragraph 5b110a11-b887-4246-91c4-7ccb6d93139f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5b110a11-b887-4246-91c4-7ccb6d93139f&quot;&gt;Réfléchissons en se demandant comment faire si nous n&amp;#39;avions pas Spark sous la main ?&lt;/p&gt;
&lt;!-- end paragraph 5b110a11-b887-4246-91c4-7ccb6d93139f--&gt;
&lt;!-- begin paragraph 1e36d385-f006-4d93-864c-c21a4c0f0f62--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1e36d385-f006-4d93-864c-c21a4c0f0f62&quot;&gt;Réponse : on parcourrait les données en une passe. Pour chaque donnée rencontrée on ferait une conversion de la structure d&amp;#39;entrée en structure de sortie sans faire appel à des données externes à part d&amp;#39;éventuels paramètres intégrés au traitement. Si on veut accélérer les choses on peut même partitionner les données en fonction du nombre de machines et de cœurs mis à disposition. On a ainsi un traitement linéaire et plutôt simple.&lt;/p&gt;
&lt;!-- end paragraph 1e36d385-f006-4d93-864c-c21a4c0f0f62--&gt;
&lt;!-- begin paragraph 4079652b-1146-44a7-8497-988055e3114c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4079652b-1146-44a7-8497-988055e3114c&quot;&gt;Si on revient à Spark, on peut récupérer les données au sein d&amp;#39;un dataset et faire appel à la fonction map. Spark s&amp;#39;occupera ensuite du partitionnement, de la parallélisation et la distribution du traitement.&lt;/p&gt;
&lt;!-- end paragraph 4079652b-1146-44a7-8497-988055e3114c--&gt;
&lt;!-- begin paragraph 87d61510-e62d-4e10-b75c-f50a0560f05f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-87d61510-e62d-4e10-b75c-f50a0560f05f&quot;&gt;Résultat : un seul job avec un seul stage et un beau DAG bien linéaire !&lt;/p&gt;
&lt;!-- end paragraph 87d61510-e62d-4e10-b75c-f50a0560f05f--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8c7667af-951f-4dec-8a8f-d30b89215a71/screenshot_2021-02-02_at_12.00.47.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 9c4e5271-447f-4646-8f25-9ad973608277--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c4e5271-447f-4646-8f25-9ad973608277&quot;&gt;Son DAG indique que les données ne sont parcourues qu&amp;#39;une seule fois. Les 100 000 lignes sont traitées en 1,3 secondes (sur ma machine) avec en extrémité des phases de sérialisation. On peut regarder dans le détail&lt;/p&gt;
&lt;!-- end paragraph 9c4e5271-447f-4646-8f25-9ad973608277--&gt;
&lt;!-- begin paragraph 3ddc562d-3746-45da-8bec-540da13abdfd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ddc562d-3746-45da-8bec-540da13abdfd&quot;&gt;Dans le graphique ci-dessous, les phases de scheduler delay et de serde représente un overhead de ~350 ms contre 1 seconde de traitement (toujours sur ma machine).&lt;/p&gt;
&lt;!-- end paragraph 3ddc562d-3746-45da-8bec-540da13abdfd--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8c7667af-951f-4dec-8a8f-d30b89215a71/screenshot_2021-02-02_at_12.05.45.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8c7667af-951f-4dec-8a8f-d30b89215a71/screenshot_2021-02-02_at_12.07.01.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 0d6b828a-ac15-4913-b45e-58c24286a593--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0d6b828a-ac15-4913-b45e-58c24286a593&quot;&gt;Le principe de la solution consiste d&amp;#39;abord à convertir chaque ligne sous la forme d&amp;#39;une case class appropriée sous la forme &lt;code&gt;dataframe.as[MyCaseClass]&lt;/code&gt;. La conversion linéaire est ensuite réalisée en faisant un &lt;code&gt;map&lt;/code&gt; sur le dataset, qui permettra d&amp;#39;obtenir pour chaque ligne une case class correspondant au schéma attendu en sortie.&lt;/p&gt;
&lt;!-- end paragraph 0d6b828a-ac15-4913-b45e-58c24286a593--&gt;
&lt;!-- begin paragraph 132bacaf-c23e-4109-9b4d-d21b70b8fc23--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-132bacaf-c23e-4109-9b4d-d21b70b8fc23&quot;&gt;Chaque période est d&amp;#39;abord convertit en WeightedPeriod, dont le poids vaut 1 pour les critères A, B et C.&lt;/p&gt;
&lt;!-- end paragraph 132bacaf-c23e-4109-9b4d-d21b70b8fc23--&gt;
&lt;!-- begin code 548325bc-311c-4788-a00f-a50467a30fb9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-548325bc-311c-4788-a00f-a50467a30fb9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class WeightedPeriod(weight: Int,
                          begin: LocalDateTime,
                          end: LocalDateTime) {
  def contains(ts: LocalDate): Boolean =
    (begin.equals(ts)
      || end.equals(ts)
      || (ts.isAfter(begin) &amp;amp;&amp;amp; ts.isBefore(end)))
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 548325bc-311c-4788-a00f-a50467a30fb9--&gt;
&lt;!-- begin paragraph 3e8442b7-9b58-490a-b428-043b19f51931--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3e8442b7-9b58-490a-b428-043b19f51931&quot;&gt;Puis les périodes des critères sont transformées en tableau de flags avec cette méthode&lt;/p&gt;
&lt;!-- end paragraph 3e8442b7-9b58-490a-b428-043b19f51931--&gt;
&lt;!-- begin code 133bd128-2426-456a-971f-ee47044d55bb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-133bd128-2426-456a-971f-ee47044d55bb&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def availability(periods: Seq[WeightedPeriod],
                 dayCount: Int,
                 current: LocalDateTime,
                 defaultValue: Int): Seq[Int] =
  (0 to dayCount)
    .map { deltaDay =&amp;gt;
      val offsetDay = current.plusDays(deltaDay)

      periods.find(_.contains(offsetDay))
        .map(_.weight)
        .getOrElse(defaultValue)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 133bd128-2426-456a-971f-ee47044d55bb--&gt;
&lt;!-- begin paragraph c661d6d5-79f0-415f-bc88-537ec8318d7d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c661d6d5-79f0-415f-bc88-537ec8318d7d&quot;&gt;Ce qui donne&lt;/p&gt;
&lt;!-- end paragraph c661d6d5-79f0-415f-bc88-537ec8318d7d--&gt;
&lt;!-- begin code ded24361-6631-4bff-9a4d-4c63cb95f049--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ded24361-6631-4bff-9a4d-4c63cb95f049&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;dataframe
  .as[InputEntity]
  .map(input =&amp;gt; OutputEntity(
    id = input.id,
    created = id.created,
    criteriaA_availability = availability(id.criteriaA_periods.map(_.withWeight(1), dayCount, currentDateTime, defaultValue=0),
    criteriaB_availability = availability(id.criteriaB_periods.map(_.withWeight(1), dayCount, currentDateTime, defaultValue=0),
    criteriaC_availability = availability(id.criteriaC_periods.map(_.withWeight(1), dayCount, currentDateTime, defaultValue=0),
    criteriaD_availability = availability(id.criteriad_periods.map(_.toWeight, dayCount, currentDateTime, defaultValue=defaultD),
  ))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ded24361-6631-4bff-9a4d-4c63cb95f049--&gt;
&lt;!-- begin heading_1 5d39a948-33f3-4d6d-a6c6-8d45edf42a17--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5d39a948-33f3-4d6d-a6c6-8d45edf42a17&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 5d39a948-33f3-4d6d-a6c6-8d45edf42a17--&gt;
&lt;!-- begin paragraph 885d315a-8efd-4ffd-8e70-aa2c9a4104fe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-885d315a-8efd-4ffd-8e70-aa2c9a4104fe&quot;&gt;Spark SQL est un outil formidable, permettant de réaliser simplement des traitements complexes et avec un niveau d&amp;#39;optimisation difficile à obtenir à la main. Néanmoins, l&amp;#39;API SQL de ce framework ne doit pas nous faire oublier qu&amp;#39;il faut analyser ce qu&amp;#39;il se passe lorsqu&amp;#39;on envoie nos requêtes à Spark. Car cette API n&amp;#39;est pas adaptée à tous les cas. Il est des cas où elle peut être plus une lourdeur qu&amp;#39;un véritable atout. Il faut dans ce cas ne pas hésiter à utiliser des UDF ou alors à se rapprocher de l&amp;#39;API fonctionnelle afin de retrouver des performances raisonnables. Car Spark reste néanmoins l&amp;#39;un des frameworks les plus performants pour ce qui concerne les traitements Big Data (qu&amp;#39;il faut bien comprendre comme de la donnée éparpillée sur un cluster, trop volumineuse pour tenir sur une seule machine) avec une API relativement confortable.&lt;/p&gt;
&lt;!-- end paragraph 885d315a-8efd-4ffd-8e70-aa2c9a4104fe--&gt;
&lt;!-- begin paragraph e509f99e-fe84-4874-8bb9-29de55ade4ab--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e509f99e-fe84-4874-8bb9-29de55ade4ab&quot;&gt;Ceci dit, nous sommes en Spark 2.2 dans ce REX. Ce qui signifie que cette conclusion est à édulcorer. Sachant que les versions plus récentes de Spark permettent d&amp;#39;intégrer des fonctions d&amp;#39;ordre supérieur dans les requêtes SQL, il serait possible d&amp;#39;exprimer simplement une solution à ce problème dans une requête.&lt;/p&gt;
&lt;!-- end paragraph e509f99e-fe84-4874-8bb9-29de55ade4ab--&gt;
&lt;!-- begin paragraph 08f2ddd8-ab9c-49fe-837e-be300dc3f561--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-08f2ddd8-ab9c-49fe-837e-be300dc3f561&quot;&gt;Photographie par &lt;a href=&quot;https://unsplash.com/@viazavier?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Laura Ockel&lt;/a&gt; sur &lt;a href=&quot;https://unsplash.com/s/photos/gear?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 08f2ddd8-ab9c-49fe-837e-be300dc3f561--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:13d0f8b6efea44ed97ff29232a149417</id>
    <title>Retour d&#039;expérience : donner une étude de cas à des étudiants</title>
    <updated>2021-02-10T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/retour-d-experience-donner-une-etude-de-cas-a-des-etudiants.html"/>
    <!--summary Je vous propose un retour d’expérience sur un dispositif d&#039;évaluation que nous avons eu l&#039;occasion de mettre en place dans le cadre d&#039;une formation auprès de l&#039;école d&#039;ingénieur ESIPE de l&#039;Université Gustave Eiffel. La formation était donnée à des étudiants en 3e année de cycle ingénieur sur le thème des bases de données pour le big data.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Formation"></category>    <content type="html">
&lt;!-- begin paragraph e57ba191-609c-41d9-ab64-3a3837a0ed97--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e57ba191-609c-41d9-ab64-3a3837a0ed97&quot;&gt;Je vous propose un retour d’expérience sur un dispositif d&amp;#39;évaluation que nous avons eu l&amp;#39;occasion de mettre en place dans le cadre d&amp;#39;une formation auprès de l&amp;#39;école d&amp;#39;ingénieur ESIPE de l&amp;#39;Université Gustave Eiffel. La formation était donnée à des étudiants en 3e année de cycle ingénieur sur le thème des bases de données pour le big data, que j&amp;#39;ai orienté pour évoquer différentes technologies NoSQL, l&amp;#39;utilisation des techniques NoSQL pour mettre en place une plateforme de stream processing (hello Kafka) et surtout un cas d&amp;#39;usage en introduction pour montrer un exemple où le métier de &lt;i&gt;data engineer&lt;/i&gt; est nécessaire et ce qu&amp;#39;il implique.&lt;/p&gt;
&lt;!-- end paragraph e57ba191-609c-41d9-ab64-3a3837a0ed97--&gt;
&lt;!-- begin paragraph 2762ae9c-f5a5-481a-8c37-296dcb85cc52--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2762ae9c-f5a5-481a-8c37-296dcb85cc52&quot;&gt;Nous sommes alors autour d&amp;#39;octobre-novembre 2020. Si les premiers cours ont eu lieu sur place mais masqué (sur un slide de présentation, j&amp;#39;avais alors montré une photo de moi-même en disant : &amp;quot;voilà, c&amp;#39;est moi sans le masque.&amp;quot;), les cours suivants se sont faits à distance. Il n&amp;#39;y avait ni TD ni TP par manque de temps de préparation. Il restait alors la question de la validation du module de formation pour les étudiants.&lt;/p&gt;
&lt;!-- end paragraph 2762ae9c-f5a5-481a-8c37-296dcb85cc52--&gt;
&lt;!-- begin heading_1 9791a16a-e661-41f3-b5f4-867f9c91b39c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9791a16a-e661-41f3-b5f4-867f9c91b39c&quot;&gt;Que doit retenir un étudiant d&amp;#39;un module de formation ?&lt;/h2&gt;
&lt;!-- end heading_1 9791a16a-e661-41f3-b5f4-867f9c91b39c--&gt;
&lt;!-- begin paragraph 16f2bdba-b46f-42ef-bd74-3aa89489e336--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-16f2bdba-b46f-42ef-bd74-3aa89489e336&quot;&gt;Bien souvent, la validation d&amp;#39;un module passe par la validation des connaissances acquises lors d&amp;#39;un examen donné sur un temps limité. Nous avons tous vécu ça. Et si ce format permet de vérifier que les leçons sont (partiellement) bien apprises et de faciliter les corrections, son intérêt s&amp;#39;arrête là ! La place pour la créativité est très limitée. C&amp;#39;est pourtant un point essentiel pour un domaine — le domaine de l&amp;#39;informatique tout entier — qui se renouvelle régulièrement.&lt;/p&gt;
&lt;!-- end paragraph 16f2bdba-b46f-42ef-bd74-3aa89489e336--&gt;
&lt;!-- begin paragraph 3a2355a9-1954-49f9-b719-f573880527cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3a2355a9-1954-49f9-b719-f573880527cb&quot;&gt;Lors de mon cours, j&amp;#39;ai cherché à montrer qu&amp;#39;avec de bonnes bases (principalement le livre de Martin Kleppmann d&amp;#39;un côté et du code source de l&amp;#39;autre), le fonctionnement de systèmes NoSQL devient plus limpide, incluant d&amp;#39;ailleurs quelques live coding pour recréer des commits logs ou des protocoles pour gérer des requêtes... Le point étant que les technologies présentées dans le cours sont beaucoup plus accessibles qu&amp;#39;elles n&amp;#39;y paraissent. Si lors du cours, il n&amp;#39;est pas possible de tout voir, il doit fournir un minimum de pointeurs pour permettre d&amp;#39;en apprendre plus par la suite (et même avec ça, ce n&amp;#39;est pas suffisant *frustration personnelle*).&lt;/p&gt;
&lt;!-- end paragraph 3a2355a9-1954-49f9-b719-f573880527cb--&gt;
&lt;!-- begin paragraph cd3ec7e2-0ed0-481a-ad6d-fabf7f26907d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd3ec7e2-0ed0-481a-ad6d-fabf7f26907d&quot;&gt;Comme je sentais que le cours ne suffisait pas, j&amp;#39;avais envie que la partie validation du module soit un complément où l&amp;#39;étudiant aurait l&amp;#39;occasion de voir et apprendre par lui-même. Et donc, l&amp;#39;examen en mode devoir sur table ne me convenait pas.&lt;/p&gt;
&lt;!-- end paragraph cd3ec7e2-0ed0-481a-ad6d-fabf7f26907d--&gt;
&lt;!-- begin paragraph 53a3ad76-dcce-4b28-b159-b4dbaa459b8c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-53a3ad76-dcce-4b28-b159-b4dbaa459b8c&quot;&gt;J&amp;#39;ai dans ma carrière participé à des missions d&amp;#39;audit et de conseil. En peu de temps, il faut fournir une solution à un client suite à un problème rencontré. Dans ce laps de temps, il faut à la fois se baser sur ses connaissances et son expérience pour imaginer des solutions adaptées, et dans la mesure cela ne suffit pas, éventuellement effectuer des recherches sur l&amp;#39;existant pour trouver de nouvelles façons de faire ou valider des idées.&lt;/p&gt;
&lt;!-- end paragraph 53a3ad76-dcce-4b28-b159-b4dbaa459b8c--&gt;
&lt;!-- begin paragraph 56bf1e50-8382-4c94-9e52-7cd314d14836--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-56bf1e50-8382-4c94-9e52-7cd314d14836&quot;&gt;J&amp;#39;ai trouvé cette approche beaucoup plus adaptée par rapport à ce que je cherchais à mettre en place pour la validation du module de formation. Pour un étudiant sans expérience, elle implique en effet :&lt;/p&gt;
&lt;!-- end paragraph 56bf1e50-8382-4c94-9e52-7cd314d14836--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;De voir un exemple de problématique rencontré chez des clients (j&amp;#39;avais alors choisi un sujet proche d&amp;#39;une de mes expériences).&lt;/li&gt;&lt;li&gt;D&amp;#39;avoir un complément par rapport au cours et tout en ayant la possibilité d&amp;#39;y apporter un regard critique.&lt;/li&gt;&lt;li&gt;D&amp;#39;effectuer des recherches et explorer des solutions existantes pour répondre au besoin.&lt;/li&gt;&lt;li&gt;Éventuellement de confronter ses ébauches de solutions face à des personnes ayant plus d&amp;#39;expériences.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 2dabd922-40fe-40b4-8026-010b98c2e9a3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2dabd922-40fe-40b4-8026-010b98c2e9a3&quot;&gt;Pour faciliter le travail, surtout en période de confinement, les étudiants étaient regroupés, sur fond de &lt;i&gt;pizza team&lt;/i&gt; !&lt;/p&gt;
&lt;!-- end paragraph 2dabd922-40fe-40b4-8026-010b98c2e9a3--&gt;
&lt;!-- begin heading_1 5e45caee-27a9-4f06-9342-2f5969878fdb--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5e45caee-27a9-4f06-9342-2f5969878fdb&quot;&gt;Sujet&lt;/h2&gt;
&lt;!-- end heading_1 5e45caee-27a9-4f06-9342-2f5969878fdb--&gt;
&lt;!-- begin paragraph 2cede448-abce-4fa3-80ed-687ca07970b9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2cede448-abce-4fa3-80ed-687ca07970b9&quot;&gt;Une entreprise gère indépendamment un ensemble de sources de données concernant des utilisateurs de son offre numérique (site web, applis mobiles, réseau social...). Elles sont mises à disposition pour différents usages métiers. Ces sources ont un certains débits : certaines sont &amp;quot;continues&amp;quot; ou aléatoires, d&amp;#39;autres fournissent des données à intervalle fixe. Elle ne dépasse pas le gigaoctet a par une seule qui fournit 400 Go par jour. Ces sources sont localisées dans des bases de données ou des fichiers localisés dans l’entreprise ou dans des stockages de type cloud.&lt;/p&gt;
&lt;!-- end paragraph 2cede448-abce-4fa3-80ed-687ca07970b9--&gt;
&lt;!-- begin paragraph cb51c25e-e25e-43f6-9caa-86cfecd285ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cb51c25e-e25e-43f6-9caa-86cfecd285ad&quot;&gt;L&amp;#39;entreprise souhaite concentrer sur une seule plateforme ces données concernant ses utilisateurs pour des usages marketing.&lt;/p&gt;
&lt;!-- end paragraph cb51c25e-e25e-43f6-9caa-86cfecd285ad--&gt;
&lt;!-- begin paragraph 36a4becb-9dd4-439f-b2ed-c1b57dcd899d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-36a4becb-9dd4-439f-b2ed-c1b57dcd899d&quot;&gt;Il est demandé de proposer une recommandation architecturale sur laquelle reposerait une telle plateforme.&lt;/p&gt;
&lt;!-- end paragraph 36a4becb-9dd4-439f-b2ed-c1b57dcd899d--&gt;
&lt;!-- begin paragraph ccd68959-00a8-4c35-8a82-4802f35e8d0c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ccd68959-00a8-4c35-8a82-4802f35e8d0c&quot;&gt;Le sujet précise :&lt;/p&gt;
&lt;!-- end paragraph ccd68959-00a8-4c35-8a82-4802f35e8d0c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;L&amp;#39;usage actuel des sources avec plus ou moins de précisions.&lt;/li&gt;&lt;li&gt;Le temps de rétention pour certaines sources de données (90 jours).&lt;/li&gt;&lt;li&gt;La fréquence de mise à jour attendue des données de la plateforme (1 fois par jour).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 111099b5-03b2-4faf-9fe2-7ca0a28e09f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-111099b5-03b2-4faf-9fe2-7ca0a28e09f0&quot;&gt;Il est précisé aussi que le client a une équipe technique, mais que ses connaissances en termes de base de données se limitent à MySQL et Elasticsearch.&lt;/p&gt;
&lt;!-- end paragraph 111099b5-03b2-4faf-9fe2-7ca0a28e09f0--&gt;
&lt;!-- begin paragraph 1c3c5f1b-8c27-4d74-ac2c-be752671e5e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1c3c5f1b-8c27-4d74-ac2c-be752671e5e4&quot;&gt;Le sujet tient sur deux pages et est volontairement incomplet. Le fait que les étudiants demandent des précisions par la suite fait partie de l&amp;#39;exercice.&lt;/p&gt;
&lt;!-- end paragraph 1c3c5f1b-8c27-4d74-ac2c-be752671e5e4--&gt;
&lt;!-- begin paragraph e8e16c13-e94a-455b-863d-efbfbd52c8d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e8e16c13-e94a-455b-863d-efbfbd52c8d3&quot;&gt;Des questions sont ajoutées au sujet, demandant par exemple de calculer l&amp;#39;espace de stockage à prévoir (cachée dans une question qui demande le volume des données, ce qui implique de penser au facteur de réplication) et d’imaginer en conclusion des points qui auraient pu être abordés dans l&amp;#39;étude.&lt;/p&gt;
&lt;!-- end paragraph e8e16c13-e94a-455b-863d-efbfbd52c8d3--&gt;
&lt;!-- begin heading_1 8d1e155d-5df7-46d6-a4fa-ff85fbff5553--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-8d1e155d-5df7-46d6-a4fa-ff85fbff5553&quot;&gt;Mise en situation et déroulement&lt;/h2&gt;
&lt;!-- end heading_1 8d1e155d-5df7-46d6-a4fa-ff85fbff5553--&gt;
&lt;!-- begin paragraph b9d685ba-103a-4e12-a974-5e4634b5d7ff--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b9d685ba-103a-4e12-a974-5e4634b5d7ff&quot;&gt;Dans le cadre de ce sujet, le groupe d&amp;#39;étudiants est vu comme un groupe d&amp;#39;experts ayant acquis des connaissances dans le domaine de la data ingénierie et capable de recommander une entreprise sur la mise en place d&amp;#39;une &amp;quot;plateforme data&amp;quot;.&lt;/p&gt;
&lt;!-- end paragraph b9d685ba-103a-4e12-a974-5e4634b5d7ff--&gt;
&lt;!-- begin paragraph 01b0392e-1561-4fca-8a65-7196f74d8fbb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-01b0392e-1561-4fca-8a65-7196f74d8fbb&quot;&gt;Certains ont même baptisé leur équipe. Je vous laisse juge...&lt;/p&gt;
&lt;!-- end paragraph 01b0392e-1561-4fca-8a65-7196f74d8fbb--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/13d0f8b6-efea-44ed-97ff-29232a149417/screenshot_2021-02-10_at_10.53.21.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph ef249222-dac8-4995-9f75-4b9364587ffb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ef249222-dac8-4995-9f75-4b9364587ffb&quot;&gt;Les étudiants ont eu un mois pour proposer une solution. Un mois c&amp;#39;est court, d&amp;#39;autant plus pour des étudiants sachant qu&amp;#39;avec les autres modules, ils ne sont pas à 100% de la journée dessus.&lt;/p&gt;
&lt;!-- end paragraph ef249222-dac8-4995-9f75-4b9364587ffb--&gt;
&lt;!-- begin paragraph f91325a2-d6fc-4bce-b6c5-fb47e4e1ef59--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f91325a2-d6fc-4bce-b6c5-fb47e4e1ef59&quot;&gt;Dans cet exercice, je joue tour à tour le rôle d&amp;#39;enseignant, coach et client. En tant que coach, j&amp;#39;oriente leur façon d&amp;#39;aborder le sujet et leurs recherches. Je les mets plus en situation sur la relation qu&amp;#39;ils devront entretenir avec le client. En tant qu&amp;#39;enseignant, j&amp;#39;émets des avis techniques sur leurs ébauches, je les oriente sur des solutions correspondant à leur souhait. En tant que client, j&amp;#39;apporte un jugement sur les ébauches de solution qui me sont présentés. Dans tout ça, j&amp;#39;évite d&amp;#39;imposer un point de vue purement personnel et de les enfermer dans certaines solutions. Il vaut indiquer un ensemble des technologies ou d&amp;#39;approches pouvant répondre au problème sans entrer dans les détails plutôt que de ne parler que d&amp;#39;une seule techno. C&amp;#39;est aux étudiants de faire l&amp;#39;effort de découvrir les technologies et d&amp;#39;en vérifier l&amp;#39;adéquation avec le sujet.&lt;/p&gt;
&lt;!-- end paragraph f91325a2-d6fc-4bce-b6c5-fb47e4e1ef59--&gt;
&lt;!-- begin paragraph 2dfa3784-97f7-4594-84c1-c28c0a495d8a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2dfa3784-97f7-4594-84c1-c28c0a495d8a&quot;&gt;Dans tout ça, il est nécessaire pour l&amp;#39;enseignant de suivre l&amp;#39;avancée. Ce suivi a surtout été à l&amp;#39;initiative de certains étudiants. En y voyant l&amp;#39;intérêt, j&amp;#39;ai fini par imposer des points de suivi à d&amp;#39;autres groupes.&lt;/p&gt;
&lt;!-- end paragraph 2dfa3784-97f7-4594-84c1-c28c0a495d8a--&gt;
&lt;!-- begin paragraph 3c5d79d8-310f-4f95-b00e-f71a896f17e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3c5d79d8-310f-4f95-b00e-f71a896f17e7&quot;&gt;Un aspect, qui est probablement venu un peu tard, fût de les pousser à rencontrer des professionnels pour confronter leurs solutions. L&amp;#39;avantage est évidemment certain, et ce n&amp;#39;est pas faute de les avoir encouragés :&lt;/p&gt;
&lt;!-- end paragraph 3c5d79d8-310f-4f95-b00e-f71a896f17e7--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;Ces sociétés sont suffisamment ouvertes pour comprendre qu’elles ont tout intérêt à aider des étudiants (qui pourraient être de futurs recrues ou des futures « évangélistes » de leurs solutions). Le cas échéant, faites en part dans votre rapport, ça rassurera le client. Et si certaines vous disent non, vous saurez celles qui ne méritent pas votre CV 😉&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph 489253fe-45b5-4402-9a4a-e60c7678dfd2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-489253fe-45b5-4402-9a4a-e60c7678dfd2&quot;&gt;Il est venu un peu tard dans le déroulement. Finalement, un seul groupe a utilisé cette possibilité.&lt;/p&gt;
&lt;!-- end paragraph 489253fe-45b5-4402-9a4a-e60c7678dfd2--&gt;
&lt;!-- begin paragraph b72875b2-e5f9-434a-9de1-207b1d65f642--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b72875b2-e5f9-434a-9de1-207b1d65f642&quot;&gt;La phase mise en place et déroulement se sont particulièrement bien passés et a été enrichissant pour tout le monde, moi y compris, comme je l&amp;#39;explique dans ces tweets :&lt;/p&gt;
&lt;!-- end paragraph b72875b2-e5f9-434a-9de1-207b1d65f642--&gt;&lt;div class=&quot;block-embed level-0&quot;&gt;&lt;div class=&quot;player twitter&quot;&gt;&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/fsarradin/status/1319936557206376448?s=20&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin heading_1 d6970fc1-1979-479c-b87d-101d59b9bdfa--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d6970fc1-1979-479c-b87d-101d59b9bdfa&quot;&gt;Soutenance &amp;amp; rapport&lt;/h2&gt;
&lt;!-- end heading_1 d6970fc1-1979-479c-b87d-101d59b9bdfa--&gt;
&lt;!-- begin paragraph fdfe2aa1-3bf9-40e3-8ada-70938eeda435--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fdfe2aa1-3bf9-40e3-8ada-70938eeda435&quot;&gt;La partie soutenance s&amp;#39;est faite groupe par groupe... à distance. Chaque groupe ayant 20mn de présentation et 10mn de questions. Les étudiants devaient ensuite rendre un rapport de quelques pages. J&amp;#39;étais alors accompagné par un collègue expérimenté.&lt;/p&gt;
&lt;!-- end paragraph fdfe2aa1-3bf9-40e3-8ada-70938eeda435--&gt;
&lt;!-- begin paragraph 229e7755-1fca-4c34-98b7-c342727bf969--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-229e7755-1fca-4c34-98b7-c342727bf969&quot;&gt;L’un des problèmes posés par cette approche est de pousser les groupes à la limite du bluff. Un mois de préparation pour des personnes qui ne peuvent pas consacrer 100% de leur temps à cet exercice, du fait des cours et d’autres préparations diverses à mettre en place, laisse peu de temps pour l’expérimentation. Comme le sujet implique d’imaginer l’usage de systèmes complets de stockage et de manipulation de données utilisées sur des problématiques techniquement avancés en entreprise, le défi représenté est passionnant pour les étudiants, mais nécessite en réalité une bonne expérience avec ces outils. Ils ne peuvent que se baser sur de la documentation accessible pour imaginer ce qui pourrait être mis en place.&lt;/p&gt;
&lt;!-- end paragraph 229e7755-1fca-4c34-98b7-c342727bf969--&gt;
&lt;!-- begin paragraph f477fd68-5f41-41d0-9219-57d9cdb0e093--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f477fd68-5f41-41d0-9219-57d9cdb0e093&quot;&gt;Il y aurait différents moyens de pallier ce problème :&lt;/p&gt;
&lt;!-- end paragraph f477fd68-5f41-41d0-9219-57d9cdb0e093--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Un cours insistant sur les usages attendus des différents types de systèmes de stockage et traitement de la donnée.&lt;/li&gt;&lt;li&gt;Un suivi des travaux en cours par l’enseignant avec de l’accompagnement ponctuel.&lt;/li&gt;&lt;li&gt;Forcer un peu plus les groupes à demander l’avis d’un expert technique.&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph e2edcf31-59e1-4405-b7fe-3c7e7b55317e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2edcf31-59e1-4405-b7fe-3c7e7b55317e&quot;&gt;Le point 1. est à mon sens pas très simple à mettre en place. Les possibilités d’une base de données sont relativement vastes. Il y a moyen de passer une bonne partie du cours sur les cas d&amp;#39;usage de chaque type de base.&lt;/p&gt;
&lt;!-- end paragraph e2edcf31-59e1-4405-b7fe-3c7e7b55317e--&gt;
&lt;!-- begin paragraph 404623c3-a51e-4a00-9a32-db62b6076a52--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-404623c3-a51e-4a00-9a32-db62b6076a52&quot;&gt;Le point 3. a été très partiellement suivi. Son avantage est en apparence majeur, mais comporte une potentielle difficulté. L&amp;#39;expert peut voir le sujet à sa façon (sachant que le sujet comporte des imprécisions) qui ne correspond pas à ce qui doit être compris et insister sur des technologies qui ne sont pas nécessaires (ça arrive même aux meilleurs 😄). Dans ce cadre, c&amp;#39;est au groupe de cadrer et challenger les recommandations de l&amp;#39;expert.&lt;/p&gt;
&lt;!-- end paragraph 404623c3-a51e-4a00-9a32-db62b6076a52--&gt;
&lt;!-- begin paragraph 372bf631-d126-4172-93ab-6ac8a5cc14fc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-372bf631-d126-4172-93ab-6ac8a5cc14fc&quot;&gt;Le point 2. est un point qui a été mis en place et qui a mon sens est celui qui est le plus intéressant. Il est intéressant, car il permet à la fois d’insister sur les détails du cours dans un cadre pratique et d’éviter que le groupe ne se disperse.&lt;/p&gt;
&lt;!-- end paragraph 372bf631-d126-4172-93ab-6ac8a5cc14fc--&gt;
&lt;!-- begin heading_1 5b27c67a-d662-44fb-9760-09383904b33c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5b27c67a-d662-44fb-9760-09383904b33c&quot;&gt;Notation&lt;/h2&gt;
&lt;!-- end heading_1 5b27c67a-d662-44fb-9760-09383904b33c--&gt;
&lt;!-- begin paragraph 4a1b9dcc-e7f7-448a-9a47-02381ac85083--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a1b9dcc-e7f7-448a-9a47-02381ac85083&quot;&gt;C&amp;#39;est la partie la plus compliquée. Dans un cas comme celui-là, il faut être capable de diviser le travail réalisé par les groupes en items à valider. Pour imaginer ces items, on peut commencer par imaginer les cas extrêmes et même des cas extrêmes du point de vue du potentiel client de l&amp;#39;étude.&lt;/p&gt;
&lt;!-- end paragraph 4a1b9dcc-e7f7-448a-9a47-02381ac85083--&gt;
&lt;!-- begin paragraph 05301725-bf90-45c6-b1f5-df5b56106e14--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-05301725-bf90-45c6-b1f5-df5b56106e14&quot;&gt;Le pire des cas apparaît lorsqu&amp;#39;un groupe ne montre aucun intérêt et remet des livrables excessivement bâclés ou ne remet pas de travail du tout sans justification compréhensible. C&amp;#39;est un manque de respect notoire et va créer auprès du client de la frustration, qui n&amp;#39;aura aucun souci à aller voir ailleurs. (Ce cas n&amp;#39;est pas arrivé dans les groupes que j&amp;#39;ai fréquentés) ⇒ 0-5&lt;/p&gt;
&lt;!-- end paragraph 05301725-bf90-45c6-b1f5-df5b56106e14--&gt;
&lt;!-- begin paragraph bd17fe48-0f32-4812-a914-eb4386db83b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd17fe48-0f32-4812-a914-eb4386db83b7&quot;&gt;Il y a un autre cas extrême : problème administratif ⇒ pas de note (ce n&amp;#39;est pas un 0, hein !)&lt;/p&gt;
&lt;!-- end paragraph bd17fe48-0f32-4812-a914-eb4386db83b7--&gt;
&lt;!-- begin paragraph 1fec9a64-6dc1-4f8f-bdd7-0e6cbc7c8b97--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1fec9a64-6dc1-4f8f-bdd7-0e6cbc7c8b97&quot;&gt;Le meilleur des cas : l&amp;#39;architecture répond au besoin du client, les livrables sont corrects. En se basant sur ces livrables, le client peut avec un effort moindre mettre en place sa plateforme data  ⇒ 20&lt;/p&gt;
&lt;!-- end paragraph 1fec9a64-6dc1-4f8f-bdd7-0e6cbc7c8b97--&gt;
&lt;!-- begin paragraph 6c032c75-db43-4631-b302-17d20b2b7a2f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6c032c75-db43-4631-b302-17d20b2b7a2f&quot;&gt;L&amp;#39;architecture et les livrables dépassent les attentes du client : c&amp;#39;est potentiellement une bonne idée de réaliser une proposition qui va au-delà des attentes, mais elle va se faire généralement au détriment d&amp;#39;autres aspects d&amp;#39;ordre professionnels et/ou privés, qui risquent de se traduire par  une diminution à moyen terme de la qualité de service rendu par l&amp;#39;équipe. Dans la vie professionnelle cela se traduit par le fait d&amp;#39;avoir trop travaillé sur un sujet au détriment d&amp;#39;autres sujets potentiellement plus importants, ce qui peut mettre à mal une carrière. De plus, ce surplus n&amp;#39;est généralement pas payé (du moins à court terme) ⇒ 20 &lt;/p&gt;
&lt;!-- end paragraph 6c032c75-db43-4631-b302-17d20b2b7a2f--&gt;
&lt;!-- begin paragraph c1f1a29c-6694-4625-9cce-dfcbbd97c003--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c1f1a29c-6694-4625-9cce-dfcbbd97c003&quot;&gt;Les cas vont être notés ensuite en fonction des éléments d&amp;#39;architecture en place (eg. toutes les sources sont-elles bien présentes ? Comment sont-elles ingérées par le système ? Comment est effectué le traitement ?...) et de l&amp;#39;effort que devra fournir le client pour mettre en place une plateforme data convenable selon son besoin.&lt;/p&gt;
&lt;!-- end paragraph c1f1a29c-6694-4625-9cce-dfcbbd97c003--&gt;
&lt;!-- begin paragraph 40aa4737-b4b4-45e6-ad2e-ae4802cca84e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-40aa4737-b4b4-45e6-ad2e-ae4802cca84e&quot;&gt;Le système de notation ici n&amp;#39;est pas parfait, mais il permet d&amp;#39;être relativement objectif par rapport à l&amp;#39;ensemble des travaux rendus.&lt;/p&gt;
&lt;!-- end paragraph 40aa4737-b4b4-45e6-ad2e-ae4802cca84e--&gt;
&lt;!-- begin heading_1 d8969988-383a-4172-80ec-397296809c0b--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d8969988-383a-4172-80ec-397296809c0b&quot;&gt;Amélioration possible&lt;/h2&gt;
&lt;!-- end heading_1 d8969988-383a-4172-80ec-397296809c0b--&gt;
&lt;!-- begin paragraph 7c3c7d30-a7f8-4d17-ae32-dc027f5385d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7c3c7d30-a7f8-4d17-ae32-dc027f5385d2&quot;&gt;Ce fût ma première expérience avec ce type d&amp;#39;exercice. Je l&amp;#39;ai trouvé vraiment intéressant et j&amp;#39;ai très envie de recommencer. De plus, elle s&amp;#39;adapte assez bien vis-à-vis des contraintes sanitaires rencontrées durant cette période.&lt;/p&gt;
&lt;!-- end paragraph 7c3c7d30-a7f8-4d17-ae32-dc027f5385d2--&gt;
&lt;!-- begin paragraph af6c5d68-e057-49a6-b4e3-f98e8425561a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-af6c5d68-e057-49a6-b4e3-f98e8425561a&quot;&gt;Je pense que quelques améliorations peuvent être apportées néanmoins.&lt;/p&gt;
&lt;!-- end paragraph af6c5d68-e057-49a6-b4e3-f98e8425561a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;C&amp;#39;est un détail, mais je pense que je devrais insister sur le fait que la soutenance et le rapport soient à destination du client. Dans les rapports, j&amp;#39;ai en effet trouvé des phrases du style &amp;quot;C&amp;#39;est un exercice demandé dans le cadre du cours de M. François Sarradin&amp;quot; ou &amp;quot;Nous avons beaucoup appris avec ce sujet...&amp;quot;. Ces phrases n&amp;#39;ont pas à être dans un rapport.&lt;/li&gt;&lt;li&gt;Fixer des rendez-vous de suivi de 30mn pour chaque groupe durant les sessions de TP, avec au moins.&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Un rendez-vous où le groupe aura préparé une série de questions à poser au client (questions fonctionnelles et techniques), ainsi que les critères de satisfaction.&lt;/li&gt;&lt;li&gt;Un rendez-vous pré-soutenance de présentation de l&amp;#39;architecture au client pour avoir les premiers retours.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Dans le cours, parler un peu plus des usages attendus de certains types de base de données.&lt;/li&gt;&lt;li&gt;Laisser du temps (quelques jours) pour rendre le rapport, afin que les étudiants y apportent des corrections par rapport à ce qui a été vu lors de la soutenance.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph ff0c9323-0cc6-41a2-a3dd-9f449aeb1035--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff0c9323-0cc6-41a2-a3dd-9f449aeb1035&quot;&gt;Le point 1. est à mon sens pas très simple à mettre en place. Les possibilités d’une base de données sont relativement vastes. Il y a moyen de passer une bonne partie du cours sur les cas d&amp;#39;usage de chaque type de base.&lt;/p&gt;
&lt;!-- end paragraph ff0c9323-0cc6-41a2-a3dd-9f449aeb1035--&gt;
&lt;!-- begin paragraph 19656b72-4b09-42c0-8dc2-290569c680d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-19656b72-4b09-42c0-8dc2-290569c680d8&quot;&gt;Le point 1. est à mon sens pas très simple à mettre en place. Les possibilités d’une base de données sont relativement vastes. Il y a moyen de passer une bonne partie du cours sur les cas d&amp;#39;usage de chaque type de base.&lt;/p&gt;
&lt;!-- end paragraph 19656b72-4b09-42c0-8dc2-290569c680d8--&gt;
&lt;!-- begin paragraph 8c77d307-bf64-4b33-8fcb-3ec35fb169a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c77d307-bf64-4b33-8fcb-3ec35fb169a7&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8c77d307-bf64-4b33-8fcb-3ec35fb169a7--&gt;
&lt;!-- begin paragraph 0b629124-6c85-46f8-a036-ce805d9ad405--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0b629124-6c85-46f8-a036-ce805d9ad405&quot;&gt;Photographie par &lt;a href=&quot;https://unsplash.com/@surface?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Surface&lt;/a&gt; sur &lt;a href=&quot;https://unsplash.com/s/photos/remote-work?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 0b629124-6c85-46f8-a036-ce805d9ad405--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:3c3d2462b22d471f969df9a7140d3099</id>
    <title>Unison langage</title>
    <updated>2021-01-10T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/unison-langage.html"/>
    <!--summary -->    <author>
      <name>Pierre Baille</name>
      <uri>https://univalence.io/blog/auteurs/pierre-baille.html</uri>
    </author>        <category term="Effet"></category>    <category term="Programmation fonctionnelle"></category>    <content type="html">
&lt;!-- begin paragraph 04a3a1cf-0937-4a90-894c-052dfe7065e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-04a3a1cf-0937-4a90-894c-052dfe7065e4&quot;&gt;Let&amp;#39;s talk about the &lt;a href=&quot;https://www.unisonweb.org/&quot;&gt;Unison&lt;/a&gt; language.&lt;/p&gt;
&lt;!-- end paragraph 04a3a1cf-0937-4a90-894c-052dfe7065e4--&gt;
&lt;!-- begin paragraph f564e2f8-eb96-4892-8257-a92bc0dfaf37--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f564e2f8-eb96-4892-8257-a92bc0dfaf37&quot;&gt;Unison is a statically typed langage whose main influences are &lt;a href=&quot;https://www.haskell.org/&quot;&gt;Haskell&lt;/a&gt;, &lt;a href=&quot;https://www.erlang.org/&quot;&gt;Erlang&lt;/a&gt; and &lt;a href=&quot;https://github.com/frank-lang/frank&quot;&gt;Frank&lt;/a&gt;. It is built upon a simple idea that has really appealing consequences:&lt;/p&gt;
&lt;!-- end paragraph f564e2f8-eb96-4892-8257-a92bc0dfaf37--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;&lt;b&gt;Code is content-addressed and immutable&lt;/b&gt;&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph 1fc05f3b-b613-4a22-a226-5ce4bf94b27d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1fc05f3b-b613-4a22-a226-5ce4bf94b27d&quot;&gt;In many langages, the main way to identify something is its name. In Unison, definitions are identified by a hash computed after its content. For instance, those two definitions below have different names but their body is identical. So they share the same hash.&lt;/p&gt;
&lt;!-- end paragraph 1fc05f3b-b613-4a22-a226-5ce4bf94b27d--&gt;
&lt;!-- begin code 9bde3b03-2f10-455c-8cc1-8bba73cf9b29--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9bde3b03-2f10-455c-8cc1-8bba73cf9b29&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;inc x = x + 1

add1 z = z + 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9bde3b03-2f10-455c-8cc1-8bba73cf9b29--&gt;
&lt;!-- begin paragraph f65f55ff-c18c-454a-b077-b47a3f682a23--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f65f55ff-c18c-454a-b077-b47a3f682a23&quot;&gt;For each hash, an AST is stored in the &lt;b&gt;unison &lt;/b&gt;&lt;b&gt;codebase&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph f65f55ff-c18c-454a-b077-b47a3f682a23--&gt;
&lt;!-- begin paragraph d38a2672-dc97-47d0-8281-999a559453ce--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d38a2672-dc97-47d0-8281-999a559453ce&quot;&gt;In fact, the text representation that we have seen above is not the primary representation for unison code. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;AST&lt;/a&gt; is. Text is one of many possible way to create/view/update unison code.&lt;/p&gt;
&lt;!-- end paragraph d38a2672-dc97-47d0-8281-999a559453ce--&gt;
&lt;!-- begin paragraph e2636dce-519c-4fcc-9d4b-37cfadf994fd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2636dce-519c-4fcc-9d4b-37cfadf994fd&quot;&gt;For instance, if we need our previously defined &lt;code&gt;inc&lt;/code&gt; function to define something else, its name will be substituted by its hash in the resulting AST.&lt;/p&gt;
&lt;!-- end paragraph e2636dce-519c-4fcc-9d4b-37cfadf994fd--&gt;
&lt;!-- begin code aa38159e-a616-40c0-af2d-a554bc094549--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-aa38159e-a616-40c0-af2d-a554bc094549&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;add2 x = inc (inc x)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code aa38159e-a616-40c0-af2d-a554bc094549--&gt;
&lt;!-- begin paragraph 7d821d76-c2bf-4b2d-bf22-3d6f2610d4a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7d821d76-c2bf-4b2d-bf22-3d6f2610d4a7&quot;&gt;Assuming that the corresponding hash for &lt;code&gt;inc&lt;/code&gt; is &lt;code&gt;#12345&lt;/code&gt; , the below code will be preprocessed as this:&lt;/p&gt;
&lt;!-- end paragraph 7d821d76-c2bf-4b2d-bf22-3d6f2610d4a7--&gt;
&lt;!-- begin code c2d57b7f-642f-420b-b408-de2c73c88a47--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c2d57b7f-642f-420b-b408-de2c73c88a47&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;add2 x = #12345 (#12345 x)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c2d57b7f-642f-420b-b408-de2c73c88a47--&gt;
&lt;!-- begin paragraph fef231e4-f8a0-4140-8dca-755cd0a09741--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fef231e4-f8a0-4140-8dca-755cd0a09741&quot;&gt;Then, argument names are substituted by normalized ones:&lt;/p&gt;
&lt;!-- end paragraph fef231e4-f8a0-4140-8dca-755cd0a09741--&gt;
&lt;!-- begin code 5885168d-593b-45ed-b409-05ba5d2fe0ae--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5885168d-593b-45ed-b409-05ba5d2fe0ae&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;add2 arg1 = #12345 (#12345 arg1)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5885168d-593b-45ed-b409-05ba5d2fe0ae--&gt;
&lt;!-- begin paragraph e94fb54f-fb3a-4aee-80c1-e177c944418c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e94fb54f-fb3a-4aee-80c1-e177c944418c&quot;&gt;With this hash based representation of our definition, we can compute an AST that is independant of names declared by developers.&lt;/p&gt;
&lt;!-- end paragraph e94fb54f-fb3a-4aee-80c1-e177c944418c--&gt;
&lt;!-- begin paragraph 480279c4-7119-441f-8835-58075a8bbe2d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-480279c4-7119-441f-8835-58075a8bbe2d&quot;&gt;This implies that we can change the name &lt;code&gt;inc&lt;/code&gt; for another name without changing the definition of &lt;code&gt;add2&lt;/code&gt; . &lt;/p&gt;
&lt;!-- end paragraph 480279c4-7119-441f-8835-58075a8bbe2d--&gt;
&lt;!-- begin paragraph 963eeaad-2ef6-4715-88ef-dd07b0b960aa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-963eeaad-2ef6-4715-88ef-dd07b0b960aa&quot;&gt;Also once &lt;code&gt;inc&lt;/code&gt; has been compiled, if you change its name, then is hash will not be modified. This mean that you do not need to recompile that function or other already compiled definitions that depends on it!&lt;/p&gt;
&lt;!-- end paragraph 963eeaad-2ef6-4715-88ef-dd07b0b960aa--&gt;
&lt;!-- begin heading_2 a03e7883-3965-4a8b-a7f9-e57c0d43be51--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-a03e7883-3965-4a8b-a7f9-e57c0d43be51&quot;&gt;Installation&lt;/h3&gt;
&lt;!-- end heading_2 a03e7883-3965-4a8b-a7f9-e57c0d43be51--&gt;
&lt;!-- begin paragraph 05cf68c0-b777-4fa6-a813-90f2f2f4a974--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-05cf68c0-b777-4fa6-a813-90f2f2f4a974&quot;&gt;It is much more fun to try those things on your own machine while following along this article. So I suggest to do so, knowing that Unison is really easy to install, so I invite you to do so:&lt;/p&gt;
&lt;!-- end paragraph 05cf68c0-b777-4fa6-a813-90f2f2f4a974--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;OSX (with homebrew)&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code b78274c7-f0bc-4229-b4ec-ea0e09e7e3b0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b78274c7-f0bc-4229-b4ec-ea0e09e7e3b0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew tap unisonweb/unison
brew install unison-language&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b78274c7-f0bc-4229-b4ec-ea0e09e7e3b0--&gt;
&lt;!-- begin paragraph 6c150e06-cd47-4710-9795-4b85ee487885--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6c150e06-cd47-4710-9795-4b85ee487885&quot;&gt;Once Unison is installed on your machine, you need to create a directory that unison will be launched against and cd inside it.&lt;/p&gt;
&lt;!-- end paragraph 6c150e06-cd47-4710-9795-4b85ee487885--&gt;
&lt;!-- begin code 047d6f25-ed36-44ef-b082-b07809ce7700--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-047d6f25-ed36-44ef-b082-b07809ce7700&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir Unison # create a dir to start with
cd Unison &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 047d6f25-ed36-44ef-b082-b07809ce7700--&gt;
&lt;!-- begin paragraph 8159c012-25e5-493a-9628-e5ab9c1d2790--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8159c012-25e5-493a-9628-e5ab9c1d2790&quot;&gt;then initialize unison and launch the &lt;b&gt;unison codebase manager&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph 8159c012-25e5-493a-9628-e5ab9c1d2790--&gt;
&lt;!-- begin code 3cf60056-5a42-4e37-9224-978807b5deff--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3cf60056-5a42-4e37-9224-978807b5deff&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ucm init # init the codebase
ucm # entering unison&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3cf60056-5a42-4e37-9224-978807b5deff--&gt;
&lt;!-- begin paragraph 1eded796-1bdf-48b5-810c-27ff4fd56726--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1eded796-1bdf-48b5-810c-27ff4fd56726&quot;&gt;You should see something like this:&lt;/p&gt;
&lt;!-- end paragraph 1eded796-1bdf-48b5-810c-27ff4fd56726--&gt;
&lt;!-- begin code fdcec0af-ff9a-40ed-94ca-9f3adc6b806b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fdcec0af-ff9a-40ed-94ca-9f3adc6b806b&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;   _____     _             
  |  |  |___|_|___ ___ ___ 
  |  |  |   | |_ -| . |   |
  |_____|_|_|_|___|___|_|_|
  
  Welcome to Unison!
  
  You are running version: release/M1m
  
  I&amp;#39;m currently watching for changes to .u files under ~/Code/Unison
  
  Type help to get help.

.&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fdcec0af-ff9a-40ed-94ca-9f3adc6b806b--&gt;
&lt;!-- begin paragraph bd6da4e9-a5ba-4916-8bbd-d73c8d61a264--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd6da4e9-a5ba-4916-8bbd-d73c8d61a264&quot;&gt;Once in the Unison REPL is launched, we import the base library.&lt;/p&gt;
&lt;!-- end paragraph bd6da4e9-a5ba-4916-8bbd-d73c8d61a264--&gt;
&lt;!-- begin code c3e6cfe6-2daa-403c-8f25-ee6a7ccd92e5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c3e6cfe6-2daa-403c-8f25-ee6a7ccd92e5&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;.&amp;gt; pull https://github.com/unisonweb/base:.releases._latest base&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c3e6cfe6-2daa-403c-8f25-ee6a7ccd92e5--&gt;
&lt;!-- begin paragraph 22972cc6-8805-4a69-9116-de16abe59e60--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-22972cc6-8805-4a69-9116-de16abe59e60&quot;&gt;And we are good to go!&lt;/p&gt;
&lt;!-- end paragraph 22972cc6-8805-4a69-9116-de16abe59e60--&gt;
&lt;!-- begin heading_2 65742214-be8a-47a6-8d40-3894f905b024--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-65742214-be8a-47a6-8d40-3894f905b024&quot;&gt;The unison codebase manager&lt;/h3&gt;
&lt;!-- end heading_2 65742214-be8a-47a6-8d40-3894f905b024--&gt;
&lt;!-- begin paragraph 48eb417f-3c21-4135-be9f-d24550ac3bab--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-48eb417f-3c21-4135-be9f-d24550ac3bab&quot;&gt;Once you have launched &lt;code&gt;ucm&lt;/code&gt;, any file within the current directory is watched. Let&amp;#39;s first create a file named &lt;code&gt;scratch.u&lt;/code&gt; with our &lt;code&gt;inc&lt;/code&gt; definition in it.&lt;/p&gt;
&lt;!-- end paragraph 48eb417f-3c21-4135-be9f-d24550ac3bab--&gt;
&lt;!-- begin code e3e130b7-a846-4302-9527-f79e42555017--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e3e130b7-a846-4302-9527-f79e42555017&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;inc x = x + 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e3e130b7-a846-4302-9527-f79e42555017--&gt;
&lt;!-- begin paragraph 0d4cf9c2-9661-4315-b502-09f0058e344a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0d4cf9c2-9661-4315-b502-09f0058e344a&quot;&gt;When saving this file, you can see the output of the &lt;code&gt;ucm&lt;/code&gt; process.&lt;/p&gt;
&lt;!-- end paragraph 0d4cf9c2-9661-4315-b502-09f0058e344a--&gt;
&lt;!-- begin code 79ccd85f-473b-4441-9203-8af6a0c9cc9a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-79ccd85f-473b-4441-9203-8af6a0c9cc9a&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;I found and typechecked these definitions in scratch.u. If you do an `add` or `update`, here&amp;#39;s
how your codebase would change:
  
  ⍟ These new definitions are ok to `add`:
    
    inc : Nat -&amp;gt; Nat&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 79ccd85f-473b-4441-9203-8af6a0c9cc9a--&gt;
&lt;!-- begin paragraph d2536b02-ace5-4423-9c13-f21e4c38f745--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d2536b02-ace5-4423-9c13-f21e4c38f745&quot;&gt;Indicating that our definition is valid.&lt;/p&gt;
&lt;!-- end paragraph d2536b02-ace5-4423-9c13-f21e4c38f745--&gt;
&lt;!-- begin paragraph dea50fe9-fa37-400b-adce-cad7abc0c905--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dea50fe9-fa37-400b-adce-cad7abc0c905&quot;&gt;Now, if we want to try our &lt;code&gt;inc&lt;/code&gt; function, we can add a &lt;b&gt;watch expression&lt;/b&gt;. To do so, just add the following line at the end of &lt;code&gt;scratch.u&lt;/code&gt; (you have to include the &amp;#39;&amp;gt;&amp;#39; symbol).&lt;/p&gt;
&lt;!-- end paragraph dea50fe9-fa37-400b-adce-cad7abc0c905--&gt;
&lt;!-- begin code 68ce4e63-7bee-411d-8f37-7896c457466f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-68ce4e63-7bee-411d-8f37-7896c457466f&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;&amp;gt; inc 10&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 68ce4e63-7bee-411d-8f37-7896c457466f--&gt;
&lt;!-- begin paragraph 07bc0fa9-3d6f-47c3-ae26-983ac91230ca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-07bc0fa9-3d6f-47c3-ae26-983ac91230ca&quot;&gt;On saving the file, &lt;code&gt;ucm&lt;/code&gt; will spot it and display:&lt;/p&gt;
&lt;!-- end paragraph 07bc0fa9-3d6f-47c3-ae26-983ac91230ca--&gt;
&lt;!-- begin code dd8a8116-581c-425d-8d41-f97b078271f2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dd8a8116-581c-425d-8d41-f97b078271f2&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Now evaluating any watch expressions (lines starting with `&amp;gt;`)... Ctrl+C cancels.


    3 | &amp;gt; inc 10
          ⧩
          11&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dd8a8116-581c-425d-8d41-f97b078271f2--&gt;
&lt;!-- begin paragraph 4081f2c8-2737-43dc-a6c7-eccee969b193--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4081f2c8-2737-43dc-a6c7-eccee969b193&quot;&gt;Since our &lt;code&gt;inc&lt;/code&gt; function seems to work as intended, we can add it to the codebase.&lt;/p&gt;
&lt;!-- end paragraph 4081f2c8-2737-43dc-a6c7-eccee969b193--&gt;
&lt;!-- begin paragraph 21efbae2-de96-43dc-9638-3faa5ec49bee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-21efbae2-de96-43dc-9638-3faa5ec49bee&quot;&gt;At the &lt;code&gt;ucm&lt;/code&gt; prompt just type &lt;code&gt;add&lt;/code&gt;:&lt;/p&gt;
&lt;!-- end paragraph 21efbae2-de96-43dc-9638-3faa5ec49bee--&gt;
&lt;!-- begin code eb4833bb-ccce-408d-b15b-dd97f2ba8d96--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-eb4833bb-ccce-408d-b15b-dd97f2ba8d96&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;.&amp;gt; add 
  
  ⍟ I&amp;#39;ve added these definitions:
  
    inc : Nat -&amp;gt; Nat&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code eb4833bb-ccce-408d-b15b-dd97f2ba8d96--&gt;
&lt;!-- begin paragraph e37a4323-e4ff-46af-9107-5e0fb012d697--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e37a4323-e4ff-46af-9107-5e0fb012d697&quot;&gt;Now that our &lt;code&gt;inc&lt;/code&gt; function is in the codebase, we can delete the content of &lt;code&gt;scratch.u&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph e37a4323-e4ff-46af-9107-5e0fb012d697--&gt;
&lt;!-- begin paragraph 016054d6-b1d6-42a3-994e-648152f57239--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-016054d6-b1d6-42a3-994e-648152f57239&quot;&gt;We can ask &lt;code&gt;ucm&lt;/code&gt; to find it like this:&lt;/p&gt;
&lt;!-- end paragraph 016054d6-b1d6-42a3-994e-648152f57239--&gt;
&lt;!-- begin code 44a10c71-f91d-4ae4-b93f-08a4380b9353--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-44a10c71-f91d-4ae4-b93f-08a4380b9353&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;.&amp;gt; find inc

  1. inc : Nat -&amp;gt; Nat
  2. base.Int.increment : Int -&amp;gt; Int
  3. base.List.distinct : [a] -&amp;gt; [a]
  4. base.Nat.increment : Nat -&amp;gt; Nat
  5. base.Store.modify.examples.increment : Nat&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 44a10c71-f91d-4ae4-b93f-08a4380b9353--&gt;
&lt;!-- begin paragraph d24070d4-20ea-4f9e-9645-a8c8e2020c6d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d24070d4-20ea-4f9e-9645-a8c8e2020c6d&quot;&gt;In the list of results, the first item is our function. We can ask &lt;code&gt;ucm&lt;/code&gt; to view it.   &lt;/p&gt;
&lt;!-- end paragraph d24070d4-20ea-4f9e-9645-a8c8e2020c6d--&gt;
&lt;!-- begin code cddf398c-290b-4f6b-8ba0-d578f809482b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-cddf398c-290b-4f6b-8ba0-d578f809482b&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;.&amp;gt; view 1

  inc : Nat -&amp;gt; Nat
  inc x =
    use Nat +
    x + 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code cddf398c-290b-4f6b-8ba0-d578f809482b--&gt;
&lt;!-- begin paragraph 2944e489-8dd6-44cb-8735-f78516f73e2e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2944e489-8dd6-44cb-8735-f78516f73e2e&quot;&gt;This is it! Enhanced with its infered type declaration and import statement (+ function from module Nat).&lt;/p&gt;
&lt;!-- end paragraph 2944e489-8dd6-44cb-8735-f78516f73e2e--&gt;
&lt;!-- begin paragraph 72deb9fb-a00c-4b0d-a285-8ab17401af52--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-72deb9fb-a00c-4b0d-a285-8ab17401af52&quot;&gt;The &lt;code&gt;find&lt;/code&gt; operation is handful to explore the current codebase. For instance, we can search for all definitions of a given type:&lt;/p&gt;
&lt;!-- end paragraph 72deb9fb-a00c-4b0d-a285-8ab17401af52--&gt;
&lt;!-- begin code a3ff352f-7ed6-48e3-a0c7-38ce6771dc03--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a3ff352f-7ed6-48e3-a0c7-38ce6771dc03&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;.&amp;gt; find : Nat -&amp;gt; Nat

  1.  base.Nat.complement : Nat -&amp;gt; Nat
  2.  base.Nat.increment : Nat -&amp;gt; Nat
  3.  base.Nat.leadingZeros : Nat -&amp;gt; Nat
  4.  base.Nat.trailingZeros : Nat -&amp;gt; Nat
  5.  base.Nat.decrement : Nat -&amp;gt; Nat&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a3ff352f-7ed6-48e3-a0c7-38ce6771dc03--&gt;
&lt;!-- begin heading_3 153c5f37-936c-4a30-8b0b-295b8e8e29fb--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-153c5f37-936c-4a30-8b0b-295b8e8e29fb&quot;&gt;Changing names&lt;/h4&gt;
&lt;!-- end heading_3 153c5f37-936c-4a30-8b0b-295b8e8e29fb--&gt;
&lt;!-- begin paragraph 5f0f5b4f-d048-4c7a-9ca6-f9d13e46cc57--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f0f5b4f-d048-4c7a-9ca6-f9d13e46cc57&quot;&gt;Let&amp;#39;s define another useful function: &lt;/p&gt;
&lt;!-- end paragraph 5f0f5b4f-d048-4c7a-9ca6-f9d13e46cc57--&gt;
&lt;!-- begin paragraph 6653b413-b935-48a8-a480-76da4c6ad1e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6653b413-b935-48a8-a480-76da4c6ad1e0&quot;&gt;&lt;code&gt;scratch.u&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 6653b413-b935-48a8-a480-76da4c6ad1e0--&gt;
&lt;!-- begin code d3f8ff1a-4434-45f9-adf2-1d2193e057c7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d3f8ff1a-4434-45f9-adf2-1d2193e057c7&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;add2 x = inc (inc x) &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d3f8ff1a-4434-45f9-adf2-1d2193e057c7--&gt;
&lt;!-- begin paragraph 57f9bc9d-7bc5-433e-9295-271639678f50--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-57f9bc9d-7bc5-433e-9295-271639678f50&quot;&gt;Once &lt;code&gt;add2&lt;/code&gt; is added to our codebase (with the &lt;code&gt;add&lt;/code&gt; command), we can retrieve its definition with the &lt;code&gt;view&lt;/code&gt; command&lt;/p&gt;
&lt;!-- end paragraph 57f9bc9d-7bc5-433e-9295-271639678f50--&gt;
&lt;!-- begin code 54e2a330-f886-47b3-8a65-d0e8c5ab6e95--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-54e2a330-f886-47b3-8a65-d0e8c5ab6e95&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; view add2

  add2 : Nat -&amp;gt; Nat 
  add2 x = inc (inc x)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 54e2a330-f886-47b3-8a65-d0e8c5ab6e95--&gt;
&lt;!-- begin paragraph 785d1193-304a-4f31-b4c2-a15445746d93--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-785d1193-304a-4f31-b4c2-a15445746d93&quot;&gt;We suddenly decide that we want to change the name of the &lt;code&gt;inc&lt;/code&gt; function to &lt;code&gt;add1&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 785d1193-304a-4f31-b4c2-a15445746d93--&gt;
&lt;!-- begin paragraph 8e43e3b4-d71f-4704-9af9-4640f340bc95--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e43e3b4-d71f-4704-9af9-4640f340bc95&quot;&gt;In any other langage we would had to navigate to the &lt;code&gt;inc&lt;/code&gt; definition, change its name, then change all its usages accordingly, but in unison we only have to run this simple &lt;code&gt;ucm&lt;/code&gt; command:&lt;/p&gt;
&lt;!-- end paragraph 8e43e3b4-d71f-4704-9af9-4640f340bc95--&gt;
&lt;!-- begin code c65e863c-da49-4ac7-8c9d-f1ea569445e3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c65e863c-da49-4ac7-8c9d-f1ea569445e3&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; move.term inc add1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c65e863c-da49-4ac7-8c9d-f1ea569445e3--&gt;
&lt;!-- begin paragraph 42157507-30bb-4612-b1e1-310513d14e25--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42157507-30bb-4612-b1e1-310513d14e25&quot;&gt;As we can see, the &lt;code&gt;add2&lt;/code&gt; definition has been correctly updated:&lt;/p&gt;
&lt;!-- end paragraph 42157507-30bb-4612-b1e1-310513d14e25--&gt;
&lt;!-- begin code fb4c2021-a093-4e04-a27a-5c76fd799faa--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fb4c2021-a093-4e04-a27a-5c76fd799faa&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; view add2

  add2 : Nat -&amp;gt; Nat 
  add2 x = add1 (add1 x)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fb4c2021-a093-4e04-a27a-5c76fd799faa--&gt;
&lt;!-- begin paragraph 8d2cd752-7405-4731-b3ef-d173c314ba5f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d2cd752-7405-4731-b3ef-d173c314ba5f&quot;&gt;No recompilation is needed neither, it just works!&lt;/p&gt;
&lt;!-- end paragraph 8d2cd752-7405-4731-b3ef-d173c314ba5f--&gt;
&lt;!-- begin heading_3 ea2bfc26-cbd1-4143-b685-92e21f01b5bd--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-ea2bfc26-cbd1-4143-b685-92e21f01b5bd&quot;&gt;Navigating the history&lt;/h4&gt;
&lt;!-- end heading_3 ea2bfc26-cbd1-4143-b685-92e21f01b5bd--&gt;
&lt;!-- begin paragraph 5ecda825-d462-4519-866d-1d54a1f4d0a4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ecda825-d462-4519-866d-1d54a1f4d0a4&quot;&gt;With immutability comes great benefits, as functional programmers often says.&lt;/p&gt;
&lt;!-- end paragraph 5ecda825-d462-4519-866d-1d54a1f4d0a4--&gt;
&lt;!-- begin paragraph fe2d30ba-351e-4d9e-b120-7cf4c86134c8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fe2d30ba-351e-4d9e-b120-7cf4c86134c8&quot;&gt;Since our codebase is always a well-typed immutable value, we have time travel for free.&lt;/p&gt;
&lt;!-- end paragraph fe2d30ba-351e-4d9e-b120-7cf4c86134c8--&gt;
&lt;!-- begin code bf9a825a-5ce5-482b-ab8c-93f8afaa95ba--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bf9a825a-5ce5-482b-ab8c-93f8afaa95ba&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;.&amp;gt; reflog

  Here is a log of the root namespace hashes, starting with the most recent, along with the command
  that got us there. Try:
  
    `fork 2 .old`             
    `fork #fpkooer5hq .old`   to make an old namespace accessible again,
                              
    `reset-root #fpkooer5hq`  to reset the root namespace and its history to that of the specified
                              namespace.
  
  1.  #fiviinv92c : move.term ..inc .add1
  2.  #1mlujphnkc : add
  3.  #cb6lu9ahad : add
  4.  #lqs21dco5d : pull https://github.com/unisonweb/base:.releases._latest .base
  5.  #7asfbtqmoj : (initial reflogged namespace)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bf9a825a-5ce5-482b-ab8c-93f8afaa95ba--&gt;
&lt;!-- begin heading_2 cc04983d-84d3-4f95-ad51-27b22815c1ef--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-cc04983d-84d3-4f95-ad51-27b22815c1ef&quot;&gt;Documentation &lt;/h3&gt;
&lt;!-- end heading_2 cc04983d-84d3-4f95-ad51-27b22815c1ef--&gt;
&lt;!-- begin paragraph 6cfc5169-3221-492c-b152-3de7e80e1997--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6cfc5169-3221-492c-b152-3de7e80e1997&quot;&gt;in Unison documentation is first class, it has type &lt;code&gt;Doc&lt;/code&gt;  and a dedicated literal.&lt;/p&gt;
&lt;!-- end paragraph 6cfc5169-3221-492c-b152-3de7e80e1997--&gt;
&lt;!-- begin paragraph 792af07f-165f-4ced-8674-97db0a7c548f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-792af07f-165f-4ced-8674-97db0a7c548f&quot;&gt;As an example, let&amp;#39;s write documentation for our &lt;code&gt;inc&lt;/code&gt; function:&lt;/p&gt;
&lt;!-- end paragraph 792af07f-165f-4ced-8674-97db0a7c548f--&gt;
&lt;!-- begin code 26f6a46a-ed6e-4318-992f-1d3d086055bb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-26f6a46a-ed6e-4318-992f-1d3d086055bb&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;inc.docs = [:
  `@inc n` returns `n + 1`

  the source for @inc is
  @[source] inc

  the result of evaluating `(@inc 1)` is @[evaluate] inc.ex1

  its signature is:
  `@[signature] inc`

  :]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 26f6a46a-ed6e-4318-992f-1d3d086055bb--&gt;
&lt;!-- begin paragraph e2e60d2d-1c01-4225-8f69-021c6232c75d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2e60d2d-1c01-4225-8f69-021c6232c75d&quot;&gt;You can then display this docs in ucm:&lt;/p&gt;
&lt;!-- end paragraph e2e60d2d-1c01-4225-8f69-021c6232c75d--&gt;
&lt;!-- begin code 154c3897-a498-43d7-81fc-77fbe57d2aeb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-154c3897-a498-43d7-81fc-77fbe57d2aeb&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; display inc.docs

  `inc n` returns `n + 1`
  
  the source for inc is
  inc x =
    use Nat +
    x + 1
  
  the result of evaluating `(inc 1)` is 2
  
  its signature is: `inc : Nat -&amp;gt; Nat`&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 154c3897-a498-43d7-81fc-77fbe57d2aeb--&gt;
&lt;!-- begin paragraph 0141733d-732b-447e-a06e-f654abc23156--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0141733d-732b-447e-a06e-f654abc23156&quot;&gt;To get more details on documentation inner syntax please see &lt;a href=&quot;https://www.unisonweb.org/docs/documentation&quot;&gt;this page&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 0141733d-732b-447e-a06e-f654abc23156--&gt;
&lt;!-- begin paragraph 247e85e9-99ce-4f5e-8575-2119281e5805--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-247e85e9-99ce-4f5e-8575-2119281e5805&quot;&gt;One neat thing to keep in mind is that the names you use in your docs have the same behaviour in case of renaming. For instance if we rename &lt;code&gt;inc&lt;/code&gt; to &lt;code&gt;add1&lt;/code&gt; with: &lt;/p&gt;
&lt;!-- end paragraph 247e85e9-99ce-4f5e-8575-2119281e5805--&gt;
&lt;!-- begin code 6bd45626-2e87-4858-874b-da8f2822ebcd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6bd45626-2e87-4858-874b-da8f2822ebcd&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; move.term inc add1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6bd45626-2e87-4858-874b-da8f2822ebcd--&gt;
&lt;!-- begin paragraph 07ad8f84-56c8-493b-b417-6945d7c1b4a5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-07ad8f84-56c8-493b-b417-6945d7c1b4a5&quot;&gt;and then display the doc again:&lt;/p&gt;
&lt;!-- end paragraph 07ad8f84-56c8-493b-b417-6945d7c1b4a5--&gt;
&lt;!-- begin code 5c56ee4e-b136-4cc1-9b92-6a7fda65e1b2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5c56ee4e-b136-4cc1-9b92-6a7fda65e1b2&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; display inc.docs

  `add1 n` returns `n + 1`
  
  the source for add1 is
  add1 x =
    use Nat +
    x + 1
  
  the result of evaluating `(add1 1)` is 2
  
  its signature is: `add1 : Nat -&amp;gt; Nat`&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5c56ee4e-b136-4cc1-9b92-6a7fda65e1b2--&gt;
&lt;!-- begin paragraph 2e202bef-943e-40e2-98d0-f5b2fd3abf64--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2e202bef-943e-40e2-98d0-f5b2fd3abf64&quot;&gt;Every dynamic value (the one starting by &lt;code&gt;@&lt;/code&gt;) are updated accordingly.&lt;/p&gt;
&lt;!-- end paragraph 2e202bef-943e-40e2-98d0-f5b2fd3abf64--&gt;
&lt;!-- begin heading_2 ccb983ea-b5aa-4bb2-91ad-a04f7c288629--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ccb983ea-b5aa-4bb2-91ad-a04f7c288629&quot;&gt;Abilities&lt;/h3&gt;
&lt;!-- end heading_2 ccb983ea-b5aa-4bb2-91ad-a04f7c288629--&gt;
&lt;!-- begin paragraph 5f450535-623e-4a18-a30a-f07f01fb627b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f450535-623e-4a18-a30a-f07f01fb627b&quot;&gt;&lt;i&gt;Abilities&lt;/i&gt; lets you use the same ordinary Unison syntax for programs that do (asynchronous) I/O, stream processing, exception handling, parsing, distributed computation, and lots more...&lt;/p&gt;
&lt;!-- end paragraph 5f450535-623e-4a18-a30a-f07f01fb627b--&gt;
&lt;!-- begin heading_3 9bc8ca9f-6ddc-4f91-b769-b4a23960a303--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-9bc8ca9f-6ddc-4f91-b769-b4a23960a303&quot;&gt;Hello world!&lt;/h4&gt;
&lt;!-- end heading_3 9bc8ca9f-6ddc-4f91-b769-b4a23960a303--&gt;
&lt;!-- begin paragraph 3e7ccdf1-d8f6-4a41-8f71-839d216c51da--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3e7ccdf1-d8f6-4a41-8f71-839d216c51da&quot;&gt;scratch.u:&lt;/p&gt;
&lt;!-- end paragraph 3e7ccdf1-d8f6-4a41-8f71-839d216c51da--&gt;
&lt;!-- begin code 0bbcf801-3b33-4570-b541-b661863c0d9b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0bbcf801-3b33-4570-b541-b661863c0d9b&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;greet name = printLine (&amp;quot;Hello &amp;quot; ++ name ++ &amp;quot;!&amp;quot;)

program = &amp;#39;(greet &amp;quot;world&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0bbcf801-3b33-4570-b541-b661863c0d9b--&gt;
&lt;!-- begin paragraph 0a2e75ee-4d86-4ff1-8294-d641ce00c182--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0a2e75ee-4d86-4ff1-8294-d641ce00c182&quot;&gt;ucm:&lt;/p&gt;
&lt;!-- end paragraph 0a2e75ee-4d86-4ff1-8294-d641ce00c182--&gt;
&lt;!-- begin code 89c0a83e-3475-42ba-b67e-1e730900e643--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-89c0a83e-3475-42ba-b67e-1e730900e643&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; run program

Hello world!&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 89c0a83e-3475-42ba-b67e-1e730900e643--&gt;
&lt;!-- begin paragraph 09a45794-bc73-4eb3-8b3b-3b5f10c1d28c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-09a45794-bc73-4eb3-8b3b-3b5f10c1d28c&quot;&gt;When saving our scratch.u file, we can see the type of the greet function:&lt;/p&gt;
&lt;!-- end paragraph 09a45794-bc73-4eb3-8b3b-3b5f10c1d28c--&gt;
&lt;!-- begin code e21e92fe-6431-4bc5-acba-37df656eba8b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e21e92fe-6431-4bc5-acba-37df656eba8b&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;greet : Text -&amp;gt; {IO} ()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e21e92fe-6431-4bc5-acba-37df656eba8b--&gt;
&lt;!-- begin paragraph 5da26f82-131b-4116-b37d-b9be01e9f0ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5da26f82-131b-4116-b37d-b9be01e9f0ae&quot;&gt;which means that the greet function takes a &lt;code&gt;Text&lt;/code&gt; as argument, needs the &lt;code&gt;IO&lt;/code&gt; ability and return nothing (&lt;code&gt;()&lt;/code&gt; or &lt;code&gt;Unit&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph 5da26f82-131b-4116-b37d-b9be01e9f0ae--&gt;
&lt;!-- begin paragraph 4ce4756d-daa6-471f-9dce-40b6e1030219--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4ce4756d-daa6-471f-9dce-40b6e1030219&quot;&gt;The functions that we have seen up to this point are pure (&lt;code&gt;inc&lt;/code&gt; and &lt;code&gt;add2&lt;/code&gt;). If &lt;code&gt;ucm&lt;/code&gt; display their type as &lt;code&gt;Nat → Nat&lt;/code&gt;, their real type is &lt;code&gt;Nat → {} Nat&lt;/code&gt;, indicating that no &lt;b&gt;abilities&lt;/b&gt; were needed.&lt;/p&gt;
&lt;!-- end paragraph 4ce4756d-daa6-471f-9dce-40b6e1030219--&gt;
&lt;!-- begin paragraph 00651190-4dc0-47b8-b40a-7c1a1ed8222a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-00651190-4dc0-47b8-b40a-7c1a1ed8222a&quot;&gt;A function from type &lt;code&gt;a&lt;/code&gt; to type &lt;code&gt;b&lt;/code&gt; that needs several abilities &lt;code&gt;A1&lt;/code&gt;, &lt;code&gt;A2&lt;/code&gt; and &lt;code&gt;A3&lt;/code&gt; will have the type like &lt;code&gt;a → {A1,A2,A3} b&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 00651190-4dc0-47b8-b40a-7c1a1ed8222a--&gt;
&lt;!-- begin heading_3 4fd6a9d0-bb72-4b5b-9416-01e4806061ce--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-4fd6a9d0-bb72-4b5b-9416-01e4806061ce&quot;&gt;Motivation&lt;/h4&gt;
&lt;!-- end heading_3 4fd6a9d0-bb72-4b5b-9416-01e4806061ce--&gt;
&lt;!-- begin paragraph ff6616f7-4816-4016-9703-3801791609f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff6616f7-4816-4016-9703-3801791609f6&quot;&gt;There are three main motivations for abilities in Unison:&lt;/p&gt;
&lt;!-- end paragraph ff6616f7-4816-4016-9703-3801791609f6--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Make side effects visible in the type signature.&lt;/li&gt;&lt;li&gt;Decouple interface from implementation.&lt;/li&gt;&lt;li&gt;Customize control flow.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_3 6dfe8233-f633-4fbb-b165-ccbc830567e9--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-6dfe8233-f633-4fbb-b165-ccbc830567e9&quot;&gt;Lazyness&lt;/h4&gt;
&lt;!-- end heading_3 6dfe8233-f633-4fbb-b165-ccbc830567e9--&gt;
&lt;!-- begin paragraph 78253ebf-2a75-4bdd-adef-221e93021b10--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78253ebf-2a75-4bdd-adef-221e93021b10&quot;&gt;As you may have noticed in the &lt;i&gt;hello world&lt;/i&gt; example, the &lt;code&gt;&amp;#39;(greet &amp;quot;world&amp;quot;)&lt;/code&gt; expression was prefixed by a single quote. This is syntax sugar for &lt;code&gt;(_ → (greet &amp;quot;world&amp;quot;))&lt;/code&gt; which denotes a function taking &lt;code&gt;()&lt;/code&gt; as argument and returning the result of evaluating &lt;code&gt;(greet &amp;quot;world&amp;quot;)&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 78253ebf-2a75-4bdd-adef-221e93021b10--&gt;
&lt;!-- begin paragraph 18bfa0f7-90f8-4801-aade-d58fce7f547f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-18bfa0f7-90f8-4801-aade-d58fce7f547f&quot;&gt;&lt;code&gt;scratch.u&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 18bfa0f7-90f8-4801-aade-d58fce7f547f--&gt;
&lt;!-- begin code 64d419f5-782e-48d4-99e2-2ac236d5a9ce--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-64d419f5-782e-48d4-99e2-2ac236d5a9ce&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;expr = &amp;#39;(1 + 1)
-- is equivalent to 
expr = (_ -&amp;gt; (1 + 1))

-- and can be resumed/evaluated like this 
&amp;gt; expr ()
-- or with syntax sugar 
&amp;gt; !expr&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 64d419f5-782e-48d4-99e2-2ac236d5a9ce--&gt;
&lt;!-- begin paragraph dfcd8a7f-15eb-4043-94d7-b071367287e3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dfcd8a7f-15eb-4043-94d7-b071367287e3&quot;&gt;This kind of quoting is often used in conjunction of abilities, as we have seen with the hello world example. &lt;/p&gt;
&lt;!-- end paragraph dfcd8a7f-15eb-4043-94d7-b071367287e3--&gt;
&lt;!-- begin paragraph 69674d35-4ad1-4753-9a54-2a8f8e4a597b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69674d35-4ad1-4753-9a54-2a8f8e4a597b&quot;&gt;This is because only functions can use abilities:&lt;/p&gt;
&lt;!-- end paragraph 69674d35-4ad1-4753-9a54-2a8f8e4a597b--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;Unison is a &amp;#39;purely functional&amp;#39; language. That means that evaluation of terms cannot in itself be effectful. Having to add the &lt;code&gt;&amp;#39;&lt;/code&gt; delay to effectfully-computed values is a consequence of that. Any effectful code needs to pull its punches — it&amp;#39;s not causing effects to happen during evaluation, but rather it&amp;#39;s evaluating to a function which can then explain to a handler what effects it would like to be performed. We&amp;#39;ll see how that handler in turn can only either (a) turn the requested effects into pure computations, ready for evaluation, or (b) pass the buck, by translating them to requests in another ability, yielding another function. If there are abilities like network or disk I/O, then the buck gets passed all the way to the very outside of the program, and into the Unison runtime system, using the &lt;code&gt;IO&lt;/code&gt; ability. At no point does a Unison term&amp;#39;s evaluation ever directly generate an effect.&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin heading_2 7e1c4d40-7338-4064-8b65-2e7133442731--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-7e1c4d40-7338-4064-8b65-2e7133442731&quot;&gt;the IO ability&lt;/h3&gt;
&lt;!-- end heading_2 7e1c4d40-7338-4064-8b65-2e7133442731--&gt;
&lt;!-- begin paragraph 0473607d-3e47-42c7-bbc9-4aa09598852e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0473607d-3e47-42c7-bbc9-4aa09598852e&quot;&gt;&lt;code&gt;&lt;b&gt;IO&lt;/b&gt;&lt;/code&gt;&lt;b&gt; is a special ability, built in to Unison&lt;/b&gt;. It is through &lt;code&gt;IO&lt;/code&gt; that &lt;b&gt;effectful&lt;/b&gt; Unison programs can actually interact with the outside world — writing to the console, reading from files and sockets, and any other behavior that goes beyond simply evaluating Unison expressions to a result. When you run your program (for example using ucm&amp;#39;s &lt;code&gt;run&lt;/code&gt; command), operations from the &lt;code&gt;IO&lt;/code&gt; ability are handled by the Unison runtime system.&lt;/p&gt;
&lt;!-- end paragraph 0473607d-3e47-42c7-bbc9-4aa09598852e--&gt;
&lt;!-- begin paragraph c28bc4d7-88ec-4325-b695-fb8e3ee318ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c28bc4d7-88ec-4325-b695-fb8e3ee318ae&quot;&gt;Asking &lt;code&gt;ucm&lt;/code&gt; about &lt;code&gt;IO&lt;/code&gt;:&lt;/p&gt;
&lt;!-- end paragraph c28bc4d7-88ec-4325-b695-fb8e3ee318ae--&gt;
&lt;!-- begin code b499735e-8244-4667-96cd-05d50807a447--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b499735e-8244-4667-96cd-05d50807a447&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;.&amp;gt; view IO

  ability base.io.IO where
    getFileSize_ : FilePath -&amp;gt;{base.io.IO} Either Error Nat
    kill_ : ThreadId -&amp;gt;{base.io.IO} Either Error ()
    send_ : Socket -&amp;gt; Bytes -&amp;gt;{base.io.IO} Either Error ()
    bracket_ :
      &amp;#39;{base.io.IO} a -&amp;gt; (a -&amp;gt;{base.io.IO} b) -&amp;gt; (a -&amp;gt;{base.io.IO} c) -&amp;gt;{base.io.IO} Either Error c
    getLine_ : Handle -&amp;gt;{base.io.IO} Either Error Text
    getText_ : Handle -&amp;gt;{base.io.IO} Either Error Text
    getFileTimestamp_ : FilePath -&amp;gt;{base.io.IO} Either Error EpochTime
    closeFile_ : Handle -&amp;gt;{base.io.IO} Either Error ()
    getTemporaryDirectory_ : {base.io.IO} (Either Error FilePath)
    getCurrentDirectory_ : {base.io.IO} (Either Error FilePath)
    renameDirectory_ : FilePath -&amp;gt; FilePath -&amp;gt;{base.io.IO} Either Error ()
    renameFile_ : FilePath -&amp;gt; FilePath -&amp;gt;{base.io.IO} Either Error ()
    receive_ : Socket -&amp;gt; Nat -&amp;gt;{base.io.IO} Either Error (Optional Bytes)
    fileExists_ : FilePath -&amp;gt;{base.io.IO} Either Error Boolean
    isDirectory_ : FilePath -&amp;gt;{base.io.IO} Either Error Boolean
    directoryContents_ : FilePath -&amp;gt;{base.io.IO} Either Error [FilePath]
    listen_ : Socket -&amp;gt;{base.io.IO} Either Error ()
    closeSocket_ : Socket -&amp;gt;{base.io.IO} Either Error ()
    clientSocket_ : HostName -&amp;gt; ServiceName -&amp;gt;{base.io.IO} Either Error Socket
    delay_ : Nat -&amp;gt;{base.io.IO} Either Error ()
    seek_ : Handle -&amp;gt; SeekMode -&amp;gt; Int -&amp;gt;{base.io.IO} Either Error ()
    serverSocket_ : Optional HostName -&amp;gt; ServiceName -&amp;gt;{base.io.IO} Either Error Socket
    accept_ : Socket -&amp;gt;{base.io.IO} Either Error Socket
    setBuffering_ : Handle -&amp;gt; Optional BufferMode -&amp;gt;{base.io.IO} Either Error ()
    openFile_ : FilePath -&amp;gt; Mode -&amp;gt;{base.io.IO} Either Error Handle
    throw : Error -&amp;gt;{base.io.IO} a
    fork_ : &amp;#39;{base.io.IO} a -&amp;gt;{base.io.IO} Either Error ThreadId
    getBuffering_ : Handle -&amp;gt;{base.io.IO} Either Error (Optional BufferMode)
    position_ : Handle -&amp;gt;{base.io.IO} Either Error Int
    setCurrentDirectory_ : FilePath -&amp;gt;{base.io.IO} Either Error ()
    createDirectory_ : FilePath -&amp;gt;{base.io.IO} Either Error ()
    removeDirectory_ : FilePath -&amp;gt;{base.io.IO} Either Error ()
    removeFile_ : FilePath -&amp;gt;{base.io.IO} Either Error ()
    systemTime_ : {base.io.IO} (Either Error EpochTime)
    isFileEOF_ : Handle -&amp;gt;{base.io.IO} Either Error Boolean
    isFileOpen_ : Handle -&amp;gt;{base.io.IO} Either Error Boolean
    isSeekable_ : Handle -&amp;gt;{base.io.IO} Either Error Boolean
    putText_ : Handle -&amp;gt; Text -&amp;gt;{base.io.IO} Either Error ()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b499735e-8244-4667-96cd-05d50807a447--&gt;
&lt;!-- begin heading_2 b1aba586-f4a6-4c03-85c3-dd75075033ea--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b1aba586-f4a6-4c03-85c3-dd75075033ea&quot;&gt;Other abilities&lt;/h3&gt;
&lt;!-- end heading_2 b1aba586-f4a6-4c03-85c3-dd75075033ea--&gt;
&lt;!-- begin paragraph cb6fddcb-7bf1-4277-acbe-030153805110--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cb6fddcb-7bf1-4277-acbe-030153805110&quot;&gt;Abilities are declared in an abstract way like this:&lt;/p&gt;
&lt;!-- end paragraph cb6fddcb-7bf1-4277-acbe-030153805110--&gt;
&lt;!-- begin code f6d8f8ea-6ee3-49b7-b58e-ed4105d4f762--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f6d8f8ea-6ee3-49b7-b58e-ed4105d4f762&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;ability Store v where
  get : v
  put : v -&amp;gt; ()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f6d8f8ea-6ee3-49b7-b58e-ed4105d4f762--&gt;
&lt;!-- begin paragraph e450577c-c865-4544-a656-e8018f05de8e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e450577c-c865-4544-a656-e8018f05de8e&quot;&gt;This declaration introduces a &lt;code&gt;Store&lt;/code&gt; &lt;b&gt;ability&lt;/b&gt; which contains 2 &lt;b&gt;actions&lt;/b&gt;: &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;put&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph e450577c-c865-4544-a656-e8018f05de8e--&gt;
&lt;!-- begin paragraph 8d0cdcf7-a5f1-4323-b216-6163ef0b39d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d0cdcf7-a5f1-4323-b216-6163ef0b39d1&quot;&gt;Unison has no builtin mutable state mechanism. So this ability is introducing one.&lt;/p&gt;
&lt;!-- end paragraph 8d0cdcf7-a5f1-4323-b216-6163ef0b39d1--&gt;
&lt;!-- begin paragraph 80e8d019-7a1a-4efa-ae3c-257a50dbb315--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-80e8d019-7a1a-4efa-ae3c-257a50dbb315&quot;&gt;This declaration does not specify any implementation (it is abstract). One nice thing about this is that we can plug any implementation we need (depending on the use-case) without modifying the code that is using those actions.&lt;/p&gt;
&lt;!-- end paragraph 80e8d019-7a1a-4efa-ae3c-257a50dbb315--&gt;
&lt;!-- begin paragraph c8a594b8-05fa-488f-a6cd-7060bba1ffee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c8a594b8-05fa-488f-a6cd-7060bba1ffee&quot;&gt;In order to be able to use this ability, we have to &lt;b&gt;handle&lt;/b&gt; it. In Unison this is done by an &lt;b&gt;Ability handler.&lt;/b&gt;&lt;/p&gt;
&lt;!-- end paragraph c8a594b8-05fa-488f-a6cd-7060bba1ffee--&gt;
&lt;!-- begin paragraph fa7b97a6-d081-44df-bf3b-92250f140d3d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa7b97a6-d081-44df-bf3b-92250f140d3d&quot;&gt;If &lt;code&gt;e&lt;/code&gt; is an expression containing &lt;b&gt;action calls&lt;/b&gt; and &lt;code&gt;h&lt;/code&gt; is an &lt;b&gt;ability handler&lt;/b&gt; then &lt;code&gt;handle e with h&lt;/code&gt; is the way to get the result of evaluating &lt;code&gt;e&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph fa7b97a6-d081-44df-bf3b-92250f140d3d--&gt;
&lt;!-- begin paragraph 1c035e78-c11a-4e91-b2b9-b2052ace9291--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1c035e78-c11a-4e91-b2b9-b2052ace9291&quot;&gt;An &lt;b&gt;ability handler&lt;/b&gt; is a function that states how to handle each &lt;b&gt;action&lt;/b&gt; we want to use in the handled code (&lt;code&gt;e&lt;/code&gt;). A &lt;b&gt;handler&lt;/b&gt; for the &lt;code&gt;Store&lt;/code&gt; ability will have to take care of &lt;code&gt;Store.get&lt;/code&gt; and &lt;code&gt;Store.put&lt;/code&gt; individually. But we will delay this and start with a simpler ability. &lt;/p&gt;
&lt;!-- end paragraph 1c035e78-c11a-4e91-b2b9-b2052ace9291--&gt;
&lt;!-- begin heading_3 ffcafa41-627a-40e5-9ec1-cfa3892fbc9f--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-ffcafa41-627a-40e5-9ec1-cfa3892fbc9f&quot;&gt;Abort&lt;/h4&gt;
&lt;!-- end heading_3 ffcafa41-627a-40e5-9ec1-cfa3892fbc9f--&gt;
&lt;!-- begin paragraph b0844c7d-a11f-4c0d-ab24-0625aaddb970--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b0844c7d-a11f-4c0d-ab24-0625aaddb970&quot;&gt;One of the most basic ability one can imagine is the &lt;code&gt;Abort&lt;/code&gt; ability which is able to &lt;code&gt;cut&lt;/code&gt; the execution of a program.&lt;/p&gt;
&lt;!-- end paragraph b0844c7d-a11f-4c0d-ab24-0625aaddb970--&gt;
&lt;!-- begin code b2465dd3-7b22-47f2-92ce-811b39fadb19--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b2465dd3-7b22-47f2-92ce-811b39fadb19&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;ability Abort where 
  cut : a&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b2465dd3-7b22-47f2-92ce-811b39fadb19--&gt;
&lt;!-- begin paragraph c35f42a5-3fa6-43c2-b2e5-faa42e9d7869--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c35f42a5-3fa6-43c2-b2e5-faa42e9d7869&quot;&gt;Here is a &lt;b&gt;handler &lt;/b&gt;for it:&lt;/p&gt;
&lt;!-- end paragraph c35f42a5-3fa6-43c2-b2e5-faa42e9d7869--&gt;
&lt;!-- begin code 99f60de2-69c4-4472-9014-dda5be2451e5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-99f60de2-69c4-4472-9014-dda5be2451e5&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;abortHandler returnedValue = cases 
  -- handling the cut action
  {Abort.cut -&amp;gt; continuation} -&amp;gt; returnedValue
  -- Exit case
  {a} -&amp;gt; a&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 99f60de2-69c4-4472-9014-dda5be2451e5--&gt;
&lt;!-- begin paragraph aaa43765-5cc5-4f89-9538-cc25ad50360d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aaa43765-5cc5-4f89-9538-cc25ad50360d&quot;&gt;There is 2 branches in our cases statement, the first one is handling our &lt;code&gt;cut&lt;/code&gt; action by ignoring the continuation and immediately returning &lt;code&gt;returnedValue&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph aaa43765-5cc5-4f89-9538-cc25ad50360d--&gt;
&lt;!-- begin paragraph c75b73cf-98ba-41d4-8b34-067996707b1e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c75b73cf-98ba-41d4-8b34-067996707b1e&quot;&gt;The first branch starts with the &lt;b&gt;action pattern&lt;/b&gt; &lt;code&gt;{Abort.cut → continuation}&lt;/code&gt;  that matches our only action &lt;code&gt;Abort.cut&lt;/code&gt; and specifies what to do when this action is encountered.&lt;b&gt; &lt;/b&gt;the &lt;code&gt;continuation&lt;/code&gt; binding is holding the rest of the program. Any &lt;b&gt;action pattern&lt;/b&gt; can do many different things with it: ignore it, call it, call it several times, record it... In this case, we are simply ignoring it and immediately returning &lt;code&gt;returnedValue&lt;/code&gt; (the only argument of our handler).&lt;/p&gt;
&lt;!-- end paragraph c75b73cf-98ba-41d4-8b34-067996707b1e--&gt;
&lt;!-- begin paragraph 737f6eb2-8fc4-4ffb-b340-7fd62f5bd777--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-737f6eb2-8fc4-4ffb-b340-7fd62f5bd777&quot;&gt;The second branch &lt;code&gt;{a} → a&lt;/code&gt; is the &lt;b&gt;exit branch&lt;/b&gt;, &lt;b&gt;every ability handler have to have one&lt;/b&gt;. It is taken when all actions calls have been handled.&lt;/p&gt;
&lt;!-- end paragraph 737f6eb2-8fc4-4ffb-b340-7fd62f5bd777--&gt;
&lt;!-- begin paragraph 0f55e7fd-9258-4faf-a72c-b4edc27d6442--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0f55e7fd-9258-4faf-a72c-b4edc27d6442&quot;&gt; Let&amp;#39;s try it by adding this watch expression:&lt;/p&gt;
&lt;!-- end paragraph 0f55e7fd-9258-4faf-a72c-b4edc27d6442--&gt;
&lt;!-- begin code d9242310-60fc-4bef-bf13-ee56ec89e689--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d9242310-60fc-4bef-bf13-ee56ec89e689&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;&amp;gt; handle 
    x = 4
    Abort.cut
    x + 2 
  with 
    abortHandler 0&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d9242310-60fc-4bef-bf13-ee56ec89e689--&gt;
&lt;!-- begin paragraph f693797c-8d02-4b07-bd6b-cc7c84fd343f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f693797c-8d02-4b07-bd6b-cc7c84fd343f&quot;&gt;it evaluates to &lt;code&gt;0&lt;/code&gt; as intended (our &lt;code&gt;abortHandler&lt;/code&gt; was initiated with &lt;code&gt;0&lt;/code&gt;). If we remove the &lt;code&gt;Abort.cut&lt;/code&gt; line it evaluates to &lt;code&gt;6&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph f693797c-8d02-4b07-bd6b-cc7c84fd343f--&gt;
&lt;!-- begin paragraph f59025f3-5170-4109-a073-772ba32c3e1f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f59025f3-5170-4109-a073-772ba32c3e1f&quot;&gt;So what has happened here?&lt;/p&gt;
&lt;!-- end paragraph f59025f3-5170-4109-a073-772ba32c3e1f--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;The first expression was evaluated normally, &lt;code&gt;x&lt;/code&gt; was bound to &lt;code&gt;4&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Then we hit an &lt;b&gt;action&lt;/b&gt; that has to be handled, so we defer the control of the execution to our &lt;b&gt;ability handler&lt;/b&gt; giving him access to the &lt;b&gt;continuation&lt;/b&gt; of the program, the first branch of our handler is taken, the continuation is ignored and the handler first argument (&lt;code&gt;0&lt;/code&gt; in this case) is returned.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;x + 2&lt;/code&gt; expression is never computed.&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin heading_3 f7531e76-e6c5-4ace-977a-febe703da2fb--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-f7531e76-e6c5-4ace-977a-febe703da2fb&quot;&gt;Store again&lt;/h4&gt;
&lt;!-- end heading_3 f7531e76-e6c5-4ace-977a-febe703da2fb--&gt;
&lt;!-- begin paragraph c3dbd31a-4438-4ad1-9f51-d66116e45e3e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c3dbd31a-4438-4ad1-9f51-d66116e45e3e&quot;&gt;Let&amp;#39;s take a look at a more interesting handler.&lt;/p&gt;
&lt;!-- end paragraph c3dbd31a-4438-4ad1-9f51-d66116e45e3e--&gt;
&lt;!-- begin paragraph dc566cb0-f9d8-44cf-8489-33879dd64ea1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dc566cb0-f9d8-44cf-8489-33879dd64ea1&quot;&gt;A handler for the &lt;code&gt;Store&lt;/code&gt; ability may look like this&lt;/p&gt;
&lt;!-- end paragraph dc566cb0-f9d8-44cf-8489-33879dd64ea1--&gt;
&lt;!-- begin code f82ecec3-a8e0-4620-a94c-b9c921008730--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f82ecec3-a8e0-4620-a94c-b9c921008730&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;storeHandler storedValue = cases
  -- handling the get action
  {Store.get -&amp;gt; cont} -&amp;gt;
    handle cont storedValue with storeHandler storedValue
  -- handling the put action 
  {Store.put v -&amp;gt; cont} -&amp;gt;
    handle cont () with storeHandler v
  -- the exit case
  {a} -&amp;gt; a&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f82ecec3-a8e0-4620-a94c-b9c921008730--&gt;
&lt;!-- begin paragraph 526aadc5-5f4f-4964-add7-c2f0d1520d3d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-526aadc5-5f4f-4964-add7-c2f0d1520d3d&quot;&gt;In general an &lt;b&gt;action pattern &lt;/b&gt;has the following structure:&lt;/p&gt;
&lt;!-- end paragraph 526aadc5-5f4f-4964-add7-c2f0d1520d3d--&gt;
&lt;!-- begin code 2171ee7c-29fa-4489-b29b-7bb493072ddf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2171ee7c-29fa-4489-b29b-7bb493072ddf&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;{A.c p_1 p_2 p_n -&amp;gt; k}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2171ee7c-29fa-4489-b29b-7bb493072ddf--&gt;
&lt;!-- begin paragraph d02dd409-9dd0-44fa-b344-ee95c1141085--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d02dd409-9dd0-44fa-b344-ee95c1141085&quot;&gt;Where &lt;code&gt;A&lt;/code&gt; is the name of the ability, &lt;code&gt;c&lt;/code&gt; is the name of the action, &lt;code&gt;p_1&lt;/code&gt; through &lt;code&gt;p_n&lt;/code&gt; are patterns matching the arguments to the action, and &lt;code&gt;k&lt;/code&gt; is a continuation for the program.&lt;/p&gt;
&lt;!-- end paragraph d02dd409-9dd0-44fa-b344-ee95c1141085--&gt;
&lt;!-- begin paragraph c0bec92a-c0aa-4ca3-884b-f1f54cade44e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c0bec92a-c0aa-4ca3-884b-f1f54cade44e&quot;&gt;Here, our handler has 3 cases, 2 for our &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;put&lt;/code&gt; actions plus the &lt;b&gt;exit case&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph c0bec92a-c0aa-4ca3-884b-f1f54cade44e--&gt;
&lt;!-- begin paragraph a7d05301-4706-4fa5-80c4-f558580b7230--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a7d05301-4706-4fa5-80c4-f558580b7230&quot;&gt;The 1st and 2nd branches are using the handler &lt;b&gt;recursively&lt;/b&gt; to handle the evaluation of the rest of the computation.&lt;/p&gt;
&lt;!-- end paragraph a7d05301-4706-4fa5-80c4-f558580b7230--&gt;
&lt;!-- begin paragraph ba67f779-ee9c-4083-a2be-b754cb77bfaa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba67f779-ee9c-4083-a2be-b754cb77bfaa&quot;&gt;The handler is initiated with an argument (&lt;code&gt;storedValue&lt;/code&gt; ) that we will use to keep some piece of state along handling, &lt;b&gt;this is a very common pattern&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph ba67f779-ee9c-4083-a2be-b754cb77bfaa--&gt;
&lt;!-- begin paragraph 92c71150-efae-4348-96f4-e6e913c26da0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-92c71150-efae-4348-96f4-e6e913c26da0&quot;&gt;One really dummy example that uses the &lt;code&gt;Store&lt;/code&gt; ability: &lt;/p&gt;
&lt;!-- end paragraph 92c71150-efae-4348-96f4-e6e913c26da0--&gt;
&lt;!-- begin code 605bec13-c949-4807-a8bf-952ede7a2d9f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-605bec13-c949-4807-a8bf-952ede7a2d9f&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;-- an helper function to update the value of our store
storeUpdate f =
  Store.put (f Store.get)

-- our dummy function that uses a mutable state 
-- in order to count the number of zeros present in a list
countZeros = cases
  -- non empty list case
  head +: tail -&amp;gt;
    -- first we check if the head is zero and incrementing our store value if so
    if (head == 0) then storeUpdate inc else ()
    -- then we recur with the tail of the list
    countZeros tail
  -- empty list case, we are using Store.get to obtain our stored value and return it
  [] -&amp;gt; Store.get

-- we are initiating the storeHandler with the value 0 
-- and handling the countZeros call with it
&amp;gt; handle countZeros [1,0,3,0,1,0]  
  with storeHandler 0 
-- it returns: 3&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 605bec13-c949-4807-a8bf-952ede7a2d9f--&gt;
&lt;!-- begin paragraph 53a0e0af-b24d-4f81-9e86-38a70da4cea7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-53a0e0af-b24d-4f81-9e86-38a70da4cea7&quot;&gt; Let see what happens on the evaluation of this &lt;b&gt;watch expression&lt;/b&gt;:&lt;/p&gt;
&lt;!-- end paragraph 53a0e0af-b24d-4f81-9e86-38a70da4cea7--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;storeHandler 0&lt;/code&gt; is returning a handler function that will be used to handle the &lt;code&gt;countZeros [1,0,3,0,1,0]&lt;/code&gt; expression.&lt;/li&gt;&lt;li&gt; &lt;code&gt;[1,0,3,0,1,0]&lt;/code&gt; is matching the first branch of the &lt;code&gt;countZeros&lt;/code&gt; function, we are binding &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;[0,3,0,1,0]&lt;/code&gt; to &lt;code&gt;tail&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;We test if &lt;code&gt;head&lt;/code&gt; equals &lt;code&gt;0&lt;/code&gt;: this is not the case so the line evaluate to &lt;code&gt;()&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;On the next line, we are recurring with &lt;code&gt;tail&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;We still match the first branch, binding &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;[3,0,1,0]&lt;/code&gt; to &lt;code&gt;tail&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;This time &lt;code&gt;head&lt;/code&gt; equals &lt;code&gt;0&lt;/code&gt;. So we call &lt;code&gt;storeUpdate&lt;/code&gt; with the &lt;code&gt;inc&lt;/code&gt; function.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;storeUpdate&lt;/code&gt; function is calling &lt;code&gt;Store.get&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Store.get&lt;/code&gt; is an action. So the control is passed to our handler.&lt;/li&gt;&lt;li&gt;The first branch of &lt;code&gt;storeHandler&lt;/code&gt; is matching the &lt;code&gt;Store.get&lt;/code&gt; action.&lt;/li&gt;&lt;li&gt;The continuation of the program has to be called with the value that the action is meant to return, here a &lt;code&gt;Nat&lt;/code&gt;, that is the currently stored value (the argument passed to &lt;code&gt;storeHandler&lt;/code&gt; initially: &lt;code&gt;0&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;Since the rest of the computation may contains other &lt;b&gt;actions&lt;/b&gt; we still have to handle, we use the same handler to do so (wrapping it around our continuation call with an &lt;b&gt;handle&lt;/b&gt; expression).&lt;/li&gt;&lt;li&gt;Now we are back in the &lt;code&gt;storeUpdate&lt;/code&gt; function, the value that we got with &lt;code&gt;Store.get&lt;/code&gt; (&lt;code&gt;0&lt;/code&gt;) is incremented by &lt;code&gt;inc&lt;/code&gt; and the &lt;code&gt;Store.put&lt;/code&gt; action is called with this new value (&lt;code&gt;1&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;Again, we are entering our handler, this time matching the second branch.&lt;/li&gt;&lt;li&gt;Like the first branch, we will resume the computation, this time passing &lt;code&gt;()&lt;/code&gt; to the continuation since the &lt;code&gt;Store.put&lt;/code&gt; is meant to return a value of type &lt;code&gt;Unit&lt;/code&gt; (&lt;code&gt;()&lt;/code&gt;). This time we will slightly change the handler to capture the new value that we want to store (&lt;code&gt;1&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;And it continues, incrementing our store each time a &lt;code&gt;0&lt;/code&gt; is encountered until the list is empty.&lt;/li&gt;&lt;li&gt;When the list is empty, the second branch of &lt;code&gt;countZeros&lt;/code&gt; is taken and we call &lt;code&gt;Store.get&lt;/code&gt; a last time to return the result (in this particular case: &lt;code&gt;3&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:f550d353d7ac4e7fb02bb0ad549c0c33</id>
    <title>Comment démarrer en Scala en 2020 ?</title>
    <updated>2020-10-26T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/comment-demarrer-en-scala-en-2020.html"/>
    <!--summary -->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="Scala"></category>    <content type="html">
&lt;!-- begin paragraph a5fc475a-5a03-4166-a840-568a0454ad86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a5fc475a-5a03-4166-a840-568a0454ad86&quot;&gt;En ce moment, on fait quelques enseignements dans le Big Data et les systèmes distribués, notamment auprès des écoles d&amp;#39;ingénieurs. On nous pose parfois la question : &amp;quot;comment on démarre avec Scala ? ... gratuitement* ?&amp;quot;&lt;/p&gt;
&lt;!-- end paragraph a5fc475a-5a03-4166-a840-568a0454ad86--&gt;
&lt;!-- begin paragraph c1e9ca50-3bd6-4eff-b84b-5dc6648bfde2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c1e9ca50-3bd6-4eff-b84b-5dc6648bfde2&quot;&gt;&lt;i&gt;(*si vous avez un budget on peut &amp;quot;passer&amp;quot; pour accompagner sur Scala 2, Scala 3, le FP, ZIO ... 😉 c&amp;#39;est notre métier.)&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph c1e9ca50-3bd6-4eff-b84b-5dc6648bfde2--&gt;
&lt;!-- begin paragraph fcda6413-d71a-4c85-acb6-8e949a3d0903--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fcda6413-d71a-4c85-acb6-8e949a3d0903&quot;&gt;Il y a beaucoup de ressources sur Scala. Cela ne manque pas. Ce qui va être clé, c&amp;#39;est de s&amp;#39;exposer aux concepts clés du langage et de faire des exercices.&lt;/p&gt;
&lt;!-- end paragraph fcda6413-d71a-4c85-acb6-8e949a3d0903--&gt;
&lt;!-- begin paragraph 42c755db-7810-4186-9209-e1cbbc905f4a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42c755db-7810-4186-9209-e1cbbc905f4a&quot;&gt;Voici quelques liens :&lt;/p&gt;
&lt;!-- end paragraph 42c755db-7810-4186-9209-e1cbbc905f4a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1DpdpBXhe-18RlsdY49AuI7ojpl_awkYIp9BYdZJD0io/edit?usp=sharing&quot;&gt;La présentation de François réalisée pour des ateliers rapides (Ramp up)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.scala-exercises.org/std_lib/asserts&quot;&gt;Scala Exercices, bibliothèque standard&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.handsonscala.com/table-of-contents.html&quot;&gt;Les premiers chapitres de Hands-On-Scala&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://twitter.github.io/scala_school/&quot;&gt;Scala School&lt;/a&gt; + &lt;a href=&quot;http://twitter.github.io/effectivescala/&quot;&gt;Effective Scala&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f9f04259-b307-49f7-94b2-b42750f4d5f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9f04259-b307-49f7-94b2-b42750f4d5f3&quot;&gt;Ce qui va être important c&amp;#39;est de pouvoir tester les concepts directement. Donc il va falloir installer le REPL Scala, voire &lt;a href=&quot;https://www.jetbrains.com/idea/download/&quot;&gt;IntelliJ IDEA&lt;/a&gt; et créer un projet pour tester le code.&lt;/p&gt;
&lt;!-- end paragraph f9f04259-b307-49f7-94b2-b42750f4d5f3--&gt;
&lt;!-- begin paragraph 9a04a314-6420-45ca-88ef-2abc2fdd022f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9a04a314-6420-45ca-88ef-2abc2fdd022f&quot;&gt;Cela se passe mieux avec du soutien. Pour ça, je recommande la communauté Scala dans la langue de Molière, que l&amp;#39;on va trouver sur &lt;a href=&quot;https://gitter.im/scala/fr&quot;&gt;gitter/scala/fr&lt;/a&gt;. Il ne faut pas hésiter, il n&amp;#39;y a pas de mauvaises questions !&lt;/p&gt;
&lt;!-- end paragraph 9a04a314-6420-45ca-88ef-2abc2fdd022f--&gt;
&lt;!-- begin paragraph a2b8140e-3e61-4262-b4cf-45b15f770504--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a2b8140e-3e61-4262-b4cf-45b15f770504&quot;&gt;Pour continuer, il y a les ouvrages suivants (payants) : &lt;/p&gt;
&lt;!-- end paragraph a2b8140e-3e61-4262-b4cf-45b15f770504--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://www.amazon.fr/Programming-Scala-3rd-Martin-Odersky/dp/0981531687&quot;&gt;Programming in Scala, 3rd Edition&lt;/a&gt; : bon livre pour débuter en Scala, incluant la patience de Martin Odersky, le père du langage.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.handsonscala.com/table-of-contents.html&quot;&gt;Hands-On-Scala&lt;/a&gt; (les autres chapitres) : approche plus pratique sur Scala proposée par Li Haoyi (Databricks, Mill, Ammonite, FastParse).&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alvinalexander.com/scala/functional-programming-simplified-book/&quot;&gt;Functional Programming, Simplified&lt;/a&gt; (in Scala) : un pavé proposé par Alvin Alexander proposant une des meilleures vulgarisations sur la programmation fonctionnelle en Scala que nous connaissons.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph a129c591-def5-432c-884e-07c8f7d9a1fa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a129c591-def5-432c-884e-07c8f7d9a1fa&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a129c591-def5-432c-884e-07c8f7d9a1fa--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:5e0d7d5c47854232a025a5a0ad8317fc</id>
    <title>Crux Database</title>
    <updated>2020-09-15T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/crux-database.html"/>
    <!--summary -->    <author>
      <name>Pierre Baille</name>
      <uri>https://univalence.io/blog/auteurs/pierre-baille.html</uri>
    </author>        <category term="clojure"></category>    <category term="Database"></category>    <content type="html">
&lt;!-- begin heading_1 a141975a-9f7e-45a5-8a74-2fa5eff0f880--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a141975a-9f7e-45a5-8a74-2fa5eff0f880&quot;&gt;Crux&lt;/h2&gt;
&lt;!-- end heading_1 a141975a-9f7e-45a5-8a74-2fa5eff0f880--&gt;
&lt;!-- begin paragraph 44f48392-4323-4615-b7af-d8de5a1ed0a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-44f48392-4323-4615-b7af-d8de5a1ed0a0&quot;&gt;Crux is a database developed by &lt;a href=&quot;https://juxt.pro/&quot;&gt;&lt;b&gt;Juxt&lt;/b&gt;&lt;/a&gt;. It has been available as a public alpha since April 2019.&lt;/p&gt;
&lt;!-- end paragraph 44f48392-4323-4615-b7af-d8de5a1ed0a0--&gt;
&lt;!-- begin paragraph 60fbd74f-3f28-423f-9303-5327cc2a7ee8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-60fbd74f-3f28-423f-9303-5327cc2a7ee8&quot;&gt;At first glance, it looks a bit like an open sourced &lt;a href=&quot;https://www.datomic.com/&quot;&gt;Datomic&lt;/a&gt;, but without schemas and with a slightly different temporal model.&lt;/p&gt;
&lt;!-- end paragraph 60fbd74f-3f28-423f-9303-5327cc2a7ee8--&gt;
&lt;!-- begin heading_2 807df4f1-f798-43e2-9c2a-5f887ec3420c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-807df4f1-f798-43e2-9c2a-5f887ec3420c&quot;&gt;Bitemporality&lt;/h3&gt;
&lt;!-- end heading_2 807df4f1-f798-43e2-9c2a-5f887ec3420c--&gt;
&lt;!-- begin paragraph 5e5f1f57-0d83-4547-8fb6-edc4b2035127--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e5f1f57-0d83-4547-8fb6-edc4b2035127&quot;&gt;While Datomic is indexing datums along a single time axis based on &lt;b&gt;transaction-time&lt;/b&gt; (the point in time where data was transacted into the database), Crux uses a bitemporal approach, indexing datums along two axis:&lt;/p&gt;
&lt;!-- end paragraph 5e5f1f57-0d83-4547-8fb6-edc4b2035127--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;transaction time&lt;/li&gt;&lt;li&gt;valid time&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph c9de593f-6da0-4b34-bfbd-6e216adde664--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c9de593f-6da0-4b34-bfbd-6e216adde664&quot;&gt;This extra time axis (valid-time) let the user populate the DB with past and future information regardless of the order in which the information arrives, and make corrections to past recordings to build an ever-improving temporal model of a given domain.&lt;/p&gt;
&lt;!-- end paragraph c9de593f-6da0-4b34-bfbd-6e216adde664--&gt;
&lt;!-- begin paragraph 2bffe82a-bfd9-49fc-a1aa-a878ff8337f7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2bffe82a-bfd9-49fc-a1aa-a878ff8337f7&quot;&gt;This kind of modeling takes into account the fact that our understanding of the past grows along the way. We do not know the exact state of the domain at each moment.&lt;/p&gt;
&lt;!-- end paragraph 2bffe82a-bfd9-49fc-a1aa-a878ff8337f7--&gt;
&lt;!-- begin paragraph 70652ea0-ee62-4040-892c-431314d73a7e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-70652ea0-ee62-4040-892c-431314d73a7e&quot;&gt;As an example, we can think of a criminal investigation&lt;/p&gt;
&lt;!-- end paragraph 70652ea0-ee62-4040-892c-431314d73a7e--&gt;
&lt;!-- begin paragraph 78f2d1d9-f61c-445c-8574-9b6e5ea0fcdc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78f2d1d9-f61c-445c-8574-9b6e5ea0fcdc&quot;&gt;&lt;a href=&quot;https://opencrux.com/docs#bitemp%E2%B8%BBcrime&quot;&gt;Crux - Open Time Store&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 78f2d1d9-f61c-445c-8574-9b6e5ea0fcdc--&gt;
&lt;!-- begin heading_2 6096a80f-9886-49bf-a95c-b66bee199dc6--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6096a80f-9886-49bf-a95c-b66bee199dc6&quot;&gt;Schemaless&lt;/h3&gt;
&lt;!-- end heading_2 6096a80f-9886-49bf-a95c-b66bee199dc6--&gt;
&lt;!-- begin paragraph cf4796d1-d4ae-42c2-b861-65a61f718297--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf4796d1-d4ae-42c2-b861-65a61f718297&quot;&gt;Crux does not enforce any schema for the documents it stores. One reason for this is that data might come from many different places, and may not ultimately be owned by the service using Crux to query the data. This design enables schema-on-write and/or schema-on-read to be achieved outside of the core of Crux, to meet the exact application requirements.&lt;/p&gt;
&lt;!-- end paragraph cf4796d1-d4ae-42c2-b861-65a61f718297--&gt;
&lt;!-- begin paragraph ffaa7e7d-32da-43cd-b3b0-93be89105637--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ffaa7e7d-32da-43cd-b3b0-93be89105637&quot;&gt;The only requirement for a crux document is to have a &lt;code&gt;:crux.db/id&lt;/code&gt; key&lt;/p&gt;
&lt;!-- end paragraph ffaa7e7d-32da-43cd-b3b0-93be89105637--&gt;
&lt;!-- begin heading_2 51752f7c-7cc8-4443-a4e8-2e2abfeb53fd--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-51752f7c-7cc8-4443-a4e8-2e2abfeb53fd&quot;&gt;Datalog queries&lt;/h3&gt;
&lt;!-- end heading_2 51752f7c-7cc8-4443-a4e8-2e2abfeb53fd--&gt;
&lt;!-- begin paragraph 6f4d670f-290d-4a30-977f-127b307b58c5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6f4d670f-290d-4a30-977f-127b307b58c5&quot;&gt;Like Datomic, Crux uses datalog as a query language. In both systems Datalog queries are represented as &lt;a href=&quot;https://docs.xtdb.com/tutorials/essential-edn/&quot;&gt;EDN&lt;/a&gt; datastructures, but are not totally compatible.&lt;/p&gt;
&lt;!-- end paragraph 6f4d670f-290d-4a30-977f-127b307b58c5--&gt;
&lt;!-- begin paragraph 1dd59971-222b-4712-99ce-1a767043edd9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1dd59971-222b-4712-99ce-1a767043edd9&quot;&gt;Datalog is a non turing-complete subset of prolog&lt;/p&gt;
&lt;!-- end paragraph 1dd59971-222b-4712-99ce-1a767043edd9--&gt;
&lt;!-- begin paragraph 3621fc58-5c8a-42ed-bfce-60fa0ddd1fad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3621fc58-5c8a-42ed-bfce-60fa0ddd1fad&quot;&gt;Unlike in Prolog,&lt;/p&gt;
&lt;!-- end paragraph 3621fc58-5c8a-42ed-bfce-60fa0ddd1fad--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;statements of a Datalog program can be &lt;b&gt;stated in any order&lt;/b&gt;.&lt;/li&gt;&lt;li&gt;Datalog queries on finite sets &lt;b&gt;are guaranteed to terminate&lt;/b&gt;&lt;/li&gt;&lt;li&gt;Datalog disallows complex terms as arguments of predicates, e.g., &lt;code&gt;p (1, 2)&lt;/code&gt; is admissible but not &lt;code&gt;p(f (1), 2)&lt;/code&gt;. It also is more restrictive about negation and recursion usage. &lt;a href=&quot;https://en.wikipedia.org/wiki/Datalog&quot;&gt;(wiki)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 ca11bc99-83b0-4f0f-8eff-48a082cf4152--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ca11bc99-83b0-4f0f-8eff-48a082cf4152&quot;&gt;Setup&lt;/h3&gt;
&lt;!-- end heading_2 ca11bc99-83b0-4f0f-8eff-48a082cf4152--&gt;
&lt;!-- begin paragraph cf9fb317-5e95-42a1-943d-5d362df5beb2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf9fb317-5e95-42a1-943d-5d362df5beb2&quot;&gt;To begin to play with Crux you only have to clone &lt;a href=&quot;https://github.com/pbaille/crux-starter/blob/master/README.md&quot;&gt;this project&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph cf9fb317-5e95-42a1-943d-5d362df5beb2--&gt;
&lt;!-- begin code 7279fac8-e283-40b1-a18d-73456e742112--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7279fac8-e283-40b1-a18d-73456e742112&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;git clone git@github.com:pbaille/crux-starter.git
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7279fac8-e283-40b1-a18d-73456e742112--&gt;
&lt;!-- begin paragraph d3e27649-00e5-47c1-ad1b-22abdd15fbc5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3e27649-00e5-47c1-ad1b-22abdd15fbc5&quot;&gt;If you are not familiar with Clojure you will find some instructions to setup an IDE in the &lt;a href=&quot;https://github.com/pbaille/crux%E2%B8%BBstarter/blob/master/README.md&quot;&gt;readme file&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph d3e27649-00e5-47c1-ad1b-22abdd15fbc5--&gt;
&lt;!-- begin heading_2 61b4cdb8-5a48-4816-83e1-98d425e5de34--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-61b4cdb8-5a48-4816-83e1-98d425e5de34&quot;&gt;Transactions&lt;/h3&gt;
&lt;!-- end heading_2 61b4cdb8-5a48-4816-83e1-98d425e5de34--&gt;
&lt;!-- begin code 192b7268-daa0-4938-be87-4266ed995954--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-192b7268-daa0-4938-be87-4266ed995954&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(ns crux-starter.p01_transactions
  (:require [crux.api :as crux]
            [crux-starter.p00_setup :refer [node]]))
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 192b7268-daa0-4938-be87-4266ed995954--&gt;
&lt;!-- begin heading_2 3e31cf29-50cd-4615-9a2f-f5afaaef474d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3e31cf29-50cd-4615-9a2f-f5afaaef474d&quot;&gt;Putting data into the database&lt;/h3&gt;
&lt;!-- end heading_2 3e31cf29-50cd-4615-9a2f-f5afaaef474d--&gt;
&lt;!-- begin paragraph 33645fa5-2483-44d5-b53d-67b966e927e2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-33645fa5-2483-44d5-b53d-67b966e927e2&quot;&gt;Crux valid documents are arbitrary nested edn maps.The only requirement is the presence of a &lt;code&gt;:crux.db/id&lt;/code&gt; key pointing to either a keyword or a map.&lt;/p&gt;
&lt;!-- end paragraph 33645fa5-2483-44d5-b53d-67b966e927e2--&gt;
&lt;!-- begin paragraph baaccd42-84c2-4e1d-99f0-2e28ed655e3d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-baaccd42-84c2-4e1d-99f0-2e28ed655e3d&quot;&gt;Let&amp;#39;s say that we have a clojure map that fulfill this requirement.&lt;/p&gt;
&lt;!-- end paragraph baaccd42-84c2-4e1d-99f0-2e28ed655e3d--&gt;
&lt;!-- begin code 0c0422d5-d944-47c7-8110-36d9008bcbda--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0c0422d5-d944-47c7-8110-36d9008bcbda&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(def data1 {:crux.db/id :data1
            :myfield &amp;quot;mydata&amp;quot;})
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0c0422d5-d944-47c7-8110-36d9008bcbda--&gt;
&lt;!-- begin paragraph 497d27f5-926c-4e7d-b7a5-ed3e7d1f440e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-497d27f5-926c-4e7d-b7a5-ed3e7d1f440e&quot;&gt;We can transact it to the database like this:&lt;/p&gt;
&lt;!-- end paragraph 497d27f5-926c-4e7d-b7a5-ed3e7d1f440e--&gt;
&lt;!-- begin code 6c50ac91-4587-491c-9327-e9b653b779ac--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6c50ac91-4587-491c-9327-e9b653b779ac&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/put data1]])
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6c50ac91-4587-491c-9327-e9b653b779ac--&gt;
&lt;!-- begin paragraph eddead12-7d97-49e3-bcb0-9c2940643d1d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eddead12-7d97-49e3-bcb0-9c2940643d1d&quot;&gt;The simplest way to retrieve it is to use &lt;code&gt;crux/entity&lt;/code&gt;:&lt;/p&gt;
&lt;!-- end paragraph eddead12-7d97-49e3-bcb0-9c2940643d1d--&gt;
&lt;!-- begin code 54fa0bef-ab76-4bd5-9b44-aec8d164d2ca--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-54fa0bef-ab76-4bd5-9b44-aec8d164d2ca&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/entity (crux/db node) :data1)
;;=&amp;gt; {:crux.db/id :data1, :myfield &amp;quot;mydata&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 54fa0bef-ab76-4bd5-9b44-aec8d164d2ca--&gt;
&lt;!-- begin paragraph 71f56713-8440-4e10-828a-e9b5ad425316--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71f56713-8440-4e10-828a-e9b5ad425316&quot;&gt;The &lt;code&gt;crux/db&lt;/code&gt; call is returning the current value of our database. If we are interested in retrieving its value at a given time, we can feed it a second argument.&lt;/p&gt;
&lt;!-- end paragraph 71f56713-8440-4e10-828a-e9b5ad425316--&gt;
&lt;!-- begin code 3c9901b1-a07c-47d3-b1f2-f3bda2e3b2f1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3c9901b1-a07c-47d3-b1f2-f3bda2e3b2f1&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/db node #inst &amp;quot;2000&amp;quot;) ;; returns the value of the database as in the beginning of the year 2000
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3c9901b1-a07c-47d3-b1f2-f3bda2e3b2f1--&gt;
&lt;!-- begin paragraph c9746fda-5f64-4d6f-90fe-f26979eff609--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c9746fda-5f64-4d6f-90fe-f26979eff609&quot;&gt;As we can check, our previously trasacted &lt;code&gt;:data1&lt;/code&gt; document does not yet exists in 2000.&lt;/p&gt;
&lt;!-- end paragraph c9746fda-5f64-4d6f-90fe-f26979eff609--&gt;
&lt;!-- begin code f067a91a-dab2-4f83-a755-4bbb5b3b95bf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f067a91a-dab2-4f83-a755-4bbb5b3b95bf&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/entity (crux/db node #inst &amp;quot;2000&amp;quot;) :data1) ;;=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f067a91a-dab2-4f83-a755-4bbb5b3b95bf--&gt;
&lt;!-- begin paragraph 6cf704ee-8393-4a27-aa00-a2384d5f4bf7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6cf704ee-8393-4a27-aa00-a2384d5f4bf7&quot;&gt;&lt;code&gt;crux/submit-tx&lt;/code&gt; can take several transactions.&lt;/p&gt;
&lt;!-- end paragraph 6cf704ee-8393-4a27-aa00-a2384d5f4bf7--&gt;
&lt;!-- begin code 323b988e-c8fe-4d96-9d59-d9ee728527b3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-323b988e-c8fe-4d96-9d59-d9ee728527b3&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/put {:crux.db/id :data2 :foo {:arbitrary {:nested &amp;quot;map&amp;quot;}}}]
                 [:crux.tx/put {:crux.db/id :data3 :data 3}]])
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 323b988e-c8fe-4d96-9d59-d9ee728527b3--&gt;
&lt;!-- begin paragraph 7ab29b7e-d0f7-46e3-849d-4c64b28d0d99--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7ab29b7e-d0f7-46e3-849d-4c64b28d0d99&quot;&gt;The &lt;code&gt;:crux.tx/put&lt;/code&gt; operation is letting you specify the valid time frame of the given document.&lt;/p&gt;
&lt;!-- end paragraph 7ab29b7e-d0f7-46e3-849d-4c64b28d0d99--&gt;
&lt;!-- begin code 60d04e75-fcc1-4e81-895b-b4217d1fb2d5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-60d04e75-fcc1-4e81-895b-b4217d1fb2d5&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [;; a document that is valid forever starting at the beginning of the year 2019
                 [:crux.tx/put {:crux.db/id :timed1 :value 10}
                  #inst &amp;quot;2019&amp;quot;]

                 ;; a document that is valid between 2017 and 2018
                 [:crux.tx/put {:crux.db/id :timed2 :value 10}
                  #inst &amp;quot;2017&amp;quot;
                  #inst &amp;quot;2018&amp;quot;]])

;; :timed1 is not yet valid in 2000
(crux/entity (crux/db node #inst &amp;quot;2000&amp;quot;) :timed1)
;;=&amp;gt; nil

;; but is in 2019
(crux/entity (crux/db node #inst &amp;quot;2019&amp;quot;) :timed1)
;;=&amp;gt; {:crux.db/id :timed1, :value 10}

;; timed2 not yet valid in 2000
(crux/entity (crux/db node #inst &amp;quot;2019&amp;quot;) :timed2)
;;=&amp;gt; nil

;; but is on june 2017
(crux/entity (crux/db node #inst &amp;quot;2017-06&amp;quot;) :timed2)
;;=&amp;gt; {:crux.db/id :timed2, :value 10}

;; but is no longer valid in 2019
(crux/entity (crux/db node #inst &amp;quot;2019&amp;quot;) :timed2)
;;=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 60d04e75-fcc1-4e81-895b-b4217d1fb2d5--&gt;
&lt;!-- begin heading_2 ff23dc7f-5c06-4f7d-a918-633ed60541c3--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ff23dc7f-5c06-4f7d-a918-633ed60541c3&quot;&gt;Deleting (invalidating) documents&lt;/h3&gt;
&lt;!-- end heading_2 ff23dc7f-5c06-4f7d-a918-633ed60541c3--&gt;
&lt;!-- begin code 103fa13d-1bda-4c1a-b4d3-235c26df31c6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-103fa13d-1bda-4c1a-b4d3-235c26df31c6&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; This form will delete (invalidate) our :timed2 entity  (that is valid in 2017 only) from august to october 2017.
(crux/submit-tx node
                [[:crux.tx/delete :timed2
                  #inst &amp;quot;2017-08&amp;quot;
                  #inst &amp;quot;2017-10&amp;quot;]])

;; still exists in january
(crux/entity (crux/db node #inst &amp;quot;2017-01&amp;quot;) :timed2)
;;=&amp;gt; {:crux.db/id :timed2, :value 10}

;; no longer exists in september
(crux/entity (crux/db node #inst &amp;quot;2017-09&amp;quot;) :timed2)
;;=&amp;gt; nil

;; still exists in december
(crux/entity (crux/db node #inst &amp;quot;2017-11&amp;quot;) :timed2)
;;=&amp;gt; {:crux.db/id :timed2, :value 10}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 103fa13d-1bda-4c1a-b4d3-235c26df31c6--&gt;
&lt;!-- begin paragraph ccdb6296-d337-4139-99cd-4e2be11555bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ccdb6296-d337-4139-99cd-4e2be11555bb&quot;&gt;Like &lt;code&gt;:crux.tx.put&lt;/code&gt;, &lt;code&gt;:crux.tx/delete&lt;/code&gt; do not have to take valid-time starts and ends.If not the data will be deleted (invalidated) from now.&lt;/p&gt;
&lt;!-- end paragraph ccdb6296-d337-4139-99cd-4e2be11555bb--&gt;
&lt;!-- begin code 463cf590-2086-4b9b-91b7-4764e419641b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-463cf590-2086-4b9b-91b7-4764e419641b&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/delete :timed1]])

;; :timed1 is no longer valid
(crux/entity (crux/db node) :timed1)
;;=&amp;gt; nil

;; but is still valid in 2019
(crux/entity (crux/db node #inst &amp;quot;2019&amp;quot;) :timed1)
;;=&amp;gt; {:crux.db/id :timed1, :value 10}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 463cf590-2086-4b9b-91b7-4764e419641b--&gt;
&lt;!-- begin heading_2 8f858cab-b6f1-4f06-ae7d-43aa30dbbff3--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8f858cab-b6f1-4f06-ae7d-43aa30dbbff3&quot;&gt;Eviction&lt;/h3&gt;
&lt;!-- end heading_2 8f858cab-b6f1-4f06-ae7d-43aa30dbbff3--&gt;
&lt;!-- begin code 785861b1-b368-448c-8678-75d470163670--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-785861b1-b368-448c-8678-75d470163670&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; remove all historical versions of a document
(crux/submit-tx node
                [[:crux.tx/evict :one]])
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 785861b1-b368-448c-8678-75d470163670--&gt;
&lt;!-- begin heading_2 e08fb584-3891-48e3-bc87-0754e93ab4fe--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e08fb584-3891-48e3-bc87-0754e93ab4fe&quot;&gt;Conditional transactions&lt;/h3&gt;
&lt;!-- end heading_2 e08fb584-3891-48e3-bc87-0754e93ab4fe--&gt;
&lt;!-- begin paragraph 5cf11adc-9cd1-4859-88f6-7a0dc8596131--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5cf11adc-9cd1-4859-88f6-7a0dc8596131&quot;&gt;One way to issue transaction only if certain condition is met is to use the &lt;code&gt;:crux.tx/match&lt;/code&gt; operation. It let you verify the value of a database document against a given value and issue some transactions only if those are equals.&lt;/p&gt;
&lt;!-- end paragraph 5cf11adc-9cd1-4859-88f6-7a0dc8596131--&gt;
&lt;!-- begin code 8a269f86-0f1d-4511-81b7-9721671cdbf8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8a269f86-0f1d-4511-81b7-9721671cdbf8&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/match
                  :data1 ;; we will check this entity against next provided arg
                  {:crux.db/id :data1 :myfield &amp;quot;mydata&amp;quot;} ;; the value we check the corresponding document against
                  ]

                 ;; if the match expression succeed we will transact the following forms
                 [:crux.tx/put
                  {:crux.db/id :data1
                   :myfield &amp;quot;mydata&amp;quot;
                   :foo :bar}]]) ;; &amp;lt;- we had an entry to our document

(crux/entity (crux/db node) :data1)
;;=&amp;gt; {:crux.db/id :data1, :myfield &amp;quot;mydata&amp;quot;, :foo :bar}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8a269f86-0f1d-4511-81b7-9721671cdbf8--&gt;
&lt;!-- begin paragraph 3609271d-bbb5-4212-9894-9fed37d5373f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3609271d-bbb5-4212-9894-9fed37d5373f&quot;&gt;Like previously seen operations, &lt;code&gt;crux.db/match&lt;/code&gt; can take a time at which to issue the matching.&lt;/p&gt;
&lt;!-- end paragraph 3609271d-bbb5-4212-9894-9fed37d5373f--&gt;
&lt;!-- begin code 04e82fd0-861e-4fa0-8b76-c8932163bb29--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-04e82fd0-861e-4fa0-8b76-c8932163bb29&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/match
                  :data2
                  {:crux.db/id :data3 :data 3}
                  #inst &amp;quot;2019&amp;quot;] ;; the point in time where we do the check

                 ;; since in 2019, :data2 does not still exists, the belowing transaction is not executed
                 [:crux.tx/put
                  {:crux.db/id :data3
                   :never :occurs}]])

(crux/entity (crux/db node) :data3)
;;=&amp;gt; {:crux.db/id :data3, :data 3}


&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 04e82fd0-861e-4fa0-8b76-c8932163bb29--&gt;
&lt;!-- begin heading_2 c7e3b02a-79f2-4948-9e89-9b08dba61796--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c7e3b02a-79f2-4948-9e89-9b08dba61796&quot;&gt;Modelling a simplistic bank account&lt;/h3&gt;
&lt;!-- end heading_2 c7e3b02a-79f2-4948-9e89-9b08dba61796--&gt;
&lt;!-- begin code 2a0d8da4-1c56-4dd9-b6b0-fc8f66964527--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2a0d8da4-1c56-4dd9-b6b0-fc8f66964527&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; initiating it with 2018 with 20 dollars on it
(crux/submit-tx node
                [[:crux.tx/put
                  {:crux.db/id :bank-account
                   :dollars 20}
                  #inst &amp;quot;2018&amp;quot;]])

;; for chrismass 2018 grandma gave us 20 dollars
(crux/submit-tx node
                [[:crux.tx/put
                  {:crux.db/id :bank-account
                   :dollars 40}
                  #inst &amp;quot;2018-12-25&amp;quot;]])

;; on the january first 2019, we&amp;#39;ve bought a cookie for 1 dollar
(crux/submit-tx node
                [[:crux.tx/put
                  {:crux.db/id :bank-account
                   :dollars 39}
                  #inst &amp;quot;2019-01&amp;quot;]])

;; in june 2018 we&amp;#39;ve gt 20 dollars
(crux/entity (crux/db node #inst &amp;quot;2018-06&amp;quot;) :bank-account)

;; on the december 28th of 2018, we are at our peak with the amount of 40 dollars
(crux/entity (crux/db node #inst &amp;quot;2018-12-28&amp;quot;) :bank-account)

;; sadly grandma is dead before christmass 2019 and we still have 39 dollars
(crux/entity (crux/db node) :bank-account)
;=&amp;gt; {:crux.db/id :bank-account, :dollars 39}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2a0d8da4-1c56-4dd9-b6b0-fc8f66964527--&gt;
&lt;!-- begin heading_2 bc657207-0fde-47f0-bfd6-e870c8fff330--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-bc657207-0fde-47f0-bfd6-e870c8fff330&quot;&gt;Transaction functions&lt;/h3&gt;
&lt;!-- end heading_2 bc657207-0fde-47f0-bfd6-e870c8fff330--&gt;
&lt;!-- begin paragraph c0ecd6c9-e2fd-48aa-bdbb-1e6fb9793bce--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c0ecd6c9-e2fd-48aa-bdbb-1e6fb9793bce&quot;&gt;Transaction functions are user-supplied functions that run on the individual Crux nodes when a transaction is being ingested. They can take any number of parameters, and return normal transaction operations which are then indexed as above. If they return false or throw an exception, the whole transaction will roll back.&lt;/p&gt;
&lt;!-- end paragraph c0ecd6c9-e2fd-48aa-bdbb-1e6fb9793bce--&gt;
&lt;!-- begin heading_3 f9f7e53d-9f31-43d7-9680-6d921a17be73--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-f9f7e53d-9f31-43d7-9680-6d921a17be73&quot;&gt;Example 1&lt;/h4&gt;
&lt;!-- end heading_3 f9f7e53d-9f31-43d7-9680-6d921a17be73--&gt;
&lt;!-- begin paragraph dfebfbd8-d601-44c1-960c-20dd7e634c22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dfebfbd8-d601-44c1-960c-20dd7e634c22&quot;&gt;A transaction function that add (or substract) a given amount on our fancy &lt;code&gt;:bank-account&lt;/code&gt; document.&lt;/p&gt;
&lt;!-- end paragraph dfebfbd8-d601-44c1-960c-20dd7e634c22--&gt;
&lt;!-- begin paragraph 1bb1e336-1faa-49a8-815e-2f3c22d7dce9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1bb1e336-1faa-49a8-815e-2f3c22d7dce9&quot;&gt;Transaction functions are defined with our old friend &lt;code&gt;crux.tx/put&lt;/code&gt;.The given document has to have a &lt;code&gt;:crux.db/fn&lt;/code&gt; key pointing to the function code (quoted).&lt;/p&gt;
&lt;!-- end paragraph 1bb1e336-1faa-49a8-815e-2f3c22d7dce9--&gt;
&lt;!-- begin code e71e6a8d-5e7e-4803-bf8d-ffd13195e4fd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e71e6a8d-5e7e-4803-bf8d-ffd13195e4fd&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/put {:crux.db/id :update-bank-account
                                :crux.db/fn
                                ;; note that the function body is quoted.
                                &amp;#39;(fn [ctx delta]
                                   ;; the first argument (ctx) is holding our node
                                   ;; we can use it as we&amp;#39;ve done so far
                                   (let [db (crux.api/db ctx) ;; we taking the present value of the database
                                         entity (crux.api/entity db :bank-account)] ;; using it to retrieve our bank-account document
                                     ;; then we are returning a vector of transaction (containing only one in this case)
                                     [[:crux.tx/put (update entity :dollars + delta)]]))}]])

(crux/submit-tx node
                [[:crux.tx/fn :update-bank-account 5]])

(crux/entity (crux/db node) :bank-account)
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e71e6a8d-5e7e-4803-bf8d-ffd13195e4fd--&gt;
&lt;!-- begin heading_3 89cd500b-3548-4507-aafb-5d1b2b847262--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-89cd500b-3548-4507-aafb-5d1b2b847262&quot;&gt;Exemple 2&lt;/h4&gt;
&lt;!-- end heading_3 89cd500b-3548-4507-aafb-5d1b2b847262--&gt;
&lt;!-- begin paragraph d4bc2b1c-a78e-426e-9883-e230818406b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d4bc2b1c-a78e-426e-9883-e230818406b7&quot;&gt;A transaction function that can create a new document by merging existing/given ones.&lt;/p&gt;
&lt;!-- end paragraph d4bc2b1c-a78e-426e-9883-e230818406b7--&gt;
&lt;!-- begin code 1181815d-ae0b-4985-ad80-dd7c414b871a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1181815d-ae0b-4985-ad80-dd7c414b871a&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/put {:crux.db/id :merge
                                :crux.db/fn
                                &amp;#39;(fn [ctx id &amp;amp; xs]
                                   (let [db (crux.api/db ctx)]
                                     [[:crux.tx/put
                                       (reduce merge
                                               {:crux.db/id id}
                                               (map (fn [e]
                                                      (cond
                                                        (keyword? e) (dissoc (crux.api/entity db e) :crux.db/id)
                                                        (map? e) e))
                                                    xs))]]))}]])

(crux/submit-tx node
                [ ;; putting 2 dummy records into the db
                 [:crux.tx/put {:crux.db/id :m1 :a 1 :b 2}]
                 [:crux.tx/put {:crux.db/id :m2 :a 4 :c 3}]
                 ;; use them to built another dummy record via our freshly defined merge transaction function
                 [:crux.tx/fn :merge :m3 :m1 :m2 {:d 5}]])

(crux/entity (crux/db node) :m3)
;;=&amp;gt; {:crux.db/id :m3, :a 4, :b 2, :c 3, :d 5}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1181815d-ae0b-4985-ad80-dd7c414b871a--&gt;
&lt;!-- begin heading_3 6d38165f-c3a1-4f9e-99bc-2bbd68f5a1b9--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-6d38165f-c3a1-4f9e-99bc-2bbd68f5a1b9&quot;&gt;Example 3&lt;/h4&gt;
&lt;!-- end heading_3 6d38165f-c3a1-4f9e-99bc-2bbd68f5a1b9--&gt;
&lt;!-- begin paragraph 88151651-750e-434b-a7ab-0a62c04598d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-88151651-750e-434b-a7ab-0a62c04598d8&quot;&gt;A transaction function that let you extend your document with new key (semantically similar to clojure&amp;#39;s &lt;code&gt;assoc&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph 88151651-750e-434b-a7ab-0a62c04598d8--&gt;
&lt;!-- begin code d08dc96f-67ea-4465-bb6d-2a90f56f993a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d08dc96f-67ea-4465-bb6d-2a90f56f993a&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/submit-tx node
                [[:crux.tx/put {:crux.db/id :assoc
                                :crux.db/fn
                                ;; note that the function body is quoted.
                                &amp;#39;(fn [ctx eid &amp;amp; kvs]
                                   (let [db (crux.api/db ctx)
                                         entity (crux.api/entity db eid)]
                                     [[:crux.tx/put (apply assoc entity kvs)]]))}]])

(crux/submit-tx node
                [[:crux.tx/put {:crux.db/id :ivan, :age 40}]])

(crux/submit-tx node
                [[:crux.tx/fn :assoc :ivan :genre :M]])

(crux/entity (crux/db node) :ivan)
;;=&amp;gt; {:crux.db/id :ivan, :age 40, :genre :M}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d08dc96f-67ea-4465-bb6d-2a90f56f993a--&gt;
&lt;!-- begin heading_2 1bd39fd3-c7c2-40a1-8acd-f135f5740a34--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-1bd39fd3-c7c2-40a1-8acd-f135f5740a34&quot;&gt;Speculative transactions&lt;/h3&gt;
&lt;!-- end heading_2 1bd39fd3-c7c2-40a1-8acd-f135f5740a34--&gt;
&lt;!-- begin code fe35f8e0-8a35-4e36-9b82-e1fdd71149a5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fe35f8e0-8a35-4e36-9b82-e1fdd71149a5&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; with the `crux/with-tx` function, we are creating an enriched database value without persisting anything to the system
(def speculative-db
  (crux/with-tx (crux/db node)
                [[:crux.tx/put {:crux.db/id :speculative-doc1 :value 42}]]))
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fe35f8e0-8a35-4e36-9b82-e1fdd71149a5--&gt;
&lt;!-- begin paragraph 5df98919-d4b8-4558-87ba-5cc347b41bd4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5df98919-d4b8-4558-87ba-5cc347b41bd4&quot;&gt;We can check that the added document does not exist in our real database.&lt;/p&gt;
&lt;!-- end paragraph 5df98919-d4b8-4558-87ba-5cc347b41bd4--&gt;
&lt;!-- begin code 2cca938f-a002-4bf3-b0f8-feca6d9cc242--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2cca938f-a002-4bf3-b0f8-feca6d9cc242&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(crux/entity (crux/db node)
             :speculative-doc1)
;=&amp;gt; nil

;; and that it exists in our speculative db
(crux/entity speculative-db
             :speculative-doc1)
;=&amp;gt; {:crux.db/id :speculative-doc1, :value 42}

;; we can issue queries over our speculative db
(crux/q speculative-db
        &amp;#39;{:find [x]
          :where [[x :value 42]]})
;=&amp;gt; #{[:speculative-doc1]}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2cca938f-a002-4bf3-b0f8-feca6d9cc242--&gt;
&lt;!-- begin heading_2 74a44ed9-7a4a-4fc5-84b4-2db36c242eba--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-74a44ed9-7a4a-4fc5-84b4-2db36c242eba&quot;&gt;Queries&lt;/h3&gt;
&lt;!-- end heading_2 74a44ed9-7a4a-4fc5-84b4-2db36c242eba--&gt;
&lt;!-- begin code d1ff1fdd-9ec5-42ee-b007-c55100bd471c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d1ff1fdd-9ec5-42ee-b007-c55100bd471c&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(ns crux-starter.p02_queries
  (:require [crux.api :as crux]
            [crux-starter.p00_setup :refer [node]]
            [crux-starter.sugar :refer [puts q]]))
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d1ff1fdd-9ec5-42ee-b007-c55100bd471c--&gt;
&lt;!-- begin heading_2 c0aa12c5-b310-436e-98e9-0c62cae018ce--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c0aa12c5-b310-436e-98e9-0c62cae018ce&quot;&gt;Data&lt;/h3&gt;
&lt;!-- end heading_2 c0aa12c5-b310-436e-98e9-0c62cae018ce--&gt;
&lt;!-- begin paragraph ddaea801-f444-41b7-bf4d-69ba9b2960d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddaea801-f444-41b7-bf4d-69ba9b2960d4&quot;&gt;Putting some data to play with in the database:&lt;/p&gt;
&lt;!-- end paragraph ddaea801-f444-41b7-bf4d-69ba9b2960d4--&gt;
&lt;!-- begin code 218c2ef0-deb1-42b8-b9d7-5712271ff002--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-218c2ef0-deb1-42b8-b9d7-5712271ff002&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(puts

  {:id :philippe
   :name &amp;quot;Philippe&amp;quot;
   :genre :M
   :age 70}

  {:id :odette
   :name &amp;quot;Odette&amp;quot;
   :genre :F
   :age 71}

  {:id :jean-pierre
   :name &amp;quot;Jean-Pierre&amp;quot;
   :genre :M
   :age 40
   :mother :odette
   :father :philippe}

  {:id :blandine
   :name &amp;quot;Blandine&amp;quot;
   :genre :F
   :age 43}

  {:id :blandine
   :name &amp;quot;Valerie&amp;quot;
   :genre :F
   :age 35}

  {:id :pierre
   :name &amp;quot;Pierre&amp;quot;
   :genre :M
   :age 20
   :father :jean-pierre
   :mother :blandine}

  {:id :clement
   :name &amp;quot;Clément&amp;quot;
   :genre :M
   :age 19
   :father :jean-pierre
   :mother :blandine}

  {:id :mathilde
   :name &amp;quot;Mathilde&amp;quot;
   :genre :F
   :age 12
   :father :jean-pierre
   :mother :valerie}

  {:id :nicolas
   :name &amp;quot;Nicolas&amp;quot;
   :genre :M
   :age 9
   :father :jean-pierre
   :mother :valerie}

  )
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 218c2ef0-deb1-42b8-b9d7-5712271ff002--&gt;
&lt;!-- begin heading_2 421cdb58-e9c0-4e4d-bf97-02930319a691--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-421cdb58-e9c0-4e4d-bf97-02930319a691&quot;&gt;Basics&lt;/h3&gt;
&lt;!-- end heading_2 421cdb58-e9c0-4e4d-bf97-02930319a691--&gt;
&lt;!-- begin code 772281d0-d1e3-4897-a519-e384eb1d4844--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-772281d0-d1e3-4897-a519-e384eb1d4844&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; attribute existence
;; find every documents that have a `:father` attribute
(q &amp;#39;{:find [x]
     :where [[x :father]]})
;=&amp;gt; #{[:clement] [:jean-pierre] [:mathilde] [:nicolas] [:pierre]}

;; finds all males
(q &amp;#39;{:find [p]
     :where [[p :genre :M]]})
;;=&amp;gt; #{[:clement] [:jean-pierre] [:nicolas] [:pierre]}

;; find sons of Valerie
(q &amp;#39;{:find [p]
     :where [[p :genre :M]
             [p :mother :valerie]]})
;;=&amp;gt; #{[:nicolas]}

;; names of blandine&amp;#39;s children
(q &amp;#39;{:find [n]
     :where [[p :mother :blandine]
             [p :name n]]})
;;=&amp;gt; #{[&amp;quot;Pierre&amp;quot;] [&amp;quot;Clément&amp;quot;]}

;; retrieve full entities
(q &amp;#39;{:find [p]
     :where [[p :genre :M]]
     :full-results? true})
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 772281d0-d1e3-4897-a519-e384eb1d4844--&gt;
&lt;!-- begin heading_2 6e62ba06-20f3-488a-a1c1-91566a1f1582--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6e62ba06-20f3-488a-a1c1-91566a1f1582&quot;&gt;Predicates&lt;/h3&gt;
&lt;!-- end heading_2 6e62ba06-20f3-488a-a1c1-91566a1f1582--&gt;
&lt;!-- begin code bec2b6cf-15be-4bb9-9c1e-80a88e3e408e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bec2b6cf-15be-4bb9-9c1e-80a88e3e408e&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; finds all adults
(q &amp;#39;{:find [p]
     :where [[p :age a]
             ;; predicates expressions have to be wrapped in vector literal
             [(&amp;gt;= a 18)]]})

;; arithmetic constraints can take logic variables as arguments
(q &amp;#39;{:find [p q]
     :where [[p :age pa]
             [q :age qa]
             [(&amp;gt; pa qa)]]})

;; regular functions
(q &amp;#39;{:find [p half-age]
     :where [[p :age a]
             ;; we can catch the return value into a var
             [(/ a 2) half-age]]})

;; find couples of persons such that the first is two times older than the second
(q &amp;#39;{:find [p q]
     :where [[p :age pa]
             [q :age qa]
             [(/ pa 2) qa]]})
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bec2b6cf-15be-4bb9-9c1e-80a88e3e408e--&gt;
&lt;!-- begin heading_2 bb966c56-438f-4989-8edc-fa846aac489d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-bb966c56-438f-4989-8edc-fa846aac489d&quot;&gt;Logic connectors&lt;/h3&gt;
&lt;!-- end heading_2 bb966c56-438f-4989-8edc-fa846aac489d--&gt;
&lt;!-- begin code c8a3711f-9c5d-4947-9326-a09815ea25dc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c8a3711f-9c5d-4947-9326-a09815ea25dc&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; or
(q &amp;#39;{:find [x]
     :where [(or [x :name &amp;quot;Nicolas&amp;quot;]
                 [x :name &amp;quot;Mathilde&amp;quot;])]})

;; in this simple case we could use set literal
(q &amp;#39;{:find [x]
     :where [[x :name #{&amp;quot;Mathilde&amp;quot; &amp;quot;Nicolas&amp;quot;}]]})

;; nested `or` and `and`
(q &amp;#39;{:find [x age]
     :where [(or (and [x :name &amp;quot;Nicolas&amp;quot;] [x :age age])
                 (and [x :father :jean-pierre]
                      [x :age age]
                      (or [(= age 12)]
                          [(&amp;gt; age 19)])))]})
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c8a3711f-9c5d-4947-9326-a09815ea25dc--&gt;
&lt;!-- begin heading_2 d323dea0-7dbf-4b67-8c51-069fe2427d14--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d323dea0-7dbf-4b67-8c51-069fe2427d14&quot;&gt;Rules&lt;/h3&gt;
&lt;!-- end heading_2 d323dea0-7dbf-4b67-8c51-069fe2427d14--&gt;
&lt;!-- begin paragraph 5b3842a9-fdcf-41cf-a075-2914997aac8e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5b3842a9-fdcf-41cf-a075-2914997aac8e&quot;&gt;Rules let you abstract clauses and create a more readable language for your queries.For instance we will create a &lt;code&gt;parent&lt;/code&gt; rule wich describe a parent relationship between its two arguments&lt;code&gt;(parent a b)&lt;/code&gt; means that &lt;code&gt;a&lt;/code&gt; is a &lt;code&gt;parent&lt;/code&gt; of &lt;code&gt;b&lt;/code&gt; (either father or mother).&lt;/p&gt;
&lt;!-- end paragraph 5b3842a9-fdcf-41cf-a075-2914997aac8e--&gt;
&lt;!-- begin code 418a8d1b-2eea-4eb0-aee4-8ffd3a8e7bdb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-418a8d1b-2eea-4eb0-aee4-8ffd3a8e7bdb&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(q &amp;#39;{:find [a b]
     ;; introducing the parent rule
     :rules [[(parent b a) (or [a :mother b] [a :father b])]]
     ;; using it
     :where [(parent a b)]
     })
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 418a8d1b-2eea-4eb0-aee4-8ffd3a8e7bdb--&gt;
&lt;!-- begin paragraph 4bdffe58-ea22-421d-8151-f98b7dd6ea8b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4bdffe58-ea22-421d-8151-f98b7dd6ea8b&quot;&gt;Rules are also a great way to express traversal relationships. Here we will define an &lt;code&gt;anccestor&lt;/code&gt; rule.&lt;/p&gt;
&lt;!-- end paragraph 4bdffe58-ea22-421d-8151-f98b7dd6ea8b--&gt;
&lt;!-- begin code 06363068-7a27-495c-94da-b8684cfb05ce--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-06363068-7a27-495c-94da-b8684cfb05ce&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(q &amp;#39;{:find [x]
     ;; we define ancestor in terms of parent
     :rules [[(parent p c) (or [c :mother p] [c :father p])]
             ;; ancestors have 2 cases
             [(ancestor a b) (parent a b)] ;; direct parent
             [(ancestor a b) (parent a pa) (ancestor fa b)] ;; transitive parent
             ]

     :where [(ancestor :odette x)]})
;;=&amp;gt; #{[:clement] [:jean-pierre] [:mathilde] [:nicolas] [:pierre]}
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 06363068-7a27-495c-94da-b8684cfb05ce--&gt;
&lt;!-- begin heading_2 1ceaa4ce-e1d1-4a1b-8f69-b893d45ea923--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-1ceaa4ce-e1d1-4a1b-8f69-b893d45ea923&quot;&gt;Ordering and pagination&lt;/h3&gt;
&lt;!-- end heading_2 1ceaa4ce-e1d1-4a1b-8f69-b893d45ea923--&gt;
&lt;!-- begin code 218f3660-4fc2-432d-8c42-932a61d4de55--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-218f3660-4fc2-432d-8c42-932a61d4de55&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(q &amp;#39;{:find [p age]
     :where [[p :age age]]
     :order-by [[age :asc]]})

#_[[:nicolas 9]
   [:mathilde 12]
   [:clement 19]
   [:pierre 20]
   [:blandine 35]
   [:jean-pierre 40]
   [:philippe 70]
   [:odette 71]]

(q &amp;#39;{:find [p age]
     :where [[p :age age]]
     :order-by [[age :asc]]
     :limit 4 ;; limits the number of results to 4
     :offset 2}) ;; starting at index 2

#_[[:clement 19]
   [:pierre 20]
   [:blandine 35]
   [:jean-pierre 40]]
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 218f3660-4fc2-432d-8c42-932a61d4de55--&gt;
&lt;!-- begin heading_2 fa18f57b-34ac-4678-a9d3-eb96e79a53bb--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-fa18f57b-34ac-4678-a9d3-eb96e79a53bb&quot;&gt;EQL projections&lt;/h3&gt;
&lt;!-- end heading_2 fa18f57b-34ac-4678-a9d3-eb96e79a53bb--&gt;
&lt;!-- begin paragraph 3ac35ed8-47b1-4b88-b0f2-6e023434f6bf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ac35ed8-47b1-4b88-b0f2-6e023434f6bf&quot;&gt;Crux queries support a &amp;#39;projection&amp;#39; syntax, allowing you to decouple specifying which entities you want from what data you’d like about those entities in your queries. Crux’s support is based on the excellent EDN Query Language (EQL) library.&lt;/p&gt;
&lt;!-- end paragraph 3ac35ed8-47b1-4b88-b0f2-6e023434f6bf--&gt;
&lt;!-- begin code b6b419b1-de87-4476-94b9-04d067530de9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b6b419b1-de87-4476-94b9-04d067530de9&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(puts
  {:crux.db/id :lawyer, :profession/name &amp;quot;Lawyer&amp;quot;}
  {:crux.db/id :doctor, :profession/name &amp;quot;Doctor&amp;quot;}
  {:crux.db/id :u1, :user/name &amp;quot;Ivan&amp;quot;, :user/profession :doctor},
  {:crux.db/id :u2, :user/name &amp;quot;Sergei&amp;quot;, :user/profession :lawyer}
  {:crux.db/id :u3, :user/name &amp;quot;Petr&amp;quot;, :user/profession :doctor})

(q &amp;#39;{:find [(eql/project ?user [:user/name {:user/profession [:profession/name]}])]
     :where [[?user :user/name ?uid]]})
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b6b419b1-de87-4476-94b9-04d067530de9--&gt;
&lt;!-- begin heading_2 0df7542a-5749-4984-8995-25fea97e1feb--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-0df7542a-5749-4984-8995-25fea97e1feb&quot;&gt;Conclusion&lt;/h3&gt;
&lt;!-- end heading_2 0df7542a-5749-4984-8995-25fea97e1feb--&gt;
&lt;!-- begin paragraph 7baefe6b-cfeb-42d0-b9c1-7bff826f579a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7baefe6b-cfeb-42d0-b9c1-7bff826f579a&quot;&gt;In this article, we&amp;#39;ve got a brief overview of crux main ideas and API, many things remains to be seen,&lt;br /&gt;
like the infrastructure and deployment parts. I hope to be able to cover this in further articles, so stay tuned!&lt;/p&gt;
&lt;!-- end paragraph 7baefe6b-cfeb-42d0-b9c1-7bff826f579a--&gt;
&lt;!-- begin paragraph fc6ada08-be90-4db1-a801-924dcd4c58ef--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fc6ada08-be90-4db1-a801-924dcd4c58ef&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fc6ada08-be90-4db1-a801-924dcd4c58ef--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:ce5a2a60182e45c8b4d913e35dccfc30</id>
    <title>Modifier les variables d&#039;environnements en Scala</title>
    <updated>2020-09-08T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/modifier-les-variables-d-environnements-en-scala.html"/>
    <!--summary C&#039;est parfois nécessaire pour des tests unitaires. Le problème est que sys.env, System et ProcessEnvironment sont immuables. Ce n&#039;est pas grave, on va faire un peu de réflexion 😁-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="article court"></category>    <category term="Java"></category>    <content type="html">
&lt;!-- begin paragraph c22c856a-b580-4d05-9205-325285659e6e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c22c856a-b580-4d05-9205-325285659e6e&quot;&gt;C&amp;#39;est parfois nécessaire pour des tests unitaires. Le problème est que &lt;code&gt;sys.env&lt;/code&gt;, &lt;code&gt;System&lt;/code&gt; et &lt;code&gt;ProcessEnvironment&lt;/code&gt; sont immuables. Ce n&amp;#39;est pas grave, on va faire un peu de réflexion 😁&lt;/p&gt;
&lt;!-- end paragraph c22c856a-b580-4d05-9205-325285659e6e--&gt;
&lt;!-- begin code 0bfa0add-5d86-4196-874c-62bdf56559e7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0bfa0add-5d86-4196-874c-62bdf56559e7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def setEnv(name: String, value: String): Unit = {
  val processEnvClass =
    Class.forName(&amp;quot;java.lang.ProcessEnvironment&amp;quot;)
  val unmodifiableEnvironmentField =
    processEnvClass.getDeclaredField(&amp;quot;theUnmodifiableEnvironment&amp;quot;)
  unmodifiableEnvironmentField.setAccessible(true)
  val unmodifiableEnvironment =
    unmodifiableEnvironmentField.get(null)

  val unmodifiableMapClass =
    Class.forName(&amp;quot;java.util.Collections$UnmodifiableMap&amp;quot;)
  val mField =
    unmodifiableMapClass.getDeclaredField(&amp;quot;m&amp;quot;)
  mField.setAccessible(true)
  val map =
    mField
      .get(theUnmodifiableEnvironment)
      .asInstanceOf[java.util.Map[String, String]]

  map.put(name, value)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0bfa0add-5d86-4196-874c-62bdf56559e7--&gt;
&lt;!-- begin paragraph 5ecce7f1-0a30-4a71-bab4-effd31535598--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ecce7f1-0a30-4a71-bab4-effd31535598&quot;&gt;Note : ça concerne principalement Scala, car dans ce langage on utilise principalement &lt;code&gt;sys.env&lt;/code&gt; pour accéder aux variables d&amp;#39;environnement.&lt;/p&gt;
&lt;!-- end paragraph 5ecce7f1-0a30-4a71-bab4-effd31535598--&gt;
&lt;!-- begin paragraph 3709b432-8cd5-4221-8eb2-852fa81a3564--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3709b432-8cd5-4221-8eb2-852fa81a3564&quot;&gt;En Java, si on passe par &lt;code&gt;System.getenv&lt;/code&gt; (majorité des cas), il n&amp;#39;y a pas de problème non plus. &lt;code&gt;sys.env&lt;/code&gt; utilise d&amp;#39;ailleurs par cette méthode. Par contre, si vous utilisez, &lt;code&gt;ProcessBuilder.environment&lt;/code&gt; vous ne verrez pas de modification dans les variables d&amp;#39;environnement, car cette méthode procède différemment pour les récupérer.&lt;/p&gt;
&lt;!-- end paragraph 3709b432-8cd5-4221-8eb2-852fa81a3564--&gt;
&lt;!-- begin paragraph b3a319c8-818a-4397-8bee-8d1f77f61cb8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3a319c8-818a-4397-8bee-8d1f77f61cb8&quot;&gt;Photographie par &lt;a href=&quot;https://unsplash.com/@dmey503&quot;&gt;Dan Meyers&lt;/a&gt; sur &lt;a href=&quot;https://unsplash.com/&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph b3a319c8-818a-4397-8bee-8d1f77f61cb8--&gt;
&lt;!-- begin paragraph fc5686c2-420e-4a3e-92cb-d947076616de--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fc5686c2-420e-4a3e-92cb-d947076616de&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fc5686c2-420e-4a3e-92cb-d947076616de--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:95c48411a762449c8eb6bb53b6f0af34</id>
    <title>Glycogen</title>
    <updated>2020-06-14T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/glycogen.html"/>
    <!--summary -->    <author>
      <name>Pierre Baille</name>
      <uri>https://univalence.io/blog/auteurs/pierre-baille.html</uri>
    </author>        <category term="clojure"></category>    <category term="polymorphism"></category>    <category term="library"></category>    <content type="html">
&lt;!-- begin heading_2 c91effcc-6fd1-419a-a75f-ce1f0f752e4c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c91effcc-6fd1-419a-a75f-ce1f0f752e4c&quot;&gt;Motivation&lt;/h3&gt;
&lt;!-- end heading_2 c91effcc-6fd1-419a-a75f-ce1f0f752e4c--&gt;
&lt;!-- begin paragraph ebbf5e23-976e-4230-b363-4ce9db509a6c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ebbf5e23-976e-4230-b363-4ce9db509a6c&quot;&gt;In Clojure(Script), when fast polymorphic functions are needed, we are typically using Protocols.&lt;/p&gt;
&lt;!-- end paragraph ebbf5e23-976e-4230-b363-4ce9db509a6c--&gt;
&lt;!-- begin paragraph 756945d3-6548-48e9-8a85-d7df35e5f4c2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-756945d3-6548-48e9-8a85-d7df35e5f4c2&quot;&gt;As an example, we will introduce a protocol for adding things together.&lt;/p&gt;
&lt;!-- end paragraph 756945d3-6548-48e9-8a85-d7df35e5f4c2--&gt;
&lt;!-- begin code f96aa442-7458-4c7f-b511-89246a695116--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f96aa442-7458-4c7f-b511-89246a695116&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(defprotocol IPlus
  (plus [x y] &amp;quot;add two things together&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f96aa442-7458-4c7f-b511-89246a695116--&gt;
&lt;!-- begin paragraph 1431e207-93b5-4a71-b85d-ed2e4e1c3b11--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1431e207-93b5-4a71-b85d-ed2e4e1c3b11&quot;&gt;Then we extend this protocol to some types&lt;/p&gt;
&lt;!-- end paragraph 1431e207-93b5-4a71-b85d-ed2e4e1c3b11--&gt;
&lt;!-- begin code f97b5ba6-b965-484a-b774-fdf11fabb765--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f97b5ba6-b965-484a-b774-fdf11fabb765&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(extend-protocol IPlus
   clojure.lang.IPersistentVector
   (plus [x y] (into x y))
   clojure.lang.IPersistentMap
   (plus [x y] (merge x y))
   clojure.lang.ISeq
   (plus [x y] (merge x y)))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f97b5ba6-b965-484a-b774-fdf11fabb765--&gt;
&lt;!-- begin paragraph 7e996860-cfa1-4f74-8798-712c107c8ae5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7e996860-cfa1-4f74-8798-712c107c8ae5&quot;&gt;If you are also targeting ClojureScript, you can use reader conditionals&lt;/p&gt;
&lt;!-- end paragraph 7e996860-cfa1-4f74-8798-712c107c8ae5--&gt;
&lt;!-- begin code a33cb1e1-0bfe-4728-9be7-22f54c5598ab--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a33cb1e1-0bfe-4728-9be7-22f54c5598ab&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(extend-protocol IPlus
  #?(:clj  clojure.lang.PersistentVector
     :cljs cljs.core/PersistentVector)
  (plus [x y] (into x y))
  #?(:clj  clojure.lang.IPersistentMap
     :cljs cljs.core/PersistentArrayMap)
  (plus [x y] (merge x y))
  #?(:clj  clojure.lang.ISeq
     :cljs cljs.core/List)
  (plus [x y] (concat x y)))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a33cb1e1-0bfe-4728-9be7-22f54c5598ab--&gt;
&lt;!-- begin paragraph 842d3467-0efe-4483-9fe6-8be72a97832b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-842d3467-0efe-4483-9fe6-8be72a97832b&quot;&gt;Those reader conditionals can become a bit noisy when this kind of code is strongly used in a project. &lt;/p&gt;
&lt;!-- end paragraph 842d3467-0efe-4483-9fe6-8be72a97832b--&gt;
&lt;!-- begin paragraph 060d7311-4aae-46e6-a6aa-be926b1e7c6f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-060d7311-4aae-46e6-a6aa-be926b1e7c6f&quot;&gt;Let&amp;#39;s check that everything is working as intended&lt;/p&gt;
&lt;!-- end paragraph 060d7311-4aae-46e6-a6aa-be926b1e7c6f--&gt;
&lt;!-- begin code 3a52ece0-8160-412e-a890-1861ead9503d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3a52ece0-8160-412e-a890-1861ead9503d&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(is (plus [1 2 3] [4 5 6])
    [1 2 3 4 5 6])

(is (plus {:a 1 :b 2} {:b 22 :c 3})
    {:a 1 :b 22 :c 3})

(is (plus (list 1 2) (list 1 2))
    (list 1 2 1 2))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3a52ece0-8160-412e-a890-1861ead9503d--&gt;
&lt;!-- begin paragraph 071b87e8-ee90-42ae-a9c6-dbcc51f885e2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-071b87e8-ee90-42ae-a9c6-dbcc51f885e2&quot;&gt;It seems to work! But in fact, this is not so easy in ClojureScript:&lt;/p&gt;
&lt;!-- end paragraph 071b87e8-ee90-42ae-a9c6-dbcc51f885e2--&gt;
&lt;!-- begin code 4c9a99a2-e57c-4625-a45e-eae5ce6acdd3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4c9a99a2-e57c-4625-a45e-eae5ce6acdd3&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(plus (range 4) (list 1 2)) 
;; this fails, complaining there is no implementation for cljs.core/Range&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4c9a99a2-e57c-4625-a45e-eae5ce6acdd3--&gt;
&lt;!-- begin paragraph bc5967ad-22af-49d5-b745-04aa298d64cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bc5967ad-22af-49d5-b745-04aa298d64cf&quot;&gt;You may think that I should extend &lt;code&gt;ISeq&lt;/code&gt; instead of &lt;code&gt;cljs.core/List&lt;/code&gt;? But it does not work, because ClojureScript does not have interfaces like Clojure. To obtain the same thing that we have in Clojure, in ClojureScript we have to write this:&lt;/p&gt;
&lt;!-- end paragraph bc5967ad-22af-49d5-b745-04aa298d64cf--&gt;
&lt;!-- begin code 0fac43db-5563-429d-966f-9a603f785f75--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0fac43db-5563-429d-966f-9a603f785f75&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(extend-protocol IPlus
   cljs.core/PersistentVector
   (plus [x y] (into x y))
   ;; duplicated impls to cover all IMap
   cljs.core/PersistentArrayMap
   (plus [x y] (merge x y))
   cljs.core/PersistentHashMap
   (plus [x y] (merge x y))
   cljs.core/PersistentTreeMap
   (plus [x y] (merge x y))
   ;; same here
   cljs.core/List
   (plus [x y] (concat x y))
   cljs.core/Range
   (plus [x y] (concat x y))
   ;; ... many other duplicates for covering ISeq
   ;; complete list of classes actually is:
   ;; ArrayNodeSeq ChunkedCons ChunkedSeq Cons Cycle ES6IteratorSeq
   ;; EmptyList IndexedSeq Iterate KeySeq LazySeq List NodeSeq PersistentArrayMapSeq
   ;; PersistentQueue PersistentQueueSeq PersistentTreeMapSeq RSeq Range RangeChunk Repeat ValSeq
   )&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0fac43db-5563-429d-966f-9a603f785f75--&gt;
&lt;!-- begin paragraph 742b01da-0ecf-4c54-aff2-d25d3759cbe3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-742b01da-0ecf-4c54-aff2-d25d3759cbe3&quot;&gt;Obviously, this is not really what we want... so i&amp;#39;ve wrote the &lt;a href=&quot;https://github.com/univalence/glycogen&quot;&gt;Glycogen&lt;/a&gt; library&lt;/p&gt;
&lt;!-- end paragraph 742b01da-0ecf-4c54-aff2-d25d3759cbe3--&gt;
&lt;!-- begin heading_2 c00d1228-9903-45f1-beed-25a65a9d3b43--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c00d1228-9903-45f1-beed-25a65a9d3b43&quot;&gt;Proposition&lt;/h3&gt;
&lt;!-- end heading_2 c00d1228-9903-45f1-beed-25a65a9d3b43--&gt;
&lt;!-- begin paragraph d89995a9-24fd-4635-ae04-edb486b207d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d89995a9-24fd-4635-ae04-edb486b207d2&quot;&gt;If we forget about implementation details, the code defined above could be defined like this:&lt;/p&gt;
&lt;!-- end paragraph d89995a9-24fd-4635-ae04-edb486b207d2--&gt;
&lt;!-- begin code 5375204c-0f07-4097-b7a6-73fa240b8fbf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5375204c-0f07-4097-b7a6-73fa240b8fbf&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(defg plus [x y]
      :map (merge x y)
      :vec (into x y)
      :lst (concat x y))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5375204c-0f07-4097-b7a6-73fa240b8fbf--&gt;
&lt;!-- begin paragraph 24a36921-485a-4976-8011-3ecf481f002c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-24a36921-485a-4976-8011-3ecf481f002c&quot;&gt;We immediately spot the point here, not bothering with platform specific stuff. The &lt;code&gt;defg&lt;/code&gt; macro is part of the &lt;code&gt;glycogen.generics&lt;/code&gt; namespace.&lt;/p&gt;
&lt;!-- end paragraph 24a36921-485a-4976-8011-3ecf481f002c--&gt;
&lt;!-- begin paragraph 484c92da-6366-4699-8ca2-15f5b4015392--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-484c92da-6366-4699-8ca2-15f5b4015392&quot;&gt;So, as you see, the classes of the target platforms (and reader conditionals) have disappeared and the problematic expression works as intended.&lt;/p&gt;
&lt;!-- end paragraph 484c92da-6366-4699-8ca2-15f5b4015392--&gt;
&lt;!-- begin code 37280457-e564-4b35-9118-979ff4600a11--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-37280457-e564-4b35-9118-979ff4600a11&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(is (plus (range 4) (list 1 2))
    (list 0 1 2 3 1 2))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 37280457-e564-4b35-9118-979ff4600a11--&gt;
&lt;!-- begin paragraph 14177850-3c87-4dc7-babc-2837c10aa5a4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-14177850-3c87-4dc7-babc-2837c10aa5a4&quot;&gt;Of course this is a simple case. There is only one arity and no destructuration patterns (all cases are sharing the same argument vector pattern, this is not always what we want, sometimes one implementation need its own destructuring pattern. We will address these considerations at a later point. For now lets discuss those type-keywords that we are using to indicate the type(s) that each implementation belongs to. (e.g: &lt;code&gt;:map&lt;/code&gt; &lt;code&gt;:vec&lt;/code&gt; &lt;code&gt;:lst&lt;/code&gt; ...)&lt;/p&gt;
&lt;!-- end paragraph 14177850-3c87-4dc7-babc-2837c10aa5a4--&gt;
&lt;!-- begin paragraph 99d1f755-0177-4a2c-adce-77943ddffa92--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-99d1f755-0177-4a2c-adce-77943ddffa92&quot;&gt;To add glycogen to your project simply add &lt;code&gt;glycogen {:mvn/version &amp;quot;0.1.1-SNAPSHOT&amp;quot;}&lt;/code&gt; to your &lt;code&gt;deps.edn&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 99d1f755-0177-4a2c-adce-77943ddffa92--&gt;
&lt;!-- begin heading_3 7854ca00-26c5-4330-a63a-8e40108fe64c--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-7854ca00-26c5-4330-a63a-8e40108fe64c&quot;&gt;Hidding host classes&lt;/h4&gt;
&lt;!-- end heading_3 7854ca00-26c5-4330-a63a-8e40108fe64c--&gt;
&lt;!-- begin paragraph cf2f0dd9-6797-4589-874a-ccee0b79e54f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf2f0dd9-6797-4589-874a-ccee0b79e54f&quot;&gt;One of the stepping stone on which this library is built upon is a little wrapper around hosting platform&amp;#39;s type hierarchies that acts as a compatibility layer and let us forget about it.&lt;/p&gt;
&lt;!-- end paragraph cf2f0dd9-6797-4589-874a-ccee0b79e54f--&gt;
&lt;!-- begin paragraph 38fa88ea-f747-40f3-85a0-0405c0bed43e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38fa88ea-f747-40f3-85a0-0405c0bed43e&quot;&gt;It resides in the &lt;code&gt;glycogen.types&lt;/code&gt; namespace.&lt;/p&gt;
&lt;!-- end paragraph 38fa88ea-f747-40f3-85a0-0405c0bed43e--&gt;
&lt;!-- begin code 3e0f327d-0297-4691-adb9-8d6e3f80f4eb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3e0f327d-0297-4691-adb9-8d6e3f80f4eb&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(require &amp;#39;[glycogen.types :as t] 
         &amp;#39;[glycogen.state :as state])&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3e0f327d-0297-4691-adb9-8d6e3f80f4eb--&gt;
&lt;!-- begin paragraph 4a4735f1-1693-41ad-a434-1228db1c84bf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a4735f1-1693-41ad-a434-1228db1c84bf&quot;&gt;The idea is really simple, there is one type register per target platform consisting of a map of type: &lt;code&gt;keyword -&amp;gt; set of keyword or class-symbol&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 4a4735f1-1693-41ad-a434-1228db1c84bf--&gt;
&lt;!-- begin paragraph 889d6d34-f8a0-4ed2-a08c-46362744e5da--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-889d6d34-f8a0-4ed2-a08c-46362744e5da&quot;&gt;from clojure you can inspect it like this:&lt;/p&gt;
&lt;!-- end paragraph 889d6d34-f8a0-4ed2-a08c-46362744e5da--&gt;
&lt;!-- begin code 104b4884-ab9c-4011-a756-1c9d9c9d5103--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-104b4884-ab9c-4011-a756-1c9d9c9d5103&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; clojure&amp;#39;s type registry
(t/get-reg)
;;=&amp;gt;
&amp;#39;{ 
   ;; primitives
	 :nil #{nil},
	 :num #{java.lang.Number},
	 :fun #{clojure.lang.Fn},
	 :lst #{clojure.lang.ISeq},
	 :vec #{clojure.lang.IPersistentVector},
	 :key #{clojure.lang.Keyword},
	 :sym #{clojure.lang.Symbol},
	 :str #{java.lang.String},
	 :link #{clojure.lang.MapEntry},
	 :set #{clojure.lang.IPersistentSet},
	 :map #{clojure.lang.PersistentArrayMap clojure.lang.PersistentHashMap},
	
	 ;; aggregates
	 :line #{:lst :vec},
	 :word #{:key :sym :str},
	 :hash #{:set :map},
	 :atom #{:num :fun :key :sym :str :link},
	 :coll #{:lst :vec :set :map},
	 :prim #{:num :fun :lst :vec :key :sym :str :link :nil :set :map}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 104b4884-ab9c-4011-a756-1c9d9c9d5103--&gt;
&lt;!-- begin paragraph 8b234ea7-3e04-427a-b3a6-ca0490f8e0dd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8b234ea7-3e04-427a-b3a6-ca0490f8e0dd&quot;&gt;To see the clojurescript type registry:&lt;/p&gt;
&lt;!-- end paragraph 8b234ea7-3e04-427a-b3a6-ca0490f8e0dd--&gt;
&lt;!-- begin code 0d48168b-4cb0-4c19-9ae4-1ebc2aefedde--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0d48168b-4cb0-4c19-9ae4-1ebc2aefedde&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(state/targeting-cljs (t/get-reg))
;;=&amp;gt;
 &amp;#39;{:nil #{nil}
   :num #{number},
   :fun #{function},
   :key #{Keyword},
   :sym #{Symbol},
   :str #{string},
   :link #{MapEntry},
   :set #{PersistentTreeSet PersistentHashSet},
   :map #{ObjMap PersistentHashMap PersistentTreeMap PersistentArrayMap},
   :vec #{MapEntry BlackNode Subvec RedNode PersistentVector},
   :lst #{IndexedSeq LazySeq PersistentTreeMapSeq NodeSeq PersistentArrayMapSeq ES6IteratorSeq ChunkedSeq 
          Cons Iterate RSeq ArrayNodeSeq Cycle ChunkedCons ValSeq Repeat PersistentQueueSeq EmptyList 
          PersistentQueue Range KeySeq List},
   
  :hash #{:set :map},
  :coll #{:lst :vec :set :map},
  :line #{:lst :vec},
  :word #{:key :sym :str},
  :atom #{:num :fun :key :sym :str :link},
  :prim #{:num :fun :lst :vec :key :sym :str :link :nil :set :map}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0d48168b-4cb0-4c19-9ae4-1ebc2aefedde--&gt;
&lt;!-- begin paragraph c488f905-2f25-441d-ad40-e97616b92168--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c488f905-2f25-441d-ad40-e97616b92168&quot;&gt;Those type registry are certainly not complete but I think that&amp;#39;s enough to see the point.&lt;/p&gt;
&lt;!-- end paragraph c488f905-2f25-441d-ad40-e97616b92168--&gt;
&lt;!-- begin paragraph ad236d94-51d1-4e1c-a169-93528add0a5a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ad236d94-51d1-4e1c-a169-93528add0a5a&quot;&gt;Along with the definitions of those registries, the &lt;code&gt;glycogen.types&lt;/code&gt; namespace is defining some handy functions and macros to play with types :&lt;/p&gt;
&lt;!-- end paragraph ad236d94-51d1-4e1c-a169-93528add0a5a--&gt;
&lt;!-- begin code 6725acdf-90e2-4c1d-9d3a-4b24ca4e55e9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6725acdf-90e2-4c1d-9d3a-4b24ca4e55e9&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; basic hierarchy informations

(t/childs :coll)
;; is getting all children of a type =&amp;gt;
&amp;#39;[:lst
 :vec
 :set
 :map
 clojure.lang.ISeq
 clojure.lang.PersistentArrayMap
 clojure.lang.IPersistentSet
 clojure.lang.PersistentHashMap
 clojure.lang.IPersistentVector]

(t/parents :fun)
;; is getting all parents of a type=&amp;gt;
&amp;#39;(:atom :prim)

(t/classes :coll)
;; is getting all classes the type is belonging to =&amp;gt;
&amp;#39;(clojure.lang.ISeq
  clojure.lang.IPersistentVector
  clojure.lang.IPersistentSet
  clojure.lang.PersistentArrayMap
  clojure.lang.PersistentHashMap)

;; set litterals can be used to denotes union types
(t/childs #{:lst :vec})
;; returns all children for types :lst and :vec =&amp;gt;
&amp;#39;(clojure.lang.ISeq clojure.lang.IPersistentVector)

(t/childof :vec :coll) ;=&amp;gt; :vec (indicating success)

(t/parentof #{:str :sym} :sym) ;=&amp;gt; #{:sym :str}

(t/parentof :word #{:str :sym}) ;=&amp;gt; :word

(t/parentof :word #{:str :vec}) ;=&amp;gt; false

;; isa

(t/isa :num 1) ;;=&amp;gt; 1

(t/isa :coll []) ;;=&amp;gt; []

;; glycogen.types/isa is a macro, the below call expands to a fast typecheck :

((clojure.core/fn
  [G__4117]
  (clojure.core/when
    (clojure.core/or
      (clojure.core/seq? G__4117)
      (clojure.core/vector? G__4117)
      (clojure.core/set? G__4117)
      (clojure.core/map? G__4117))
    G__4117))
[])

;; you can use set notation here to

(t/isa #{:sym :line} &amp;#39;aze) ;=&amp;gt; &amp;#39;aze
(t/isa #{:sym :line} (list 1 2 3))) ;=&amp;gt; &amp;#39;(1 2 3)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6725acdf-90e2-4c1d-9d3a-4b24ca4e55e9--&gt;
&lt;!-- begin paragraph 3502cf0f-3275-46dc-b28c-ed5cee209a22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3502cf0f-3275-46dc-b28c-ed5cee209a22&quot;&gt;The &lt;code&gt;glycogen.types&lt;/code&gt; namespace is Clojure only, so you may wonder about Clojurescript... in fact it is not intended to expose anything at runtime in Clojurescript, it serves only at compile time when a macro is expanded, depending on the targeted platform, the type registry of Clojure or Clojurescript is used.&lt;/p&gt;
&lt;!-- end paragraph 3502cf0f-3275-46dc-b28c-ed5cee209a22--&gt;
&lt;!-- begin heading_2 1bd7efe5-f592-45bc-96d6-4b6909e5e8e5--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-1bd7efe5-f592-45bc-96d6-4b6909e5e8e5&quot;&gt;Usage&lt;/h3&gt;
&lt;!-- end heading_2 1bd7efe5-f592-45bc-96d6-4b6909e5e8e5--&gt;
&lt;!-- begin paragraph 0788e9fc-a8c7-474c-a467-e2aba0f98522--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0788e9fc-a8c7-474c-a467-e2aba0f98522&quot;&gt;Now we&amp;#39;ve seen how &lt;code&gt;glycogen.types&lt;/code&gt; works we will return to our initial intent, which is to be able to define generic functions in a more concise and powerful way.&lt;/p&gt;
&lt;!-- end paragraph 0788e9fc-a8c7-474c-a467-e2aba0f98522--&gt;
&lt;!-- begin paragraph 3943c11e-0873-4084-935e-9b86e7ce06c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3943c11e-0873-4084-935e-9b86e7ce06c0&quot;&gt;Let&amp;#39;s recap the points in which protocols declarations and implementations could be improved:&lt;/p&gt;
&lt;!-- end paragraph 3943c11e-0873-4084-935e-9b86e7ce06c0--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Be able to share implementations&lt;/li&gt;&lt;li&gt;Support for variadic arity &lt;/li&gt;&lt;li&gt;Avoiding to write reader conditionals&lt;/li&gt;&lt;li&gt;Type less, grasp the intent faster&lt;/li&gt;&lt;li&gt;Be able to clone an existing generic fonction&lt;/li&gt;&lt;li&gt;Partial implementation&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph b6096d21-44d4-4bd9-acb2-9451ad8ad203--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b6096d21-44d4-4bd9-acb2-9451ad8ad203&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b6096d21-44d4-4bd9-acb2-9451ad8ad203--&gt;
&lt;!-- begin paragraph 2304f5c3-1b85-4d2d-87bd-6ad4d3d7a88d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2304f5c3-1b85-4d2d-87bd-6ad4d3d7a88d&quot;&gt;Let&amp;#39;s first import the library:&lt;/p&gt;
&lt;!-- end paragraph 2304f5c3-1b85-4d2d-87bd-6ad4d3d7a88d--&gt;
&lt;!-- begin code da011e54-0670-42da-a4c7-984b269271a9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-da011e54-0670-42da-a4c7-984b269271a9&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(ns glycogen.demo
  (#?(:clj :require :cljs :require-macros)
   [glycogen.generics :as g]))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code da011e54-0670-42da-a4c7-984b269271a9--&gt;
&lt;!-- begin paragraph 84354678-7e56-4133-bd2e-7741cd17fd30--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-84354678-7e56-4133-bd2e-7741cd17fd30&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 84354678-7e56-4133-bd2e-7741cd17fd30--&gt;
&lt;!-- begin heading_3 c580c288-0384-4327-b0cd-ab06d0efa78b--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-c580c288-0384-4327-b0cd-ab06d0efa78b&quot;&gt;Declaration &lt;/h4&gt;
&lt;!-- end heading_3 c580c288-0384-4327-b0cd-ab06d0efa78b--&gt;
&lt;!-- begin code f873c9da-d870-44a4-8d1a-23f16fdb2f2a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f873c9da-d870-44a4-8d1a-23f16fdb2f2a&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/defg fmap 
        &amp;quot;apply one or several function to something&amp;quot;
        ([this f])
        ([this f &amp;amp; fs]))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f873c9da-d870-44a4-8d1a-23f16fdb2f2a--&gt;
&lt;!-- begin paragraph 2b2d206d-4a6c-436f-ae33-ced7038d3fc6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2b2d206d-4a6c-436f-ae33-ced7038d3fc6&quot;&gt;Here we are declaring a &lt;code&gt;fmap&lt;/code&gt; generic function with a docstring a fixed arity and a variadic one.&lt;/p&gt;
&lt;!-- end paragraph 2b2d206d-4a6c-436f-ae33-ced7038d3fc6--&gt;
&lt;!-- begin paragraph 90aecbf7-a899-45e8-8867-566fe01259e3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-90aecbf7-a899-45e8-8867-566fe01259e3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 90aecbf7-a899-45e8-8867-566fe01259e3--&gt;
&lt;!-- begin heading_3 bd54c082-3375-4ec3-90e2-08ec375297d1--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-bd54c082-3375-4ec3-90e2-08ec375297d1&quot;&gt;Extension&lt;/h4&gt;
&lt;!-- end heading_3 bd54c082-3375-4ec3-90e2-08ec375297d1--&gt;
&lt;!-- begin code 30013c56-ac26-4913-9c60-38771327232a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-30013c56-ac26-4913-9c60-38771327232a&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/generic+ fmap

  ([x f] ;; arity 2

   ;; variadic arity
   :vec (mapv f x)

   ;; set literal can be used to implement several types at once
   #{:set :map} (into (empty x) (map f x))

   ;; the default case (if x does not implement fmap)
   (f x))



  ([x f &amp;amp; fs] ;; variadic arity

   ;; we have only a default case here
   (reduce fmap x (cons f fs))))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 30013c56-ac26-4913-9c60-38771327232a--&gt;
&lt;!-- begin paragraph 5abc5867-45ee-4317-96bb-d2ba4b16765d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5abc5867-45ee-4317-96bb-d2ba4b16765d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5abc5867-45ee-4317-96bb-d2ba4b16765d--&gt;
&lt;!-- begin heading_3 f38cae79-87cb-48a2-b728-b69980037390--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-f38cae79-87cb-48a2-b728-b69980037390&quot;&gt;All at once &lt;/h4&gt;
&lt;!-- end heading_3 f38cae79-87cb-48a2-b728-b69980037390--&gt;
&lt;!-- begin code 8c752015-10b8-498e-8e2f-25f8ae4968b3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8c752015-10b8-498e-8e2f-25f8ae4968b3&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/defg fmap

  &amp;quot;apply one or several function to something&amp;quot;

  ([x f]
   :vec (mapv f x)
   #{:set :map} (into (empty x) (map f x))
   (f x))

  ([x f &amp;amp; fs]
   (reduce fmap x (cons f fs))))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8c752015-10b8-498e-8e2f-25f8ae4968b3--&gt;
&lt;!-- begin paragraph a4aa2246-e3c5-4ec0-8ccc-5dfee61370c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a4aa2246-e3c5-4ec0-8ccc-5dfee61370c0&quot;&gt;So in this exemple we have addressed several points &lt;/p&gt;
&lt;!-- end paragraph a4aa2246-e3c5-4ec0-8ccc-5dfee61370c0--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;variadic arity &lt;/li&gt;&lt;li&gt;code deduplication &lt;/li&gt;&lt;li&gt;hiding target platform details&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph c9f7c8c3-db87-45ef-a73d-45b78f509f92--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c9f7c8c3-db87-45ef-a73d-45b78f509f92&quot;&gt;Let&amp;#39;s try &lt;code&gt;fmap&lt;/code&gt;:&lt;/p&gt;
&lt;!-- end paragraph c9f7c8c3-db87-45ef-a73d-45b78f509f92--&gt;
&lt;!-- begin code 9704ea28-636b-4ad2-a235-2d8ded3a0e10--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9704ea28-636b-4ad2-a235-2d8ded3a0e10&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(is (fmap [1 2 3] inc)
    [2 3 4])

(is (fmap {:a 1 :b 2} (comp vec reverse))
    {1 :a, 2 :b})

;; variadic arity
(is (fmap [1 2 3] inc inc)
    [3 4 5])&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9704ea28-636b-4ad2-a235-2d8ded3a0e10--&gt;
&lt;!-- begin paragraph 609b75e7-de4f-406a-b2ca-0ce2d03535cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-609b75e7-de4f-406a-b2ca-0ce2d03535cc&quot;&gt;One other thing that is I think a little annoying in clojure is that each implementer have to define all arities of the implemented generic. with glycogen&amp;#39;s generics it is not mandatory, in the below exemple, the variadic arity has only a default case, it will be the implementation used for any type that implement only the arity 2 (in this case).&lt;/p&gt;
&lt;!-- end paragraph 609b75e7-de4f-406a-b2ca-0ce2d03535cc--&gt;
&lt;!-- begin paragraph 2331a1d1-6eeb-4994-b70d-a372f9359f0e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2331a1d1-6eeb-4994-b70d-a372f9359f0e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2331a1d1-6eeb-4994-b70d-a372f9359f0e--&gt;
&lt;!-- begin paragraph 655ed908-8438-4cc2-8e79-b9640dec09b5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-655ed908-8438-4cc2-8e79-b9640dec09b5&quot;&gt;Let&amp;#39;s add an implementation of &lt;code&gt;fmap&lt;/code&gt;for lists.&lt;/p&gt;
&lt;!-- end paragraph 655ed908-8438-4cc2-8e79-b9640dec09b5--&gt;
&lt;!-- begin paragraph fb1b819a-02d2-42df-a8d3-37f5e8da7aeb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fb1b819a-02d2-42df-a8d3-37f5e8da7aeb&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fb1b819a-02d2-42df-a8d3-37f5e8da7aeb--&gt;
&lt;!-- begin code c24091c3-b590-425c-bdad-bbecd1013775--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c24091c3-b590-425c-bdad-bbecd1013775&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/generic+ fmap [x f]
  :lst (map f x))

(is (fmap (range 4) inc)
    &amp;#39;(1 2 3 4))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c24091c3-b590-425c-bdad-bbecd1013775--&gt;
&lt;!-- begin paragraph 5c50dd15-42e6-4392-a933-e95b225df491--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5c50dd15-42e6-4392-a933-e95b225df491&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5c50dd15-42e6-4392-a933-e95b225df491--&gt;
&lt;!-- begin paragraph 27773ba2-1544-4c20-b47d-021255723aaa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27773ba2-1544-4c20-b47d-021255723aaa&quot;&gt;In clojure we would have been forced to give implementation for every arities of &lt;code&gt;fmap&lt;/code&gt;. Here it is not the case, we can verify it by trying to use the variadic arity of &lt;code&gt;fmap&lt;/code&gt;on a list.&lt;/p&gt;
&lt;!-- end paragraph 27773ba2-1544-4c20-b47d-021255723aaa--&gt;
&lt;!-- begin code 3d6f4be2-bce7-4764-ba32-622ee32d237d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3d6f4be2-bce7-4764-ba32-622ee32d237d&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(is (fmap (range 4) inc inc)
    &amp;#39;(2 3 4 5))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3d6f4be2-bce7-4764-ba32-622ee32d237d--&gt;
&lt;!-- begin paragraph f66a847d-b14a-4978-9e92-2bc0e21efa39--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f66a847d-b14a-4978-9e92-2bc0e21efa39&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph f66a847d-b14a-4978-9e92-2bc0e21efa39--&gt;
&lt;!-- begin paragraph d87adc8c-e188-4e0c-817e-47de5bea7c88--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d87adc8c-e188-4e0c-817e-47de5bea7c88&quot;&gt;We can even only overide the variadic arity if needed:&lt;/p&gt;
&lt;!-- end paragraph d87adc8c-e188-4e0c-817e-47de5bea7c88--&gt;
&lt;!-- begin code ffa5c224-9ecd-4dce-b59b-3bdb0ba40907--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ffa5c224-9ecd-4dce-b59b-3bdb0ba40907&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/generic+ fmap [x f &amp;amp; fs]
  :lst
  (do (println &amp;quot;smart variadic fmap&amp;quot;)
      (fmap x (apply comp (reverse (cons f fs))))))

(is (fmap (range 4) inc inc)
    &amp;#39;(2 3 4 5)) ;; prints &amp;quot;smart variadic fmap&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ffa5c224-9ecd-4dce-b59b-3bdb0ba40907--&gt;
&lt;!-- begin heading_2 3cb75d4a-98a8-4b89-aa4a-33b61660eb53--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3cb75d4a-98a8-4b89-aa4a-33b61660eb53&quot;&gt;Related operations&lt;/h3&gt;
&lt;!-- end heading_2 3cb75d4a-98a8-4b89-aa4a-33b61660eb53--&gt;
&lt;!-- begin paragraph fa5b9353-987a-4232-baf2-7493a51f2aa0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa5b9353-987a-4232-baf2-7493a51f2aa0&quot;&gt;In addition to &lt;code&gt;def-protocol&lt;/code&gt; and &lt;code&gt;extend-protocol&lt;/code&gt;, in clojure we have related operations like &lt;code&gt;extend-type&lt;/code&gt;, &lt;code&gt;defrecord&lt;/code&gt;, &lt;code&gt;reify&lt;/code&gt; etc...&lt;/p&gt;
&lt;!-- end paragraph fa5b9353-987a-4232-baf2-7493a51f2aa0--&gt;
&lt;!-- begin heading_3 4b4a9b39-c8fa-4537-8d7a-89d0aa1575f6--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-4b4a9b39-c8fa-4537-8d7a-89d0aa1575f6&quot;&gt;&lt;code&gt;thing&lt;/code&gt;&lt;/h4&gt;
&lt;!-- end heading_3 4b4a9b39-c8fa-4537-8d7a-89d0aa1575f6--&gt;
&lt;!-- begin paragraph 8d93d6dd-5423-4306-9d37-8360d1d40e4f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d93d6dd-5423-4306-9d37-8360d1d40e4f&quot;&gt;In clojure and clojurescript we have &lt;code&gt;reify&lt;/code&gt; that creates an anonymous class that implements some protocols. Here we can do roughly the same with &lt;code&gt;glycogen.generics/thing&lt;/code&gt;.&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph 8d93d6dd-5423-4306-9d37-8360d1d40e4f--&gt;
&lt;!-- begin code 71ea5cb5-933e-4e86-8a95-d37f3f75178d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-71ea5cb5-933e-4e86-8a95-d37f3f75178d&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(let [mything
      ;; we are creating an anonymous class that implement 
      ;; the previously defined generics in a really dummy way
      (g/thing (fmap [x f] [:fmaped x f])
               (plus [x y] [:plused x y]))]
  ;; checks
  (is (plus mything 1)
      [:plused mything 1])
  (is (fmap mything inc)
      [:fmaped mything inc]))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 71ea5cb5-933e-4e86-8a95-d37f3f75178d--&gt;
&lt;!-- begin heading_3 be913d11-f073-47c3-ad0d-7ce16460f3a1--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-be913d11-f073-47c3-ad0d-7ce16460f3a1&quot;&gt;&lt;code&gt;fork&lt;/code&gt;&lt;/h4&gt;
&lt;!-- end heading_3 be913d11-f073-47c3-ad0d-7ce16460f3a1--&gt;
&lt;!-- begin paragraph d6ecdb31-6800-4dfe-a95d-a16fad5ffd78--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d6ecdb31-6800-4dfe-a95d-a16fad5ffd78&quot;&gt;One other thing that may be useful sometimes is the ability to stole a generic from somewhere else and build a new one by overriding some parts of the original one (without altering the original one and the code that depends on it). For this we have the &lt;code&gt;glycogen.generics/fork&lt;/code&gt; operator.&lt;/p&gt;
&lt;!-- end paragraph d6ecdb31-6800-4dfe-a95d-a16fad5ffd78--&gt;
&lt;!-- begin code 733c457e-beff-4abc-98df-fbaf416d09e4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-733c457e-beff-4abc-98df-fbaf416d09e4&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/fork fmap ;; the generic that we are cloning/forking
        tweaked-fmap ;; the name that will hold the copy
        ;; it takes the same body format as defg or generic+
        ;; here we are only overiding the arity 2 implementation for vectors
        [x f]
        :vec (do (println &amp;quot;tweaked fmap&amp;quot;) (mapv f x)))

(tweaked-fmap [1 2 3] inc) ;; printing &amp;quot;tweaked fmap&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 733c457e-beff-4abc-98df-fbaf416d09e4--&gt;
&lt;!-- begin paragraph 0f140b12-fdb0-4c53-8d39-84998ec92cb9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0f140b12-fdb0-4c53-8d39-84998ec92cb9&quot;&gt;The &lt;code&gt;tweaked-fmap&lt;/code&gt; generic now exists on its own and is completly hermetic to &lt;code&gt;fmap&lt;/code&gt; further changes/extensions.&lt;/p&gt;
&lt;!-- end paragraph 0f140b12-fdb0-4c53-8d39-84998ec92cb9--&gt;
&lt;!-- begin heading_3 5142ae3f-137a-48f4-9bb5-cedfe1fde980--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-5142ae3f-137a-48f4-9bb5-cedfe1fde980&quot;&gt;&lt;code&gt;type+&lt;/code&gt;&lt;/h4&gt;
&lt;!-- end heading_3 5142ae3f-137a-48f4-9bb5-cedfe1fde980--&gt;
&lt;!-- begin paragraph 8fd9dce5-e711-4171-b40c-7c4e9d0592f7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8fd9dce5-e711-4171-b40c-7c4e9d0592f7&quot;&gt;There is something similar to &lt;code&gt;extend-type&lt;/code&gt; and its name is &lt;code&gt;glycogen.generics/type+&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 8fd9dce5-e711-4171-b40c-7c4e9d0592f7--&gt;
&lt;!-- begin paragraph 5ae5092c-5506-491f-9f19-2e88c744b309--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ae5092c-5506-491f-9f19-2e88c744b309&quot;&gt;Here we are extending the type &lt;code&gt;:num&lt;/code&gt; to our previously defined generics (&lt;code&gt;plus&lt;/code&gt; and &lt;code&gt;fmap&lt;/code&gt;)&lt;/p&gt;
&lt;!-- end paragraph 5ae5092c-5506-491f-9f19-2e88c744b309--&gt;
&lt;!-- begin code 7325fe2a-97ba-4a25-87ff-223943e1d0bb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7325fe2a-97ba-4a25-87ff-223943e1d0bb&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/type+ :num
         (fmap [x f] (println &amp;quot;fmaping num&amp;quot;) (f x))
         (plus [x y] (+ x y)))

(is (with-out-str
      (is (fmap 1 inc)
          2))
    &amp;quot;fmaping num\n&amp;quot;)

(is (plus 1 2)
    3)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7325fe2a-97ba-4a25-87ff-223943e1d0bb--&gt;
&lt;!-- begin heading_3 2516624d-5799-49b6-8ba7-e262703e0a79--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-2516624d-5799-49b6-8ba7-e262703e0a79&quot;&gt;&lt;code&gt;deft&lt;/code&gt;&lt;/h4&gt;
&lt;!-- end heading_3 2516624d-5799-49b6-8ba7-e262703e0a79--&gt;
&lt;!-- begin paragraph 68c1f8bd-82c7-4bc8-9458-8921541b82ec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-68c1f8bd-82c7-4bc8-9458-8921541b82ec&quot;&gt;One thing we are still missing is the ability to introduce new types the way &lt;code&gt;deftype&lt;/code&gt; or &lt;code&gt;defrecord&lt;/code&gt; do it.&lt;/p&gt;
&lt;!-- end paragraph 68c1f8bd-82c7-4bc8-9458-8921541b82ec--&gt;
&lt;!-- begin paragraph cb6931b7-890d-4159-a4d8-fde9372f3ab1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cb6931b7-890d-4159-a4d8-fde9372f3ab1&quot;&gt;The &lt;code&gt;deft&lt;/code&gt; macro is similar to &lt;code&gt;defrecord&lt;/code&gt; but let you implement generics.&lt;/p&gt;
&lt;!-- end paragraph cb6931b7-890d-4159-a4d8-fde9372f3ab1--&gt;
&lt;!-- begin paragraph 1ec10023-1493-4d6d-8f5a-16564c102309--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1ec10023-1493-4d6d-8f5a-16564c102309&quot;&gt;We will define a &lt;code&gt;:pair&lt;/code&gt; type holding two fields &lt;code&gt;car&lt;/code&gt; and &lt;code&gt;cdr&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 1ec10023-1493-4d6d-8f5a-16564c102309--&gt;
&lt;!-- begin paragraph 1cf4f54b-c07e-4fe2-a862-22c64bb463b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1cf4f54b-c07e-4fe2-a862-22c64bb463b7&quot;&gt;Along with defining a new record, the &lt;code&gt;deft&lt;/code&gt; macro defines some useful functions to work with your type: &lt;/p&gt;
&lt;!-- end paragraph 1cf4f54b-c07e-4fe2-a862-22c64bb463b7--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;a constructor function (here &lt;code&gt;pair&lt;/code&gt;) &lt;/li&gt;&lt;li&gt;a casting generic function (here &lt;code&gt;→pair&lt;/code&gt;) that can be implemented by other types in order to cast into the defined type.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 1743055a-8e21-4883-af73-5a4898566a73--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1743055a-8e21-4883-af73-5a4898566a73&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1743055a-8e21-4883-af73-5a4898566a73--&gt;
&lt;!-- begin code e9c423c5-337a-407d-9e0b-5a3e0611dd63--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e9c423c5-337a-407d-9e0b-5a3e0611dd63&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/deft :pair ;; the type tag
        [car cdr] ;; the fields
        ;; generics implementations
        ;; note that the constructor function is available (pair)
        (plus [_ y] (pair car (if cdr (plus cdr y) y)))
        (fmap [_ f] (pair (f car) (when cdr (fmap cdr f)))))

(defn lst [&amp;amp; xs]
  (reduce (fn [p x] (pair x p))
          nil (reverse xs)))

(is (fmap (plus (lst 1 2 3) (lst 4 5 6))
          inc)
    (lst 2 3 4 5 6 7))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e9c423c5-337a-407d-9e0b-5a3e0611dd63--&gt;
&lt;!-- begin paragraph a5c923e0-fd74-46f6-8aa8-5103416bbce8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a5c923e0-fd74-46f6-8aa8-5103416bbce8&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a5c923e0-fd74-46f6-8aa8-5103416bbce8--&gt;
&lt;!-- begin heading_2 ac65a8ad-570d-400a-9f6f-5005fb4b1c92--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ac65a8ad-570d-400a-9f6f-5005fb4b1c92&quot;&gt;more about &lt;code&gt;defg&lt;/code&gt;&lt;/h3&gt;
&lt;!-- end heading_2 ac65a8ad-570d-400a-9f6f-5005fb4b1c92--&gt;
&lt;!-- begin heading_3 1a15658e-8bb8-47b2-98bf-1277f41f9f9d--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-1a15658e-8bb8-47b2-98bf-1277f41f9f9d&quot;&gt;precedence&lt;/h4&gt;
&lt;!-- end heading_3 1a15658e-8bb8-47b2-98bf-1277f41f9f9d--&gt;
&lt;!-- begin paragraph 8870ceab-f0d0-49f4-b6de-152031812b83--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8870ceab-f0d0-49f4-b6de-152031812b83&quot;&gt;The order of implementations matters, the semantics are similar to clojure/cond, the first implementation have priority on the laters.&lt;/p&gt;
&lt;!-- end paragraph 8870ceab-f0d0-49f4-b6de-152031812b83--&gt;
&lt;!-- begin paragraph 5c02f106-41a2-47d1-9b58-1b579fa8c2ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5c02f106-41a2-47d1-9b58-1b579fa8c2ed&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5c02f106-41a2-47d1-9b58-1b579fa8c2ed--&gt;
&lt;!-- begin code 9aaab0a8-465f-4223-9019-fe836fc1cf48--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9aaab0a8-465f-4223-9019-fe836fc1cf48&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/defg whoami [x]
        :vec &amp;quot;I&amp;#39;m a vector&amp;quot;
        :coll &amp;quot;I&amp;#39;m a collection&amp;quot;)

(is (whoami [1 2])
    &amp;quot;I&amp;#39;m a vector&amp;quot;)

(is (whoami (list 1 2))
    &amp;quot;I&amp;#39;m a collection&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9aaab0a8-465f-4223-9019-fe836fc1cf48--&gt;
&lt;!-- begin heading_3 a2b87a21-b280-40b1-8823-3fc42dd1d15d--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-a2b87a21-b280-40b1-8823-3fc42dd1d15d&quot;&gt;bindings&lt;/h4&gt;
&lt;!-- end heading_3 a2b87a21-b280-40b1-8823-3fc42dd1d15d--&gt;
&lt;!-- begin paragraph 904783c9-4d27-407b-96c6-7c6a4a5294fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-904783c9-4d27-407b-96c6-7c6a4a5294fb&quot;&gt;As mentioned previously, sharing the binding pattern accross all the implementations of an arity is not always what we want. But in fact you can provides several times the same arity with different binding patterns. &lt;/p&gt;
&lt;!-- end paragraph 904783c9-4d27-407b-96c6-7c6a4a5294fb--&gt;
&lt;!-- begin paragraph cd322c6d-b398-4d38-827b-627a13aea81d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd322c6d-b398-4d38-827b-627a13aea81d&quot;&gt;To demonstrate this we will extend the &lt;code&gt;-&amp;gt;pair&lt;/code&gt; generic that convert something to a pair (and has been automatically declared by the &lt;code&gt;deft&lt;/code&gt; exemple above).&lt;/p&gt;
&lt;!-- end paragraph cd322c6d-b398-4d38-827b-627a13aea81d--&gt;
&lt;!-- begin code be8e353a-ceb1-427a-8c7e-14103851760a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-be8e353a-ceb1-427a-8c7e-14103851760a&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(g/generic+ -&amp;gt;pair 

      ([[car &amp;amp; cdr]] 
       :coll (pair car cdr))

      ([x] 
       :pair x 
       (pair x nil)))

(is (-&amp;gt;pair [1 2 3])
    (-&amp;gt;pair (list 1 2 3))
    (pair 1 (list 2 3)))

(is (-&amp;gt;pair 1)
    (pair 1 nil))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code be8e353a-ceb1-427a-8c7e-14103851760a--&gt;
&lt;!-- begin heading_2 596e5f44-55fa-4f2d-8cf7-b6dcf3327608--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-596e5f44-55fa-4f2d-8cf7-b6dcf3327608&quot;&gt;Under the hood&lt;/h3&gt;
&lt;!-- end heading_2 596e5f44-55fa-4f2d-8cf7-b6dcf3327608--&gt;
&lt;!-- begin paragraph 54f18f23-ac2d-4fe5-b555-157f353d41dc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-54f18f23-ac2d-4fe5-b555-157f353d41dc&quot;&gt;In order for all of this to work we cannot map directly to clojure&amp;#39;s protocol, I mean that in fact when defining a polyarity generic several protocols are defined, one for each arity and one for the variadic arity. &lt;/p&gt;
&lt;!-- end paragraph 54f18f23-ac2d-4fe5-b555-157f353d41dc--&gt;
&lt;!-- begin paragraph 7f2cfbb9-1e59-4233-b83d-489c0a8bfafb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7f2cfbb9-1e59-4233-b83d-489c0a8bfafb&quot;&gt;Let&amp;#39;s take a look at the macro expansion of &lt;code&gt;defg&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 7f2cfbb9-1e59-4233-b83d-489c0a8bfafb--&gt;
&lt;!-- begin code d89fc343-fee4-41f4-97f9-08654f51c9b2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d89fc343-fee4-41f4-97f9-08654f51c9b2&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&amp;#39;(do
   ;; first are doing some var cleaning, if the defined 
   ;; generic already exists we are removing related vars (a common case in dev)
  (do
    (clojure.core/ns-unmap (quote glycogen.article) (quote plus))
    (clojure.core/ns-unmap (quote glycogen.article) (quote p_plus_3))
    (clojure.core/ns-unmap (quote glycogen.article) (quote Iplus_3))
    (clojure.core/ns-unmap (quote glycogen.article) (quote p_plus_2))
    (clojure.core/ns-unmap (quote glycogen.article) (quote Iplus_2)))
   
   ;; for each arity of the defined generic we are defining a protocol
  (do
    ;; the arity 3 is holding our variadic arity
    (clojure.core/defprotocol glycogen.article/Iplus_3 (p_plus_3 [a_5712 a_5713 a_5714]))
    (clojure.core/defprotocol glycogen.article/Iplus_2 (p_plus_2 [a_5715 a_5716])))
   
   ;; we are wrapping all this in a function that will be the user calling interface
  (clojure.core/defn
    plus
    ;; the arity 2 is simply wrapping the arity 2 protocol
    ([a_5715 a_5716] (p_plus_2 a_5715 a_5716))
    ;; the variadic arity wraps the rest argument and uses the arity 3 protocol
    ([a_5712 a_5713 &amp;amp; a_5714] (p_plus_3 a_5712 a_5713 a_5714)))
   
   ;; for each implementation we are defining a var, it can serves several purposes, 
   ;; one is to easily implement copying of generics across namespaces
   ;; one other is to be able to inline some implementations (in some compiler context, no-one wants to see those wierd names in code)
  (do
    (do
      (clojure.core/ns-unmap (quote glycogen.article) (quote plus_2_IMPL_lst))
      (clojure.core/defn plus_2_IMPL_lst ([x y] (concat x y))))
    (do
      (clojure.core/ns-unmap (quote glycogen.article) (quote plus_2_IMPL_vec))
      (clojure.core/defn plus_2_IMPL_vec ([x y] (into x y))))
    (do
      (clojure.core/ns-unmap (quote glycogen.article) (quote plus_2_IMPL_map))
      (clojure.core/defn plus_2_IMPL_map ([x y] (merge x y))))
    
    ;; note that a default case that throw a &amp;quot;missing implementation&amp;quot; 
    ;; error is automatically defined when no default case in given by the user
    (do
      (clojure.core/ns-unmap (quote glycogen.article) (quote plus_2_IMPL_any))
      (clojure.core/defn
        plus_2_IMPL_any
        ([x y]
         (glycogen.prelude/error
           &amp;quot;missing implementation for generic: &amp;quot;
           (quote plus)
           &amp;quot;\npattern:\n&amp;quot;
           (quote [x y])
           &amp;quot;\nwhere:\n&amp;quot;
           {(quote x) x, (quote y) y}))))
    (do
      (clojure.core/ns-unmap (quote glycogen.article) (quote plus_3_IMPL_any))
      (clojure.core/defn plus_3_IMPL_any ([x y ys] (reduce plus (plus x y) ys)))))
   
   ;; then we are actually extending defined protocols (in clojurescript extend-type is used)
  (do
    (clojure.core/extend clojure.lang.ISeq glycogen.article/Iplus_2 {:p_plus_2 plus_2_IMPL_lst})
    (clojure.core/extend clojure.lang.IPersistentVector glycogen.article/Iplus_2 {:p_plus_2 plus_2_IMPL_vec})
    (clojure.core/extend clojure.lang.PersistentArrayMap glycogen.article/Iplus_2 {:p_plus_2 plus_2_IMPL_map})
    (clojure.core/extend clojure.lang.PersistentHashMap glycogen.article/Iplus_2 {:p_plus_2 plus_2_IMPL_map})
    (clojure.core/extend Object glycogen.article/Iplus_2 {:p_plus_2 plus_2_IMPL_any})
    (clojure.core/extend Object glycogen.article/Iplus_3 {:p_plus_3 plus_3_IMPL_any}))
   
   ;; finally we are returning the main function 
  plus)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d89fc343-fee4-41f4-97f9-08654f51c9b2--&gt;
&lt;!-- begin heading_2 f147e1e4-df90-41f4-86ec-3e674b31231c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f147e1e4-df90-41f4-86ec-3e674b31231c&quot;&gt;Further ideas&lt;/h3&gt;
&lt;!-- end heading_2 f147e1e4-df90-41f4-86ec-3e674b31231c--&gt;
&lt;!-- begin paragraph 9a12de06-95af-4a4d-b0ce-40dd7295258a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9a12de06-95af-4a4d-b0ce-40dd7295258a&quot;&gt;Generic function should maybe being able to be anonymous and used as lambdas are. For exemple:&lt;/p&gt;
&lt;!-- end paragraph 9a12de06-95af-4a4d-b0ce-40dd7295258a--&gt;
&lt;!-- begin paragraph 75f1030c-4666-4a60-9c33-2f6d99487033--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75f1030c-4666-4a60-9c33-2f6d99487033&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 75f1030c-4666-4a60-9c33-2f6d99487033--&gt;
&lt;!-- begin code b6532990-edc7-4163-ad4e-7a7697ec3c5c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b6532990-edc7-4163-ad4e-7a7697ec3c5c&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;(let [f (fg [x] 
            :coll [:coll x] 
            :atom [:atom x]
            [:any x])]
  (is (f {:a 1})
      [:coll {:a 1}])
  (is (f 1)
      [:atom 1]))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b6532990-edc7-4163-ad4e-7a7697ec3c5c--&gt;
&lt;!-- begin paragraph ab3bf105-97f0-46b7-bb27-959983e86d17--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ab3bf105-97f0-46b7-bb27-959983e86d17&quot;&gt;But it brings several question to the table&lt;/p&gt;
&lt;!-- end paragraph ab3bf105-97f0-46b7-bb27-959983e86d17--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;how can we simulate closure behavior? is it even possible?&lt;/li&gt;&lt;li&gt;memory management&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:8f0f824a3ac04b8c9eebcc26179c267b</id>
    <title>SBT pour centraliser et homogénéiser la déclaration de services</title>
    <updated>2020-06-03T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/sbt-pour-centraliser-et-homogeneiser-la-declaration-de-services.html"/>
    <!--summary Utiliser SBT pour déclarer simplement les composants d&#039;un pipeline de services Kafka Streams et les configurer automatiquement-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="SBT"></category>    <category term="Microservice"></category>    <category term="Scala"></category>    <category term="Kafka"></category>    <content type="html">
&lt;!-- begin paragraph f44eb04e-48cd-468d-a880-2fde1937a129--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f44eb04e-48cd-468d-a880-2fde1937a129&quot;&gt;Si dans un projet, vous vous retrouvez à gérer plusieurs services, il peut être intéressant de centraliser la configuration (ou du moins la configuration par défaut) de ces services sur des paramètres comme le port d&amp;#39;écoute HTTP, les endpoints d&amp;#39;API Rest ou, pour des applications Kafka, le nom des topics communicants entre les services. L&amp;#39;idée est d&amp;#39;éviter des collisions (comme pour le port d&amp;#39;écoute HTTP) ou de taper à côté (comme pour les topics Kafka) et de s’éviter des heures de debugging sur des erreurs futiles s’étendant à un seul caractère. Si vous avez en plus un moyen à la fois concis et homogène de configurer vos services et mettre en avant leur interdépendance, vous pouvez rapidement analyser la structure du projet.&lt;/p&gt;
&lt;!-- end paragraph f44eb04e-48cd-468d-a880-2fde1937a129--&gt;
&lt;!-- begin paragraph d09eada7-b528-49cc-831f-a1f0056ac1bf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d09eada7-b528-49cc-831f-a1f0056ac1bf&quot;&gt;Dans ce cas, pourquoi ne pas utiliser l&amp;#39;outil de build pour mettre en place cette centralisation. Nous allons voir ça avec SBT.&lt;/p&gt;
&lt;!-- end paragraph d09eada7-b528-49cc-831f-a1f0056ac1bf--&gt;
&lt;!-- begin heading_1 ffe35449-3e63-4b80-ae89-5878ca511998--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ffe35449-3e63-4b80-ae89-5878ca511998&quot;&gt;Quelques mots sur SBT&lt;/h2&gt;
&lt;!-- end heading_1 ffe35449-3e63-4b80-ae89-5878ca511998--&gt;
&lt;!-- begin paragraph f7a229e3-700b-4065-92b1-e2747550cdd9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f7a229e3-700b-4065-92b1-e2747550cdd9&quot;&gt;SBT, le Maven du monde Scala, est un outil qui n&amp;#39;est pas facile à maîtriser. Il a cependant des capacités de personnalisation qui sont assez importantes.&lt;/p&gt;
&lt;!-- end paragraph f7a229e3-700b-4065-92b1-e2747550cdd9--&gt;
&lt;!-- begin paragraph eae9995f-8e93-4235-8459-00d2f0dd32b4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eae9995f-8e93-4235-8459-00d2f0dd32b4&quot;&gt;En effet, la gestion d&amp;#39;un projet sous SBT ne se base pas sur un fichier de description (XML, JSON, YAML...), mais sur des fichiers utilisant le langage Scala. Ceci permet d’utiliser toute la capacité du langage pour mettre en place un DSL (Domain Specific Language) et obtenir un style de déclaration de projet adapté à la définition de nos services.&lt;/p&gt;
&lt;!-- end paragraph eae9995f-8e93-4235-8459-00d2f0dd32b4--&gt;
&lt;!-- begin heading_1 6912618c-f12f-45a3-913f-783a271fcc19--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6912618c-f12f-45a3-913f-783a271fcc19&quot;&gt;Déclarer des services Kafka Streams&lt;/h2&gt;
&lt;!-- end heading_1 6912618c-f12f-45a3-913f-783a271fcc19--&gt;
&lt;!-- begin paragraph a0ee6c8a-876e-4e52-a9de-3f6cc8d243a2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a0ee6c8a-876e-4e52-a9de-3f6cc8d243a2&quot;&gt;L’idée est d’avoir l’approche la plus déclarative pour décrire nos services. Ce qui veut dire que notre fichier de build doit permettre de comprendre très rapidement quelle est la structure du projet, quels sont les services, comment est-ce qu’ils interagissent entre eux et comment les monitorer.&lt;/p&gt;
&lt;!-- end paragraph a0ee6c8a-876e-4e52-a9de-3f6cc8d243a2--&gt;
&lt;!-- begin paragraph 7a0f4491-3d27-46d1-8d69-12f9e6311897--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7a0f4491-3d27-46d1-8d69-12f9e6311897&quot;&gt;Pour ça, nous allons créer dans le répertoire &lt;code&gt;project/&lt;/code&gt; le fichier &lt;code&gt;BuildHelper.scala&lt;/code&gt; et allons y créer une fonction qui permet de déclarer des sous-modules représentant des services.&lt;/p&gt;
&lt;!-- end paragraph 7a0f4491-3d27-46d1-8d69-12f9e6311897--&gt;
&lt;!-- begin paragraph 0b3f2149-244d-4f73-aac4-22099196ae45--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0b3f2149-244d-4f73-aac4-22099196ae45&quot;&gt;Voici la signature de la fonction &lt;code&gt;serviceProject&lt;/code&gt; pour déclarer des services Kafka Streams avec une API Rest de monitoring, destinés à composer notre pipeline de traitement.&lt;/p&gt;
&lt;!-- end paragraph 0b3f2149-244d-4f73-aac4-22099196ae45--&gt;
&lt;!-- begin code 5f8f5c99-075c-4570-869a-a8f9f1fbeb63--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5f8f5c99-075c-4570-869a-a8f9f1fbeb63&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import sbt._
import sbt.Keys

object BuildHelper {

  def serviceProject(
      serviceName: String,
      port: Int,
      inputStream: Option[String] = None,
      outputStream: Option[String] = None
  ): Project = ???

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5f8f5c99-075c-4570-869a-a8f9f1fbeb63--&gt;
&lt;!-- begin paragraph df1f7b21-3ad8-49ed-a946-e41d55461768--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-df1f7b21-3ad8-49ed-a946-e41d55461768&quot;&gt;La structure du projet est ainsi plus visible. Avec une section déclarant en un seul lieu les topics disponibles, nous évitons des erreurs de nom ,par exemple , le nom des topics a changé plusieurs fois et qu’on a oublié de propager la modification dans certains services. Nous pouvons aussi utiliser des variables pour mettre en valeur la dépendance en terme de topic entre les services. Sur un autre aspect, les collisions de port d’écoute HTTP sont plus aisés à détecter.&lt;/p&gt;
&lt;!-- end paragraph df1f7b21-3ad8-49ed-a946-e41d55461768--&gt;
&lt;!-- begin paragraph f9711827-f1cc-4e13-b8d1-913325fa8d26--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9711827-f1cc-4e13-b8d1-913325fa8d26&quot;&gt;Ainsi, les services sont plus homogènes et leur déclaration est plus simple à écrire et à lire.&lt;/p&gt;
&lt;!-- end paragraph f9711827-f1cc-4e13-b8d1-913325fa8d26--&gt;
&lt;!-- begin code 7b21f825-5093-4f71-a78e-5d331f1e75a7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7b21f825-5093-4f71-a78e-5d331f1e75a7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import BuildHelper._

lazy val root =
  (project in file(&amp;quot;.&amp;quot;))
  .aggregate(serviceCommon, serviceSerde, serviceIngest, serviceProcess, serviceExport)

// ---- TOPIC NAMES ----------------

val streams = new {
  val data        = &amp;quot;data-stream&amp;quot;
  val event       = &amp;quot;event-stream&amp;quot;
  val information = &amp;quot;information-stream&amp;quot;
}

// ---- SERVICE DECLARATION ----------------

lazy val serviceIngest =
  serviceProject(
    serviceName  = &amp;quot;service-ingest&amp;quot;,
    port         = 10001,
    inputStream  = Some(streams.data),
    outputStream = Some(streams.event)
  )
  .settings(commonSettings)
  .dependsOn(serviceCommon, serviceSerde)

lazy val serviceProcess =
  serviceProject(
    serviceName  = &amp;quot;service-process&amp;quot;,
    port         = 10002,
    inputStream  = Some(streams.event),
    outputStream = Some(streams.information)
  )
  .settings(commonSettings)
  .dependsOn(serviceCommon, serviceSerde)

lazy val serviceExport =
  serviceProject(
    serviceName  = &amp;quot;service-export&amp;quot;,
    port         = 10003,
    inputStream  = Some(streams.information),
    outputStream = None
  )
  .settings(commonSettings)
  .dependsOn(serviceCommon, serviceSerde)

// ---- ADDITIONAL SUBMODULES ----------------

lazy val serviceCommon =
  (project in file(&amp;quot;service-common&amp;quot;)) // ...

lazy val serviceSerde =
  (project in file(&amp;quot;service-serde&amp;quot;)) // ...

// ---- COMMONS ----------------

// settings necessary for the current pipeline
lazy val commonSettings =
  Def.settings(/* ... */)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7b21f825-5093-4f71-a78e-5d331f1e75a7--&gt;
&lt;!-- begin paragraph d79c9f95-9fc7-409d-8770-3bcb750c5eaa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d79c9f95-9fc7-409d-8770-3bcb750c5eaa&quot;&gt;Ce qui correspond au diagramme suivant.&lt;/p&gt;
&lt;!-- end paragraph d79c9f95-9fc7-409d-8770-3bcb750c5eaa--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://www.plantuml.com/plantuml/img/RP4nRiCm34LtdOBmr0u1d7OlidSfq3KBuVoaGYpA9UaWnsyiMQY7BABuG3rzGGTpLf_J63pw2FgA4yOee4y-Nvc-NS79WbsZJAoSA6N9mu_c17AT4SBlZ1dKdTXvPsMy8pPQ-g0OdENoy7O6lSK1HuboWVbHNlPzt-zVot1bJOKRnBUk0fxA9iq3p5RRMxMjiEcYdBCczfXbAztWfshSfwmjvtirLnR2yZZQVRJSjIoW9YthqhOaQwr7_g_TxClXQZz0JeyF-GC0&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 81e908f8-b29c-49b8-9019-456d3729a9a8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81e908f8-b29c-49b8-9019-456d3729a9a8&quot;&gt;SBT permet la génération de fichier comme tout bon outil de build et ça vaut pour les fichiers de configuration. En se basant sur la déclaration vue précédemment, on peut générer automatiquement une configuration par défaut. En utilisant un format de fichier de configuration comme HOCON, il est possible de surcharger cette configuration par défaut. Voici un exemple de configuration générée automatiquement à partir de la déclaration vue avant.&lt;/p&gt;
&lt;!-- end paragraph 81e908f8-b29c-49b8-9019-456d3729a9a8--&gt;
&lt;!-- begin code 660f469f-1836-4af7-ab72-dafeee1c9d10--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-660f469f-1836-4af7-ab72-dafeee1c9d10&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;service {
  name = &amp;quot;service-ingest&amp;quot;
  http {
    port = 10001
  }
  kafka {
    bootstrapServers  = &amp;quot;localhost:9092&amp;quot;
    schemaRegistryUrl = &amp;quot;localhost:8081&amp;quot;
  }
}
topics {
  inputStream  = &amp;quot;data-stream&amp;quot;
  outputStream = &amp;quot;event-stream&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 660f469f-1836-4af7-ab72-dafeee1c9d10--&gt;
&lt;!-- begin paragraph 8a6fe7bd-ec8a-4d7a-a452-bacebff6ebb9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8a6fe7bd-ec8a-4d7a-a452-bacebff6ebb9&quot;&gt;Par contre, contrairement à la génération automatique de code source qui se déclenche lors de l&amp;#39;étape de compilation, la génération automatique de fichier de ressource se déclenche lors du &lt;i&gt;run&lt;/i&gt; du service ou à l&amp;#39;étape &lt;i&gt;package&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 8a6fe7bd-ec8a-4d7a-a452-bacebff6ebb9--&gt;
&lt;!-- begin paragraph 10269c32-cb15-4164-905a-f0375c437635--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-10269c32-cb15-4164-905a-f0375c437635&quot;&gt;Pour intégrer cette configuration et éventuellement la surcharger, à supposer que le fichier se trouve dans &lt;code&gt;target/scala-*/resource_managed/main/service-generated/application-generated.conf&lt;/code&gt;, votre &lt;code&gt;application.conf&lt;/code&gt; ressemblera à&lt;/p&gt;
&lt;!-- end paragraph 10269c32-cb15-4164-905a-f0375c437635--&gt;
&lt;!-- begin code 1034841b-77e6-491d-a01e-cc89d69d8439--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1034841b-77e6-491d-a01e-cc89d69d8439&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;include classpath(&amp;quot;service-generated/application-generated.conf&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1034841b-77e6-491d-a01e-cc89d69d8439--&gt;
&lt;!-- begin heading_1 9719aeb1-f034-4a3a-859e-9c12e5f1ffbf--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9719aeb1-f034-4a3a-859e-9c12e5f1ffbf&quot;&gt;Implémentation&lt;/h2&gt;
&lt;!-- end heading_1 9719aeb1-f034-4a3a-859e-9c12e5f1ffbf--&gt;
&lt;!-- begin paragraph 9e5c58a1-b5a5-4a39-9b4a-06bbcf508356--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e5c58a1-b5a5-4a39-9b4a-06bbcf508356&quot;&gt;Nous allons tout d&amp;#39;abord inclure le plugin sbt-native-packager, afin d&amp;#39;avoir un exemple avec un plugin à configurer pour chaque service. On peut aussi faire de même avec d&amp;#39;autres plugins comme sbt-buildinfo.&lt;/p&gt;
&lt;!-- end paragraph 9e5c58a1-b5a5-4a39-9b4a-06bbcf508356--&gt;
&lt;!-- begin code e9ebd296-851b-46c3-9177-af78604f0d6c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e9ebd296-851b-46c3-9177-af78604f0d6c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;addSbtPlugin(&amp;quot;com.typesafe.sbt&amp;quot; % &amp;quot;sbt-native-packager&amp;quot; % &amp;quot;1.7.2&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e9ebd296-851b-46c3-9177-af78604f0d6c--&gt;
&lt;!-- begin paragraph 5aa61395-e7ef-4d28-9a70-e549f4c4785b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5aa61395-e7ef-4d28-9a70-e549f4c4785b&quot;&gt;Ci-dessous, nous avons l&amp;#39;implémentation de &lt;code&gt;serviceProject&lt;/code&gt;. Cette fonction assure différentes tâches :&lt;/p&gt;
&lt;!-- end paragraph 5aa61395-e7ef-4d28-9a70-e549f4c4785b--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;La déclaration du sous-module&lt;/li&gt;&lt;li&gt;La déclaration du répertoire du sous-module&lt;/li&gt;&lt;li&gt;La déclaration de l&amp;#39;identifiant du sous-module (utilisé pour la navigation entre sous-modules dans le CLI de SBT)&lt;/li&gt;&lt;li&gt;La déclaration du nom du projet&lt;/li&gt;&lt;li&gt;La déclaration de la &lt;i&gt;main class&lt;/i&gt;&lt;/li&gt;&lt;li&gt;L&amp;#39;ajout d&amp;#39;une tâche dans la génération automatique des fichiers de ressources pour générer la configuration par défaut du service, en suivant &lt;a href=&quot;https://www.scala-sbt.org/1.x/docs/Howto-Generating-Files.html#Generate+resources&quot;&gt;l&amp;#39;approche recommandée dans SBT&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 524c5d2c-d8bb-4cdb-8961-b52f2f579de1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-524c5d2c-d8bb-4cdb-8961-b52f2f579de1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  def serviceProject(
      serviceName: String,
      port: Int,
      inputStream: Option[String] = None,
      outputStream: Option[String] = None
  ): Project = {
    val nameParts        = serviceName.split(&amp;quot;-&amp;quot;)
    val projectId        = nameParts.head + nameParts.tail.map(_.capitalize).mkString
    val packageName      = s&amp;quot;io.univalence.service.${nameParts.last}&amp;quot;
    def serviceMainClass = s&amp;quot;Service${nameParts.last.capitalize}Main&amp;quot;

    Project(projectId, new File(serviceName))
      .enablePlugins(JavaAppPackaging)
      // basic settings required by all services
      // (set anything like scalac options, dependencies, test configuration...)
      .settings(serviceSettings)
      .settings(
        name                 := serviceName,
        mainClass in Compile := Some(s&amp;quot;$packageName.$serviceMainClass&amp;quot;)
      )
      .settings(
        // add resource generation task for the default config of the service
        Compile / resourceGenerators += Def.task {
          // get path of the generated file
          val confFile = (Compile / resourceManaged).value / &amp;quot;service-generated&amp;quot; / &amp;quot;application-generated.conf&amp;quot;

          val files =
            generateServiceConfig(
              confFile     = confFile,
              serviceName  = serviceName,
              defaultPort  = port,
              inputStream  = inputStream,
              outputStream = outputStream
            )

          streams.value.log.info(s&amp;quot;generated $files&amp;quot;)

          files
        }.taskValue
      )
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 524c5d2c-d8bb-4cdb-8961-b52f2f579de1--&gt;
&lt;!-- begin paragraph 4f32624c-c8b4-4c06-81c4-e054460d55f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4f32624c-c8b4-4c06-81c4-e054460d55f6&quot;&gt;La génération de la configuration par défaut va se baser sur une fonction qui utilise un template. J&amp;#39;utilise ensuite la fonction &lt;code&gt;IO.write&lt;/code&gt; pour écrire dans le fichier. La fonction doit renvoyer un &lt;code&gt;Seq[File]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 4f32624c-c8b4-4c06-81c4-e054460d55f6--&gt;
&lt;!-- begin code 69254327-2308-4f37-bd90-3393716186dc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-69254327-2308-4f37-bd90-3393716186dc&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;  def generateServiceConfig(
      confFile: File,
      serviceName: String,
      defaultPort: Int,
      inputStream: Option[String],
      outputStream: Option[String]
  ): Seq[File] = {
    val isParam = inputStream.map(is =&amp;gt; s&amp;quot;&amp;quot;&amp;quot;inputStream  = &amp;quot;$is&amp;quot;&amp;quot;&amp;quot;&amp;quot;).getOrElse(&amp;quot;&amp;quot;)
    val osParam = outputStream.map(os =&amp;gt; s&amp;quot;&amp;quot;&amp;quot;outputStream = &amp;quot;$os&amp;quot;&amp;quot;&amp;quot;&amp;quot;).getOrElse(&amp;quot;&amp;quot;)

    val content =
      s&amp;quot;&amp;quot;&amp;quot;service {
       |  name = &amp;quot;$serviceName&amp;quot;
       |  http {
       |    port = $defaultPort
       |  }
       |  kafka {
       |    bootstrapServers  = &amp;quot;localhost:9092&amp;quot;
       |    schemaRegistryUrl = &amp;quot;localhost:8081&amp;quot;
       |  }
       |}
       |topics {
       |  $isParam
       |  $osParam
       |}
       |&amp;quot;&amp;quot;&amp;quot;.stripMargin

    IO.write(confFile, content)

    Seq(confFile)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 69254327-2308-4f37-bd90-3393716186dc--&gt;
&lt;!-- begin heading_1 8d60072b-7086-44a7-b416-f69079f57893--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-8d60072b-7086-44a7-b416-f69079f57893&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 8d60072b-7086-44a7-b416-f69079f57893--&gt;
&lt;!-- begin paragraph 8fca9e37-35d5-4153-aea1-b6c818d692c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8fca9e37-35d5-4153-aea1-b6c818d692c3&quot;&gt;SBT permet d’utiliser les capacités du langage Scala pour mettre en place un DSL adapté à la déclaration des services qui composent notre projet. Un tel DSL doit permettre de comprendre rapidement la structure du projet et de détecter les dépendances entre ses composants en les centralisant. L’implémentation d’un tel DSL n’est vraiment compliqué mais nécessite de connaître le fonctionnement de SBT. La documentation de SBT est à ce titre nécessaire et assez bien fournie.&lt;/p&gt;
&lt;!-- end paragraph 8fca9e37-35d5-4153-aea1-b6c818d692c3--&gt;
&lt;!-- begin paragraph 71cb892b-53a8-4ad6-97f1-e412f02a2972--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71cb892b-53a8-4ad6-97f1-e412f02a2972&quot;&gt;Photographie par Steve Harvey sur Unsplash&lt;/p&gt;
&lt;!-- end paragraph 71cb892b-53a8-4ad6-97f1-e412f02a2972--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:a4bd1101dc4e44d8a3db3a2d831a1a60</id>
    <title>Des microservices Kafka Streams avec ZIO et http4s</title>
    <updated>2020-05-06T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/des-microservices-kafka-streams-avec-zio-et-http4s.html"/>
    <!--summary -->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="ZIO"></category>    <category term="Kafka"></category>    <category term="Kafka Streams"></category>    <category term="http4s"></category>    <category term="Programmation fonctionnelle"></category>    <content type="html">
&lt;!-- begin paragraph 6c9b7628-2a97-42e9-8319-3f1edf221ab0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6c9b7628-2a97-42e9-8319-3f1edf221ab0&quot;&gt;Kafka Streams propose depuis la version 0.10.0.0 de Kafka un paradigme intéressant pour traiter des données au fil de l&amp;#39;eau. Comme son nom l&amp;#39;indique, il se base déjà sur Kafka qui propose une plateforme de traitement en streaming parmi les plus performantes actuellement (bon, il y a aussi Pulsar). Et puis Kafka Streams propose une API avec des opérations que nous retrouvons fréquemment en programmation fonctionnelle (map, flatMap, reduce...). Enfin, Kafka Streams nous pousse à nous orienter vers une architecture micro-services. Tant de buzzwords :)&lt;/p&gt;
&lt;!-- end paragraph 6c9b7628-2a97-42e9-8319-3f1edf221ab0--&gt;
&lt;!-- begin paragraph 5fc2b95e-6567-44f3-9142-e67e61fd96bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5fc2b95e-6567-44f3-9142-e67e61fd96bb&quot;&gt;Mouep ! Ça ne vous empêchera pas nécessairement d&amp;#39;avoir un réseau de &lt;i&gt;micro-sévices&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 5fc2b95e-6567-44f3-9142-e67e61fd96bb--&gt;
&lt;!-- begin paragraph 19d6e254-139b-4835-82cd-06d85ed14ff2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-19d6e254-139b-4835-82cd-06d85ed14ff2&quot;&gt;Il est en effet important de savoir dans quel état est chaque service, de surveiller leur activité. Une façon de faire est de coller à Kafka Streams un service Web qui expose une API Rest de monitoring.&lt;/p&gt;
&lt;!-- end paragraph 19d6e254-139b-4835-82cd-06d85ed14ff2--&gt;
&lt;!-- begin paragraph c36ac855-ab5d-4ad2-bf5f-0deb1a5bd241--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c36ac855-ab5d-4ad2-bf5f-0deb1a5bd241&quot;&gt;Que proposent les outils de la programmation fonctionnelle pour faire ça ?&lt;/p&gt;
&lt;!-- end paragraph c36ac855-ab5d-4ad2-bf5f-0deb1a5bd241--&gt;
&lt;!-- begin paragraph 4e3b71b3-22df-40ab-a4bd-b8f2974b138f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e3b71b3-22df-40ab-a4bd-b8f2974b138f&quot;&gt;Ici, je vous propose de tester une approche basée sur ZIO et http4s.&lt;/p&gt;
&lt;!-- end paragraph 4e3b71b3-22df-40ab-a4bd-b8f2974b138f--&gt;
&lt;!-- begin heading_1 6682ba3e-edcf-4897-b542-b386ad25ac36--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6682ba3e-edcf-4897-b542-b386ad25ac36&quot;&gt;Un microservice Kafka Streams&lt;/h2&gt;
&lt;!-- end heading_1 6682ba3e-edcf-4897-b542-b386ad25ac36--&gt;
&lt;!-- begin paragraph cb5056e0-d867-46a3-8208-6362cba585fc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cb5056e0-d867-46a3-8208-6362cba585fc&quot;&gt;Nous allons nous baser sur un des &amp;quot;hello world&amp;quot; des services de streaming : le découpage de lignes de texte en mots. Nous recevons des lignes de texte depuis un premier topic &amp;quot;text-stream&amp;quot;, nous découpons ces lignes en mots et nous émettons ces mots sur un second topic &amp;quot;word-stream&amp;quot;.&lt;/p&gt;
&lt;!-- end paragraph cb5056e0-d867-46a3-8208-6362cba585fc--&gt;
&lt;!-- begin paragraph 03a151de-8297-4ee7-b5f5-18acc476893a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-03a151de-8297-4ee7-b5f5-18acc476893a&quot;&gt;Le &lt;code&gt;main&lt;/code&gt; d&amp;#39;un service Kafka Streams ressemble à peu près à ça.&lt;/p&gt;
&lt;!-- end paragraph 03a151de-8297-4ee7-b5f5-18acc476893a--&gt;
&lt;!-- begin code 48fc4664-575b-4a32-9abd-284e5c7914f1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-48fc4664-575b-4a32-9abd-284e5c7914f1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val inputStream = &amp;quot;text-stream&amp;quot;
val outputStream = &amp;quot;word-stream&amp;quot;

// -- Definition of the topology
val topology: Topology = {
  import org.apache.kafka.streams.scala.ImplicitConversions._
  import org.apache.kafka.streams.scala.Serdes._
  import org.apache.kafka.streams.scala.StreamsBuilder

  val builder = new StreamBuilder()

  builder.stream[String, String](inputStream)
      // our algorithme
      .flatMapValues(line =&amp;gt;
        line.split(&amp;quot;[\\s.,;:!?]+&amp;quot;).toIterable)  // cut cut cut... 🔪
    .to(outputStream)

  builder.build()
}

// -- Kafka Streams parameters
val properties = {
  import StreamsConfig._
  
  JavaProperties(
      APPLICATION_ID_CONFIG -&amp;gt; &amp;quot;mon-service&amp;quot;,
      BOOTSTRAP_SERVERS_CONFIG -&amp;gt; &amp;quot;localhost:9092&amp;quot;,
      DEFAULT_KEY_SERDE_CLASS_CONFIG -&amp;gt; Serdes.String().getClass,
      DEFAULT_VALUE_SERDE_CLASS_CONFIG -&amp;gt; Serdes.String().getClass
    )
}

// -- Kafka Streams service
val streams = new KafkaStreams(topology, properties)

streams.start()

sys.ShutdownHookThread { stream.close() }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 48fc4664-575b-4a32-9abd-284e5c7914f1--&gt;
&lt;!-- begin paragraph 200bddf0-e9c5-49a7-87e0-fc1d8f5f4f1f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-200bddf0-e9c5-49a7-87e0-fc1d8f5f4f1f&quot;&gt;&lt;code&gt;JavaProperties&lt;/code&gt; est une petite astuce pour produire des &lt;code&gt;java.lang.Properties&lt;/code&gt; en Scala avec moins de boilerplate 😁&lt;/p&gt;
&lt;!-- end paragraph 200bddf0-e9c5-49a7-87e0-fc1d8f5f4f1f--&gt;
&lt;!-- begin code 8ea673fe-636c-41be-8602-ecea44f3bd10--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8ea673fe-636c-41be-8602-ecea44f3bd10&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import java.util.Properties

object JavaProperties {
  import scala.jdk.CollectionConverters._ // scala 2.13

  def apply(properties: (String, AnyRef)*): Properties = {
    val prop = new Properties()
    prop.putAll(properties.toMap.asJava)

    prop
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8ea673fe-636c-41be-8602-ecea44f3bd10--&gt;
&lt;!-- begin paragraph a16294b2-644a-4181-b409-ec6c06e81c73--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a16294b2-644a-4181-b409-ec6c06e81c73&quot;&gt;Pour information, la topologie de notre service ressemble à (&lt;a href=&quot;https://zz85.github.io/kafka-streams-viz/#VG9wb2xvZ2llczoKICAgU3ViLXRvcG9sb2d5OiAwCiAgICBTb3VyY2U6IEtTVFJFQU0tU09VUkNFLTAwMDAwMDAwMDAgKHRvcGljczogW3RleHQtc3RyZWFtXSkKICAgICAgLS0+IEtTVFJFQU0tRkxBVE1BUFZBTFVFUy0wMDAwMDAwMDAxCiAgICBQcm9jZXNzb3I6IEtTVFJFQU0tRkxBVE1BUFZBTFVFUy0wMDAwMDAwMDAxIChzdG9yZXM6IFtdKQogICAgICAtLT4gS1NUUkVBTS1TSU5LLTAwMDAwMDAwMDIKICAgICAgPC0tIEtTVFJFQU0tU09VUkNFLTAwMDAwMDAwMDAKICAgIFNpbms6IEtTVFJFQU0tU0lOSy0wMDAwMDAwMDAyICh0b3BpYzogd29yZC1zdHJlYW0pCiAgICAgIDwtLSBLU1RSRUFNLUZMQVRNQVBWQUxVRVMtMDAwMDAwMDAwMQoK&quot;&gt;lien&lt;/a&gt;)&lt;/p&gt;
&lt;!-- end paragraph a16294b2-644a-4181-b409-ec6c06e81c73--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/a4bd1101-dc4e-44d8-a3db-3a2d831a1a60/topology.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph f50c697c-44cd-44d8-b46a-a5f47b499382--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f50c697c-44cd-44d8-b46a-a5f47b499382&quot;&gt;Pour tester ce service, il faudra d&amp;#39;abord créer les topics. Vous pourrez alors lancer le service. Puis vous instancierez un &lt;i&gt;consumer&lt;/i&gt; pour récupérer les données depuis le topic &amp;quot;word-stream&amp;quot; et instancierez un &lt;i&gt;producer&lt;/i&gt; pour émettre des lignes de texte sur le topic &amp;quot;text-stream&amp;quot;. Kafka propose ses propres outils pour ça. Sinon, vous pouvez toujours jeter un œil à &lt;a href=&quot;https://www.conduktor.io/&quot;&gt;Conduktor&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph f50c697c-44cd-44d8-b46a-a5f47b499382--&gt;
&lt;!-- begin heading_1 b10a3d12-6e28-4b54-ac83-e444e0a981c4--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-b10a3d12-6e28-4b54-ac83-e444e0a981c4&quot;&gt;Kafka Streams et ZIO&lt;/h2&gt;
&lt;!-- end heading_1 b10a3d12-6e28-4b54-ac83-e444e0a981c4--&gt;
&lt;!-- begin paragraph d23797ad-2edb-4043-9ac0-8cd2bdd0a7f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d23797ad-2edb-4043-9ac0-8cd2bdd0a7f0&quot;&gt;Peut-on avoir un code plus fonctionnellement pure ?&lt;/p&gt;
&lt;!-- end paragraph d23797ad-2edb-4043-9ac0-8cd2bdd0a7f0--&gt;
&lt;!-- begin paragraph 81555f0c-94fc-47e6-bc2f-bc004e2a61cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81555f0c-94fc-47e6-bc2f-bc004e2a61cc&quot;&gt;Ici, nous allons lier l&amp;#39;exécution de notre application avec l&amp;#39;exécution du service Kafka Streams... et plus encore !&lt;/p&gt;
&lt;!-- end paragraph 81555f0c-94fc-47e6-bc2f-bc004e2a61cc--&gt;
&lt;!-- begin paragraph d981291d-5a80-45e3-8c51-badf058360ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d981291d-5a80-45e3-8c51-badf058360ee&quot;&gt;En première étape, nous allons encapsuler Kafka Streams avec ZIO, car d&amp;#39;un point de vue de ZIO, Kafka Streams n&amp;#39;est pas purement fonctionnelle.&lt;/p&gt;
&lt;!-- end paragraph d981291d-5a80-45e3-8c51-badf058360ee--&gt;
&lt;!-- begin paragraph 8ba05fa6-383c-49c5-a66b-93c861752169--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8ba05fa6-383c-49c5-a66b-93c861752169&quot;&gt;Voici une classe abstraite qui nous permettra d&amp;#39;arriver à nos fins.&lt;/p&gt;
&lt;!-- end paragraph 8ba05fa6-383c-49c5-a66b-93c861752169--&gt;
&lt;!-- begin code 3465f96a-117d-40e3-b3a8-16939be97ada--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3465f96a-117d-40e3-b3a8-16939be97ada&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio.{RIO, Task, UIO, ZIO}

/**
  * Wrap a mutable dependency into a ZIO context.
  *
  * @param a reference to the mutable dependency
  * @tparam A mutable dependency type
  */
abstract class WrapMutable[A](private val a: A) {

  final protected def executeTotal[B](f: A =&amp;gt; B): UIO[B] = UIO(f(a))

  final protected def executeTotalM[R, E, B](
    f: A =&amp;gt; ZIO[R, E, B]
  ): ZIO[R, E, B] = f(a)

  final protected def unsafeTotal[B](f: A =&amp;gt; B): B = f(a)

  final def execute[B](f: A =&amp;gt; B): Task[B] = Task(f(a))

  final def executeM[R, B](f: A =&amp;gt; RIO[R, B]): RIO[R, B] = Task(f(a)).flatten

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3465f96a-117d-40e3-b3a8-16939be97ada--&gt;
&lt;!-- begin paragraph 03619b80-cefa-4cc7-88c7-00a660d7cfed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-03619b80-cefa-4cc7-88c7-00a660d7cfed&quot;&gt;Cette classe permet d&amp;#39;encapsuler d&amp;#39;autres classes ou services qui ne sont pas référentiellement transparents (voir &lt;a href=&quot;https://blog.univalence.io/transparence-referentielle-i-la-perte-de-la-predictibilite/&quot;&gt;1&lt;/a&gt; et &lt;a href=&quot;https://blog.univalence.io/transparence-referentielle-ii-the-rise-of-non-strict-evaluation/&quot;&gt;2&lt;/a&gt; pour plus d&amp;#39;explication sur ce terme). Les appels sont aussi encapsulés dans des instances de ZIO. Ainsi, pour un service &lt;code&gt;myService&lt;/code&gt; avec une méthode &lt;code&gt;perform&lt;/code&gt;, au lieu de faire &lt;code&gt;myService.perform&lt;/code&gt;, je vais faire &lt;code&gt;val zMyService = new WrapMutable(myService) {}; zMyService.execute(_.perform)&lt;/code&gt; pour gérer avec ZIO l&amp;#39;appel de la méthode.&lt;/p&gt;
&lt;!-- end paragraph 03619b80-cefa-4cc7-88c7-00a660d7cfed--&gt;
&lt;!-- begin paragraph 2eb4a997-90c8-46c7-a1a0-b8b44caab85a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2eb4a997-90c8-46c7-a1a0-b8b44caab85a&quot;&gt;Voici une utilisation de WrapMutable dans le cadre Kafka Streams, dans laquelle je lui crée une surcouche ZIO.&lt;/p&gt;
&lt;!-- end paragraph 2eb4a997-90c8-46c7-a1a0-b8b44caab85a--&gt;
&lt;!-- begin code 1e95dcc5-72b5-41d6-8b47-41dd797efe7e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1e95dcc5-72b5-41d6-8b47-41dd797efe7e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import java.util.concurrent.TimeUnit
import org.apache.kafka.streams.{KafkaStreams, TopologyDescription}
import zio.duration.Duration
import zio.{UIO, ZEnv, ZIO}

class ZKafkaStreams(streams: KafkaStreams,
                    stateCheckingDelay: Duration)
    extends WrapMutable[KafkaStreams](streams) {

  /**
    * The main function that launch the service
    */
  def serve: ZIO[Clock, Throwable, Unit] = 
    (cleanup *&amp;gt; start *&amp;gt; waitWhileIsUp).ensuring(close).unit

  // this part execute a check loop that the Kafka Streams is still up
  def waitWhileIsUp:URIO[Clock,Unit] =
    isUp.repeat {
      import zio.Schedule._
      doWhile[Boolean](x =&amp;gt; x) &amp;gt;&amp;gt;&amp;gt; fixed(Duration(500, TimeUnit.MILLISECONDS))
    }.unit

  def isUp:UIO[Boolean] = status.map(s =&amp;gt; s.isRunning || s == KafkaStreams.State.CREATED)

  //REMAP
  def cleanUp:Task[Unit]              = execute(_.cleanUp())
  def start:Task[Unit]                = execute(_.start)
  def close:UIO[Unit]                 = executeTotal(_.close)
  def status: UIO[KafkaStreams.State] = executeTotal(_.state())

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1e95dcc5-72b5-41d6-8b47-41dd797efe7e--&gt;
&lt;!-- begin paragraph 5f6efedf-16e6-4c4c-b16d-e474594b42ab--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f6efedf-16e6-4c4c-b16d-e474594b42ab&quot;&gt;L&amp;#39;opérateur &lt;code&gt;*&amp;gt;&lt;/code&gt; permet de séquencer des appels (implémenté avec un &lt;code&gt;flatMap&lt;/code&gt;). L&amp;#39;exécution du service Kafka Streams est commandé par la méthode &lt;code&gt;serve&lt;/code&gt;. Une méthode &lt;code&gt;status&lt;/code&gt; est ajoutée à des fins de monitoring comme nous le verrons plus loin.&lt;/p&gt;
&lt;!-- end paragraph 5f6efedf-16e6-4c4c-b16d-e474594b42ab--&gt;
&lt;!-- begin paragraph 7ea36cf2-72a2-495e-8477-d7657091bbdc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7ea36cf2-72a2-495e-8477-d7657091bbdc&quot;&gt;Il ne reste plus qu&amp;#39;à créer notre application ZIO.&lt;/p&gt;
&lt;!-- end paragraph 7ea36cf2-72a2-495e-8477-d7657091bbdc--&gt;
&lt;!-- begin code 537917e1-5a2e-45b3-916a-849b09a8daef--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-537917e1-5a2e-45b3-916a-849b09a8daef&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio.ZIO

object LineProcessingService extends zio.App {

  val topology: Topology = { /* ... (see above) */ }

  val properties: Properties = { /* ... (see above) */ }

  lazy val zKafkaStream: ZKafkaStreams =
    new ZKafkaStreams(
      streams = new KafkaStreams(topology, properties),
    )

  override def run(args: List[String]): ZIO[zio.ZEnv, Nothing, Int] =
    zKafkaStream.serve
      .as(0)
      .orDie

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 537917e1-5a2e-45b3-916a-849b09a8daef--&gt;
&lt;!-- begin paragraph 2bfb3bf5-5e93-4eeb-b30b-00d5be8a9bee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2bfb3bf5-5e93-4eeb-b30b-00d5be8a9bee&quot;&gt;Sur les dernières lignes, j&amp;#39;indique que la sortie nominale de notre application retourne la valeur 0 à l&amp;#39;OS (&lt;code&gt;.as(0)&lt;/code&gt;) ou la valeur 1 en cas de problème (&lt;code&gt;.orDie&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph 2bfb3bf5-5e93-4eeb-b30b-00d5be8a9bee--&gt;
&lt;!-- begin paragraph 6624f255-1e13-40be-96be-2b09f64ef0c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6624f255-1e13-40be-96be-2b09f64ef0c3&quot;&gt;OK, j&amp;#39;ai mon service embarqué dans ZIO. Mais je n&amp;#39;ai pas moyen de savoir si celui-ci fonctionne normalement si je suis en dehors de la machine hôte.&lt;/p&gt;
&lt;!-- end paragraph 6624f255-1e13-40be-96be-2b09f64ef0c3--&gt;
&lt;!-- begin heading_1 7a3f9126-c961-4464-b02d-6796523ff27a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-7a3f9126-c961-4464-b02d-6796523ff27a&quot;&gt;Émettre des donnés de monitoring depuis son service&lt;/h2&gt;
&lt;!-- end heading_1 7a3f9126-c961-4464-b02d-6796523ff27a--&gt;
&lt;!-- begin paragraph 9ac19f9f-e809-45a1-92b0-04b640079c38--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9ac19f9f-e809-45a1-92b0-04b640079c38&quot;&gt;Une fonctionnalité nécessaire dans un service est un endpoint HTTP indiquant si ce service fonctionne. Nous allons utiliser &lt;a href=&quot;https://http4s.org/&quot;&gt;http4s&lt;/a&gt; et le lier à ZIO pour envoyer une réponse 200 (OK) ou 503 (SERVICE_UNAVAILABLE) en fonction de l&amp;#39;état de Kafka Streams.&lt;/p&gt;
&lt;!-- end paragraph 9ac19f9f-e809-45a1-92b0-04b640079c38--&gt;
&lt;!-- begin paragraph 24f40b59-3cba-4b1e-830b-2bdd173cd3ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-24f40b59-3cba-4b1e-830b-2bdd173cd3ae&quot;&gt;Au moment d&amp;#39;écrire ces lignes, il existe quelques tests et tutos associant ZIO et http4s (un article de &lt;a href=&quot;https://medium.com/@wiemzin/zio-with-http4s-and-doobie-952fba51d089&quot;&gt;Wiem Zien ici&lt;/a&gt;, un article &lt;a href=&quot;https://timpigden.github.io/_pages/zio-http4s/intro.html&quot;&gt;là&lt;/a&gt;, une petit tour sur &lt;a href=&quot;https://www.google.com/search?q=zio%20http4s&quot;&gt;Google&lt;/a&gt;...). http4s est par défaut associé cats, cats-effect et fs2. Mais, il y a dans ZIO un projet qui lui permet d&amp;#39;exposer une interface cats-effect.&lt;/p&gt;
&lt;!-- end paragraph 24f40b59-3cba-4b1e-830b-2bdd173cd3ae--&gt;
&lt;!-- begin paragraph 1cbef703-15fb-4582-9b6a-7908dbf4161c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1cbef703-15fb-4582-9b6a-7908dbf4161c&quot;&gt;Voici comment est-ce que ça se passe. Tout d&amp;#39;abord, nous codons le service.&lt;/p&gt;
&lt;!-- end paragraph 1cbef703-15fb-4582-9b6a-7908dbf4161c--&gt;
&lt;!-- begin code fab7e888-1da6-4e38-b306-ca97466b9e22--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fab7e888-1da6-4e38-b306-ca97466b9e22&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.http4s._
import org.http4s.dsl.Http4sDsl
import zio.interop.catz._
import zio.Task

object HealthCheckService {
  private val dsl = Http4sDsl[Task]
  import dsl._

  def webService(streams: ZKafkaStreams): HttpRoutes[Task] =
    HttpRoutes.of[Task] {
        case GET -&amp;gt; Root =&amp;gt;
          streams.status.flatMap { st =&amp;gt;
            if (st.isRunning) Ok()
            else ServiceUnavailable()
          }
      }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fab7e888-1da6-4e38-b306-ca97466b9e22--&gt;
&lt;!-- begin paragraph 2104ceca-9dfe-4347-b182-f5735b865484--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2104ceca-9dfe-4347-b182-f5735b865484&quot;&gt;Dans les projets ZIO, il y a une &lt;a href=&quot;https://github.com/zio/interop-cats&quot;&gt;lib d&amp;#39;interopérabilité avec cats&lt;/a&gt;. En effet, http4s est basé sur cats, cats-effect et fs2 pour instancier des services. De plus, le projet &lt;a href=&quot;https://github.com/zio/zio-http&quot;&gt;ZIO-http&lt;/a&gt; n&amp;#39;a pas démarrer. Grâce ZIO-interop-cats, l&amp;#39;API ZIO est adapté pour utilisé &lt;code&gt;zio.Task&lt;/code&gt; à la place du type IO de cats-effect et qui est conceptuellement proche (l&amp;#39;aspect bifunctor en moins).&lt;/p&gt;
&lt;!-- end paragraph 2104ceca-9dfe-4347-b182-f5735b865484--&gt;
&lt;!-- begin paragraph 3bc65f4a-6306-471a-9ea1-99bd08496023--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3bc65f4a-6306-471a-9ea1-99bd08496023&quot;&gt;Et voici en parallèle, un générateur de service Web avec la variable &lt;code&gt;router&lt;/code&gt; à redéfinir pour exposer des endpoints.&lt;/p&gt;
&lt;!-- end paragraph 3bc65f4a-6306-471a-9ea1-99bd08496023--&gt;
&lt;!-- begin code 9a4ab15b-bdd7-4efe-9edb-c4cbce0bbd9b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9a4ab15b-bdd7-4efe-9edb-c4cbce0bbd9b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.http4s.HttpRoutes
import org.http4s.server.blaze.BlazeServerBuilder
import zio.interop.catz._
import zio.interop.catz.implicits._
import zio.{Task, ZEnv, ZIO}

abstract class ZKafkaStreamsWebService(host: String, port: Int) {
  import org.http4s.implicits._

  val router: HttpRoutes[Task]

  final def serve: ZIO[ZEnv, Throwable, Unit] =
    ZIO.runtime[ZEnv]
      .flatMap { implicit rts =&amp;gt;
        BlazeServerBuilder[Task]
          .bindHttp(port, host)
          .withHttpApp(router.orNotFound)
          .serve
          .compile
          .drain
      }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9a4ab15b-bdd7-4efe-9edb-c4cbce0bbd9b--&gt;
&lt;!-- begin paragraph 3dec80ef-efed-4f12-b259-cada7bd0beb6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3dec80ef-efed-4f12-b259-cada7bd0beb6&quot;&gt;En associant le tout, nous avons un service Web pour notre service avec un endpoint de &lt;i&gt;health check&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 3dec80ef-efed-4f12-b259-cada7bd0beb6--&gt;
&lt;!-- begin code 119e86bb-c8cf-4058-ae98-67e44bc0dfec--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-119e86bb-c8cf-4058-ae98-67e44bc0dfec&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import com.carrefour.phenix.atp.poc_ks.lib.{ZKafkaStreams, ZKafkaStreamsWebService}
import com.carrefour.phenix.atp.poc_ks.lib.endpoints.{HealthCheckService, ServiceInfoService, TopologyService}
import org.http4s.HttpRoutes
import org.http4s.server.Router
import zio._
import zio.interop.catz._

class LineProcessingWebService(zKafkaStreams: ZKafkaStreams,
                               host: String,
                               port: Int)
    extends ZKafkaStreamsWebService(host, port) {

  override val router: HttpRoutes[Task] =
    Router(
      &amp;quot;/monitoring/healthCheck&amp;quot; -&amp;gt; HealthCheckService.webService(zKafkaStreams)
    )

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 119e86bb-c8cf-4058-ae98-67e44bc0dfec--&gt;
&lt;!-- begin paragraph 900d2ebb-ed71-4d58-9396-6d43bd010257--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-900d2ebb-ed71-4d58-9396-6d43bd010257&quot;&gt;Pour terminer, voici le contenu du code de notre application.&lt;/p&gt;
&lt;!-- end paragraph 900d2ebb-ed71-4d58-9396-6d43bd010257--&gt;
&lt;!-- begin code c460b2ec-d56c-4ed7-86dc-2be61fec427a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c460b2ec-d56c-4ed7-86dc-2be61fec427a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;lazy val webService: LineProcessingWebService =
  new LineProcessingWebService(
    zKafkaStreams = zKafkaStream,
    host          = &amp;quot;localhost&amp;quot;,
    port          = 8080)

override def run(args: List[String]): ZIO[zio.ZEnv, Nothing, Int] =
  (webService.serve.fork
    *&amp;gt; zKafkaStream.serve)
    .as(0)
    .orDie&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c460b2ec-d56c-4ed7-86dc-2be61fec427a--&gt;
&lt;!-- begin paragraph a443fc54-ca84-4440-90bb-ee0a5e569879--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a443fc54-ca84-4440-90bb-ee0a5e569879&quot;&gt;Ici &lt;code&gt;.fork&lt;/code&gt; permet de lancer le service Web en parallèle, en plaçant son exécution dans un &lt;i&gt;fiber&lt;/i&gt;. Un &lt;i&gt;fiber&lt;/i&gt; est une sorte de version plus légère et collaborative des threads.&lt;/p&gt;
&lt;!-- end paragraph a443fc54-ca84-4440-90bb-ee0a5e569879--&gt;
&lt;!-- begin paragraph 9b992cb9-2e27-4e5e-93c1-45722602da5b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b992cb9-2e27-4e5e-93c1-45722602da5b&quot;&gt;On a donc un service Web qui est lancé en parallèle et le service Kafka Streams sur lequel notre application est synchronisée.&lt;/p&gt;
&lt;!-- end paragraph 9b992cb9-2e27-4e5e-93c1-45722602da5b--&gt;
&lt;!-- begin heading_1 05ca0359-644e-40bd-8042-b0776af46a7c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-05ca0359-644e-40bd-8042-b0776af46a7c&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 05ca0359-644e-40bd-8042-b0776af46a7c--&gt;
&lt;!-- begin paragraph a02f383e-3754-4bda-8fd9-460465ad3f36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a02f383e-3754-4bda-8fd9-460465ad3f36&quot;&gt;Ici, Kafka Streams fonctionne très bien avec ZIO et http4s. L&amp;#39;ensemble offre un cadre en partie fonctionnellement pure qui favorise la composition des différentes briques de notre application.&lt;/p&gt;
&lt;!-- end paragraph a02f383e-3754-4bda-8fd9-460465ad3f36--&gt;
&lt;!-- begin paragraph 42b0f6f5-1f77-48e3-a29c-bbf8b70c43e2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42b0f6f5-1f77-48e3-a29c-bbf8b70c43e2&quot;&gt;Actuellement, de mon côté, la solution est à l&amp;#39;état de POC. En dehors de ce qui est propre à Kafka Streams, il faudrait voir l&amp;#39;ajout d&amp;#39;autres fonctionnalités comme l&amp;#39;ajout d&amp;#39;autres endpoints de monitoring plus sophistiqués et l&amp;#39;intégration avec des outils de monitoring existants, la sécurisation de l&amp;#39;API Rest ou plus simplement voir comment peut se mettre en place des tests unitaires et des tests d&amp;#39;intégration. Mais les premiers résultats obtenus ici sont en ce me concerne déjà très intéressants.&lt;/p&gt;
&lt;!-- end paragraph 42b0f6f5-1f77-48e3-a29c-bbf8b70c43e2--&gt;
&lt;!-- begin paragraph 2195cb92-431d-4fd8-b506-93b0d8716e45--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2195cb92-431d-4fd8-b506-93b0d8716e45&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2195cb92-431d-4fd8-b506-93b0d8716e45--&gt;
&lt;!-- begin paragraph d46a5bd9-321c-413c-af64-f0858fa74143--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d46a5bd9-321c-413c-af64-f0858fa74143&quot;&gt;Le code est disponible avec quelques ajouts sur &lt;a href=&quot;https://github.com/univalence/blog-code/tree/master/kstreams_pipeline&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph d46a5bd9-321c-413c-af64-f0858fa74143--&gt;
&lt;!-- begin paragraph c02a6a36-3278-4ae5-a531-42b961b6ec44--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c02a6a36-3278-4ae5-a531-42b961b6ec44&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c02a6a36-3278-4ae5-a531-42b961b6ec44--&gt;
&lt;!-- begin paragraph e6f74f6e-7fe0-40c8-9f71-ead24dfbef3b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e6f74f6e-7fe0-40c8-9f71-ead24dfbef3b&quot;&gt;Photo par &lt;a href=&quot;https://unsplash.com/@k8townsend?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Kate Townsend&lt;/a&gt; sur &lt;a href=&quot;https://unsplash.com/s/photos/waiter?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph e6f74f6e-7fe0-40c8-9f71-ead24dfbef3b--&gt;
&lt;!-- begin paragraph 6a2afd03-1a66-4329-a6dc-18df574876ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a2afd03-1a66-4329-a6dc-18df574876ee&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6a2afd03-1a66-4329-a6dc-18df574876ee--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:22b88422a08049a0af7c29a47924c94c</id>
    <title>Patientons avec les nouveautés de Spark 3.0</title>
    <updated>2020-04-27T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/patientons-avec-les-nouveautes-de-spark-3-0.html"/>
    <!--summary Résumé des changements et nouveautés apportés avec Spark 3.0-->    <author>
      <name>Bernarith Men</name>
      <uri>https://univalence.io/blog/auteurs/bernarith-men.html</uri>
    </author>        <category term="Spark"></category>    <category term="Changelog"></category>    <category term="Catalyst"></category>    <content type="html">
&lt;!-- begin paragraph 3a4a7815-3d9c-40ca-b96f-dd6ec17f2fda--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3a4a7815-3d9c-40ca-b96f-dd6ec17f2fda&quot;&gt;On attend Spark 3.0.0 depuis la preview 2 de Noël 2019. Ici vous n&amp;#39;aurez &lt;b&gt;pas&lt;/b&gt; la date de release, mais un résumé des principaux changements et nouveautés et connus à ce jour, que l&amp;#39;on pourra attendre de cette nouvelle version majeure de Spark. Je vous invite aussi à voir nos précédents articles sur Spark comme celui-ci sur &lt;a href=&quot;https://blog.univalence.io/spark-2-4-tour-dhorizon-de-la-nouvelle-version/&quot;&gt;Spark 2.4&lt;/a&gt; et les &lt;a href=&quot;https://blog.univalence.io/tag/spark/&quot;&gt;autres&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 3a4a7815-3d9c-40ca-b96f-dd6ec17f2fda--&gt;
&lt;!-- begin paragraph b0936d15-a96b-471d-878a-213895e63d84--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b0936d15-a96b-471d-878a-213895e63d84&quot;&gt;&lt;i&gt;Disclaimer!!! Attention quelques liens sont donnés vers la &lt;/i&gt;&lt;b&gt;preview&lt;/b&gt;&lt;i&gt; 2 de Spark 3.0. Elle pourrait comporter des bugs et erreurs dans sa documentation.&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph b0936d15-a96b-471d-878a-213895e63d84--&gt;
&lt;!-- begin heading_1 f14a4bb5-8cc9-465f-8412-d0ae97456974--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f14a4bb5-8cc9-465f-8412-d0ae97456974&quot;&gt;Compatibilité des versions&lt;/h2&gt;
&lt;!-- end heading_1 f14a4bb5-8cc9-465f-8412-d0ae97456974--&gt;
&lt;!-- begin paragraph 9747c2d8-3130-4d8b-bf54-9781bd136b60--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9747c2d8-3130-4d8b-bf54-9781bd136b60&quot;&gt;Premièrement, sur la compatibilité, Spark 3.0.0 pourra s’exécuter avec :&lt;/p&gt;
&lt;!-- end paragraph 9747c2d8-3130-4d8b-bf54-9781bd136b60--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Java 8 / 11&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Le support de versions antérieures à &lt;b&gt;Java 8 version 8u92&lt;/b&gt; est déprécié.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Scala 2.12&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Spark 3 arrête le support de&lt;b&gt; &lt;/b&gt;Scala 2.11, qui est déjà déprécié depuis Spark 2.4.1.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Python 2.7+/3.4+&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Le support des versions de Python 2 et 3 antérieures à la version &lt;b&gt;3.6&lt;/b&gt; est déprécié.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;R 3.1+&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Le support de version antérieure à R &lt;b&gt;3.4&lt;/b&gt; est déprécié.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 930e3b02-7434-4aa4-afde-9ffeca3242b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-930e3b02-7434-4aa4-afde-9ffeca3242b7&quot;&gt;Il n&amp;#39;y aura plus de support d&amp;#39;Hadoop 2.6, la version par défaut sera la &lt;b&gt;2.7&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 930e3b02-7434-4aa4-afde-9ffeca3242b7--&gt;
&lt;!-- begin heading_1 1025b5b0-7918-4c7e-aec4-81c06db7d45c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1025b5b0-7918-4c7e-aec4-81c06db7d45c&quot;&gt;Principaux changements et nouveautés&lt;/h2&gt;
&lt;!-- end heading_1 1025b5b0-7918-4c7e-aec4-81c06db7d45c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Changement dans le module MLlib &lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;L&amp;#39;API basé sur les RDD ne devrait plus être supporté. En effet, cette API côté MLlib était en mode maintenance depuis Spark 2.0 et &lt;i&gt;devrait &lt;/i&gt;être supprimée avec la version 3.0.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Support des fichiers binaires&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;La nouvelle version apportera le support de fichiers binaires, comme les images, l&amp;#39;audio ou les vidéos. On pourra donc lire ces fichiers pour récupérer les métadonnées sous forme de DataFrame. Par contre, la réécriture d&amp;#39;un DataFrame vers un fichier binaire de ce type n&amp;#39;est pas possible. Une &lt;a href=&quot;https://spark.apache.org/docs/3.0.0-preview2/sql-data-sources-binaryFile.html&quot;&gt;documentation sur la preview&lt;/a&gt; est fournie, avec le &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-25348&quot;&gt;ticket&lt;/a&gt; JIRA.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;L&amp;#39;API DataSource V2 sera changée&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Introduite dans Spark 2.3, l&amp;#39;API change dans Spark 3.0 et offre une meilleure intégration avec les optimiseurs Spark, les sources tierces auront de meilleures performances avec cette version.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Meilleures performances&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Accélération du Catalyst Optimizer grâce à des améliorations des algorithmes, par exemple sur le JIRA, &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-25974&quot;&gt;SPARK-25974&lt;/a&gt; et &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-27815&quot;&gt;SPARK-27815&lt;/a&gt; parmi d&amp;#39;autres.&lt;/li&gt;&lt;li&gt;Dynamic Partition Pruning (traité dans cet article plus bas)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Spark Graph ?&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Spark Graph est un nouveau module développé par Neo4J, dont le nom projet de base était &amp;quot;Cypher for Apache Spark&amp;quot; puis &amp;quot;Morpheus&amp;quot;. Il apporte le langage Cypher à Spark. Cypher est un langage de requêtage dédié pour les graphes (une sorte de SQL, mais adaptés aux parcours de graphes). Plus d&amp;#39;information dans cette  &lt;a href=&quot;https://databricks.com/session_eu19/graph-features-in-spark-3-0-integrating-graph-querying-and-algorithms-in-spark-graph&quot;&gt;présentation&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Néanmoins le module ne va probablement pas être disponible pour la release de Spark 3.0 d&amp;#39;après cette &lt;a href=&quot;http://apache-spark-developers-list.1001551.n3.nabble.com/SparkGraph-review-process-td28037.html#a28900&quot;&gt;discussion&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Utilisation des ressources GPU&lt;/b&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;Malgré le support des ressources GPU par YARN et Kubernetes, Spark ne prenait pas en compte les ressources GPU exposées par ces derniers. Avec Spark 3 il sera possible d&amp;#39;allouer les ressources des GPU sur le cluster de manière &lt;b&gt;native&lt;/b&gt;, cela fonctionnera avec YARN, Kubernetes et le mode Standalone. Cela pourrait être très utile pour des utilisations comme le Deep Learning. Pour en savoir plus, vous pouvez regarder l&amp;#39;Epic associée &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-24615&quot;&gt;SPARK-24615&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 635e21a5-0604-4658-97af-6931b3f8e3da--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-635e21a5-0604-4658-97af-6931b3f8e3da&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 635e21a5-0604-4658-97af-6931b3f8e3da--&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Beaucoup de codes dépréciés en 2.x vont être supprimés. 🎉&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph bfeb636c-a39a-429f-9b15-d6de52352fd0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bfeb636c-a39a-429f-9b15-d6de52352fd0&quot;&gt;Pour en savoir plus, vous pouvez aussi consulter les entrées du &lt;a href=&quot;https://issues.apache.org/jira/sr/jira.issueviews:searchrequest-printable/temp/SearchRequest.html?jqlQuery=statusCategory%20%3D%20done%20AND%20project%20%3D%2012315420%20AND%20fixVersion%20%3D%2012339177%20ORDER%20BY%20priority%20DESC%2C%20key%20ASC&amp;tempMax=1000&quot;&gt;JIRA&lt;/a&gt; de Spark pour voir les autres features et bugfix.&lt;/p&gt;
&lt;!-- end paragraph bfeb636c-a39a-429f-9b15-d6de52352fd0--&gt;
&lt;!-- begin heading_1 2c78a6ec-3b28-47ac-9226-7b1625ed4be5--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2c78a6ec-3b28-47ac-9226-7b1625ed4be5&quot;&gt;Un point sur le Dynamic Partition Pruning&lt;/h2&gt;
&lt;!-- end heading_1 2c78a6ec-3b28-47ac-9226-7b1625ed4be5--&gt;
&lt;!-- begin paragraph a002b680-12ad-4253-a7ee-26cd8c10344c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a002b680-12ad-4253-a7ee-26cd8c10344c&quot;&gt;Vous connaissiez peut-être déjà le Static Partition Pruning, où quand on avait ce genre de jointure :&lt;/p&gt;
&lt;!-- end paragraph a002b680-12ad-4253-a7ee-26cd8c10344c--&gt;
&lt;!-- begin code 0980eb3d-5df5-4895-9c5c-9164b7971fad--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0980eb3d-5df5-4895-9c5c-9164b7971fad&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT t1.foo FROM t1
JOIN t2
	ON t1.partcol = t2.partcol 
	AND t2.partcol = 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0980eb3d-5df5-4895-9c5c-9164b7971fad--&gt;
&lt;!-- begin paragraph 9249cf3d-3f51-4c5e-acf6-5e3ea6cb0796--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9249cf3d-3f51-4c5e-acf6-5e3ea6cb0796&quot;&gt;Avec &lt;code&gt;t1&lt;/code&gt; partitionné sur la colonne &lt;code&gt;partcol&lt;/code&gt;. À la &lt;b&gt;compilation&lt;/b&gt;, Spark va inférer que &lt;code&gt;t1.partcol = 1&lt;/code&gt;, va faire son &amp;quot;pruning&amp;quot; et pouvoir choisir les bonnes partitions à scanner. Tout cela parce que le filtre se fait sur les colonnes de partitions.&lt;/p&gt;
&lt;!-- end paragraph 9249cf3d-3f51-4c5e-acf6-5e3ea6cb0796--&gt;
&lt;!-- begin paragraph 731eb6c2-dd6c-4719-9aae-03d7a35e9b2c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-731eb6c2-dd6c-4719-9aae-03d7a35e9b2c&quot;&gt;Or qu&amp;#39;en est-il avec une jointure de ce type ? &lt;/p&gt;
&lt;!-- end paragraph 731eb6c2-dd6c-4719-9aae-03d7a35e9b2c--&gt;
&lt;!-- begin code 49cdfa69-ffbf-490a-805e-1ba7286158ad--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-49cdfa69-ffbf-490a-805e-1ba7286158ad&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT * FROM dim_table 
JOIN fact_table 
	ON (dim_table.partcol = fact_table.somcol) 
WHERE dim_table.othercol &amp;gt; 10.&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 49cdfa69-ffbf-490a-805e-1ba7286158ad--&gt;
&lt;!-- begin paragraph 60441dae-79bc-4608-a55e-c3a84350b69a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-60441dae-79bc-4608-a55e-c3a84350b69a&quot;&gt;Ici on a une jointure entre une table de fait &lt;code&gt;fact_table&lt;/code&gt; (potentiellement très grande) et une table de dimension &lt;code&gt;dim_table&lt;/code&gt; (qui n&amp;#39;est pas forcément partitionnée et petite). Le filtre se fait sur la colonne &lt;code&gt;othercol&lt;/code&gt; de la table &lt;code&gt;dim_table&lt;/code&gt; qui n&amp;#39;est pas la colonne de jointure. &lt;/p&gt;
&lt;!-- end paragraph 60441dae-79bc-4608-a55e-c3a84350b69a--&gt;
&lt;!-- begin paragraph 4e572c6c-e3eb-4274-89bc-1c98bcdaf1ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e572c6c-e3eb-4274-89bc-1c98bcdaf1ee&quot;&gt;Spark ne peut savoir qu&amp;#39;au runtime ce qu&amp;#39;il faut filtrer sur la table &lt;code&gt;fact_table&lt;/code&gt; avec le résultat du filtre sur &lt;code&gt;dim_table&lt;/code&gt; . Si la table de dimension est petite, Spark va probablement faire un Broadcast Hash Join, durant ce processus une HashTable est créée à partir de la table de dimension et le résultat est mis dans une variable de broadcast.&lt;/p&gt;
&lt;!-- end paragraph 4e572c6c-e3eb-4274-89bc-1c98bcdaf1ee--&gt;
&lt;!-- begin paragraph 0c76e2fc-3e41-45a4-a8c7-2a53f77f4a32--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0c76e2fc-3e41-45a4-a8c7-2a53f77f4a32&quot;&gt;Avant, cette variable de broadcast était diffusée à tous les workers et alors le filtrage se faisait localement sur chaque partition de la table de fait et ainsi on évitait un shuffle.&lt;/p&gt;
&lt;!-- end paragraph 0c76e2fc-3e41-45a4-a8c7-2a53f77f4a32--&gt;
&lt;!-- begin paragraph dd3c4120-46da-4a0e-8254-ef4ea5478a13--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dd3c4120-46da-4a0e-8254-ef4ea5478a13&quot;&gt;Maintenant, avec le &lt;b&gt;Dynamic Partition Pruning&lt;/b&gt;, on va prendre cette variable de broadcast qui servira de filtre dynamique. On l&amp;#39;utilisera dans la stratégie de scan de la table de fait et on ne sélectionnera que les partitions qui nous intéresse. Donc on évite un shuffle &lt;b&gt;et un &lt;/b&gt;scan inutile de partitions.&lt;/p&gt;
&lt;!-- end paragraph dd3c4120-46da-4a0e-8254-ef4ea5478a13--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/22b88422-a080-49a0-af7c-29a47924c94c/rose-1744950_1280.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 979d98b4-95d8-40f3-b3a0-57935693b084--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-979d98b4-95d8-40f3-b3a0-57935693b084&quot;&gt;Au niveau performance, des tests ont été faits par les équipes de Databricks sur le benchmark &lt;a href=&quot;http://www.tpc.org/tpcds/&quot;&gt;TPC-DS&lt;/a&gt; dont les résultats ont donné :&lt;/p&gt;
&lt;!-- end paragraph 979d98b4-95d8-40f3-b3a0-57935693b084--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Pour 1 To de données, pour environ 60% de requêtes. On a une accélération qui va de &lt;b&gt;2x à 18x &lt;/b&gt;plus vite.&lt;/li&gt;&lt;li&gt;Pour 10 To, il y a des accélérations qui vont &lt;b&gt;jusqu&amp;#39;à 100x. &lt;/b&gt;&lt;/li&gt;&lt;li&gt;Dans ces requêtes accélérées, ils ont observé que 90% de la donnée était rétirée du scope, d&amp;#39;où le gain de performance.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 4cb3b9e0-94a9-40d0-b8c5-50747a1eefe8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4cb3b9e0-94a9-40d0-b8c5-50747a1eefe8&quot;&gt;Vous trouverez &lt;a href=&quot;https://databricks.com/session_eu19/dynamic-partition-pruning-in-apache-spark&quot;&gt;ici&lt;/a&gt; un talk de Databricks sur le sujet et aussi le &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-11150&quot;&gt;ticket&lt;/a&gt; JIRA associé à cette feature.&lt;/p&gt;
&lt;!-- end paragraph 4cb3b9e0-94a9-40d0-b8c5-50747a1eefe8--&gt;
&lt;!-- begin heading_1 5b26e2b0-628e-4b32-b32c-292d420fed27--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5b26e2b0-628e-4b32-b32c-292d420fed27&quot;&gt;Comment faire pour se préparer ?&lt;/h2&gt;
&lt;!-- end heading_1 5b26e2b0-628e-4b32-b32c-292d420fed27--&gt;
&lt;!-- begin paragraph ce1048f1-275f-4382-ab9f-22c460eca6f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce1048f1-275f-4382-ab9f-22c460eca6f9&quot;&gt;Si vous voulez vous préparer à une migration vers la version 3.0, il existe plusieurs guides officiels de migration de Spark 2.4 vers Spark 3.0, que l&amp;#39;ont peut trouver dans la &lt;a href=&quot;https://spark.apache.org/docs/3.0.0-preview2/migration-guide.html&quot;&gt;documentation&lt;/a&gt; de la preview 2. Vous remarquerez qu&amp;#39;il y a le plus de changements dans Spark SQL.&lt;/p&gt;
&lt;!-- end paragraph ce1048f1-275f-4382-ab9f-22c460eca6f9--&gt;
&lt;!-- begin paragraph 5166d8ff-63ff-4d8e-b727-5347cffa2b16--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5166d8ff-63ff-4d8e-b727-5347cffa2b16&quot;&gt;En Scala, il faut en premier sortir de Scala 2.11 en passant à Spark 2.4.1.&lt;/p&gt;
&lt;!-- end paragraph 5166d8ff-63ff-4d8e-b727-5347cffa2b16--&gt;
&lt;!-- begin paragraph 0d7c1616-aec2-468f-83ce-bb2fec39b06e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0d7c1616-aec2-468f-83ce-bb2fec39b06e&quot;&gt;Vous pouvez en suite tester la 3.0.0-preview2 Spark :&lt;/p&gt;
&lt;!-- end paragraph 0d7c1616-aec2-468f-83ce-bb2fec39b06e--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;En téléchargement direct &lt;a href=&quot;https://spark.apache.org/downloads.html&quot;&gt;ici&lt;/a&gt; &lt;/li&gt;&lt;li&gt;Ou directement dans votre projet sbt avec :
&lt;!-- begin code c89a6360-ded5-48da-852c-0b730e24c0d0--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-c89a6360-ded5-48da-852c-0b730e24c0d0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies += &amp;quot;org.apache.spark&amp;quot; %% &amp;quot;spark-sql&amp;quot; % &amp;quot;3.0.0-preview2&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c89a6360-ded5-48da-852c-0b730e24c0d0--&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 5d6fc473-af4c-42eb-916a-91bb20727841--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5d6fc473-af4c-42eb-916a-91bb20727841&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 5d6fc473-af4c-42eb-916a-91bb20727841--&gt;
&lt;!-- begin paragraph ae27567d-aa07-4977-974c-d0dfc474e3b5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ae27567d-aa07-4977-974c-d0dfc474e3b5&quot;&gt;Voilà pour les principales features que l&amp;#39;ont pourra attendre de Spark 3.0. À ce jour, nous ne connaissons toujours pas de date de release. Mais peut être en saurons-nous plus lors du Spark+AI Summit 2020 qui se déroulera du 22 au 26 Juin et qui, à cause de la situation mondiale actuelle, sera virtuel et gratuit pour tous. &lt;a href=&quot;https://databricks.com/sparkaisummit/north-america-2020&quot;&gt;https://databricks.com/sparkaisummit/north-america-2020&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph ae27567d-aa07-4977-974c-d0dfc474e3b5--&gt;
&lt;!-- begin divider 264b09b6-c93c-4d82-9f4e-669ed690d18c--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-264b09b6-c93c-4d82-9f4e-669ed690d18c&quot;&gt;
&lt;!-- end divider 264b09b6-c93c-4d82-9f4e-669ed690d18c--&gt;
&lt;!-- begin paragraph ce2f1ff5-16f1-4e0f-99b3-e8b32e863d53--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce2f1ff5-16f1-4e0f-99b3-e8b32e863d53&quot;&gt;Photographie par &lt;a href=&quot;https://unsplash.com/photos/CrHG_ZYn1Dw&quot;&gt;Adrien Delforge sur Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph ce2f1ff5-16f1-4e0f-99b3-e8b32e863d53--&gt;
&lt;!-- begin paragraph 9c57c725-cb3b-4140-99ae-c7d5c8f9b156--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c57c725-cb3b-4140-99ae-c7d5c8f9b156&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9c57c725-cb3b-4140-99ae-c7d5c8f9b156--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:af3ad66536074e2b89c1f7e5671c76db</id>
    <title>NE FAITES PAS CETTE ERREUR</title>
    <updated>2020-03-26T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/ne-faites-pas-cette-erreur.html"/>
    <!--summary Et si les erreurs n&#039;en étaient pas ?-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="Programmation fonctionnelle"></category>    <content type="html">
&lt;!-- begin paragraph e461ef8c-b017-4aae-b0b9-02981ef726cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e461ef8c-b017-4aae-b0b9-02981ef726cc&quot;&gt;&lt;i&gt;Dialogue interne&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph e461ef8c-b017-4aae-b0b9-02981ef726cc--&gt;
&lt;!-- begin paragraph 2f7b4806-2ed2-457e-9dab-6b777df73c83--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2f7b4806-2ed2-457e-9dab-6b777df73c83&quot;&gt;« Et bim ! Encore une erreur. Que ce soit un code 500, je veux bien. Mais avec le message &amp;quot;Houston. We’ve got a problem.&amp;quot;, l’humour c’est bien, mais je ne vais pas aller très loin avec ça. Ma seule solution serait de fouiller dans le code source pour comprendre ce qui s’est passé. Au fait, j’espère que j’y ai les accès ? »&lt;/p&gt;
&lt;!-- end paragraph 2f7b4806-2ed2-457e-9dab-6b777df73c83--&gt;
&lt;!-- begin paragraph ac5c190c-3ef1-4e31-a4f6-1f9a9093f77a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ac5c190c-3ef1-4e31-a4f6-1f9a9093f77a&quot;&gt;Dans le monde Java et avec d&amp;#39;autres langages, pour gérer les erreurs, on pense souvent au mécanisme des exceptions ou de valeurs particulières comme -1, null ou NaN. Et on y pense d&amp;#39;ailleurs souvent un peu trop. -1 va bien fonctionner tant que vous n’oubliez pas de le gérer et à condition que la réponse nominale de votre fonction est un entier positif. null, il faut penser à le gérer aussi et c&amp;#39;est facile de l&amp;#39;oublier, sinon NullPointerException ne serait pas si courant. NaN se passe bien tant que vous ne cherchez pas à faire de comparaison, car NaN ≠ NaN. Et puis les exceptions... comme dirait &lt;a href=&quot;https://www.youtube.com/watch?v=lvlnH-uEjZA&quot;&gt;Nicolas Rinaudo&lt;/a&gt;, les exceptions c&amp;#39;est pire que le goto, sachant que dans le cas des goto, on sait où est-ce que ça atterrit !&lt;/p&gt;
&lt;!-- end paragraph ac5c190c-3ef1-4e31-a4f6-1f9a9093f77a--&gt;
&lt;!-- begin paragraph 75e4f818-19aa-49a4-8bfe-61062fcdf924--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75e4f818-19aa-49a4-8bfe-61062fcdf924&quot;&gt;Ceci dit, les checked exceptions proposent une idée intéressante car elles doivent apparaître dans la signature des fonctions et donnent des indications sur leur comportement. Mais c’est généralement une écriture qui alourdit le code et ça se termine avec des unchecked exceptions.&lt;/p&gt;
&lt;!-- end paragraph 75e4f818-19aa-49a4-8bfe-61062fcdf924--&gt;
&lt;!-- begin paragraph 92bf7ba0-7bde-47f3-ad0a-c5ff6e42ca3a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-92bf7ba0-7bde-47f3-ad0a-c5ff6e42ca3a&quot;&gt;La communauté FP propose néanmoins de reprendre cette idée pour mettre en valeur le fait qu’une fonction puisse retourner un résultat exceptionnel. Cela passe par des types comme Option, Either ou IO. Et ces types sont nécessaires, en particulier parce qu’ils sont accompagnés d’un ensemble d’opérations standards qui permettent de conserver un code linéaire (ie. sans boucles ni if explicites). Cependant, ils ne sont pas suffisants, car ils ne donnent aucune indication sur la nature de l’erreur.&lt;/p&gt;
&lt;!-- end paragraph 92bf7ba0-7bde-47f3-ad0a-c5ff6e42ca3a--&gt;
&lt;!-- begin paragraph e212bda9-8954-470f-990d-97064476f60b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e212bda9-8954-470f-990d-97064476f60b&quot;&gt;Une erreur peut être d&amp;#39;ordre technique, dans la mesure où on a atteint une limite physique (out of memory, timeout, stack overflow...). Elle peut être d&amp;#39;ordre métier, dans le mesure où un contexte sort du comportement attendu par le métier (ie. identifiant de compte invalide, produit non disponible, badge déjà validé...). Mais la façon de gérer une erreur, quelle qu&amp;#39;elle soit, est une décision métier.&lt;/p&gt;
&lt;!-- end paragraph e212bda9-8954-470f-990d-97064476f60b--&gt;
&lt;!-- begin paragraph 9766a539-b641-46ed-8d34-99d86d0bbfc2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9766a539-b641-46ed-8d34-99d86d0bbfc2&quot;&gt;Puisque la gestion d&amp;#39;une erreur est une décision métier, au niveau technique on ne devrait pas chercher à émettre une erreur. D&amp;#39;ailleurs, on ne devrait même pas réfléchir au résultat nominal à priori. On devrait en fait qualifier une situation sur le plan métier.&lt;/p&gt;
&lt;!-- end paragraph 9766a539-b641-46ed-8d34-99d86d0bbfc2--&gt;
&lt;!-- begin paragraph ec1920b4-0503-4cbe-9d83-f32ec09b6d9b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec1920b4-0503-4cbe-9d83-f32ec09b6d9b&quot;&gt;Prenons un exemple simple. Et pour paraphraser &lt;a href=&quot;https://www.youtube.com/watch?v=q0PlcgR5M1Q&quot;&gt;François Armand&lt;/a&gt;, nous allons prendre l’exemple de la division euclidienne.&lt;/p&gt;
&lt;!-- end paragraph ec1920b4-0503-4cbe-9d83-f32ec09b6d9b--&gt;
&lt;!-- begin paragraph b06a4a6e-4efb-4c0c-ae08-88b4dd627ba5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b06a4a6e-4efb-4c0c-ae08-88b4dd627ba5&quot;&gt;Il est en parti vrai d&amp;#39;écrire&lt;/p&gt;
&lt;!-- end paragraph b06a4a6e-4efb-4c0c-ae08-88b4dd627ba5--&gt;
&lt;!-- begin code 4ce40e3b-3d4f-4fb8-8ad6-496d84e275b1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4ce40e3b-3d4f-4fb8-8ad6-496d84e275b1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def divide(a: Int, b: Int): Int&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4ce40e3b-3d4f-4fb8-8ad6-496d84e275b1--&gt;
&lt;!-- begin paragraph 5ee29824-0be6-410f-8e2a-940db7e0e612--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ee29824-0be6-410f-8e2a-940db7e0e612&quot;&gt;Seulement, si b vaut 0, à quelle genre de réponse doit-on s’attendre ? On voit rapidement que -1 n’est pas une réponse valide. null n’est pas permis par définition du type Int. On pourrait envoyer une exception... ce qui reviendrait à ajouter du code alourdissant celui existant pour traiter l’exception, si on ne l’oublie pas.&lt;/p&gt;
&lt;!-- end paragraph 5ee29824-0be6-410f-8e2a-940db7e0e612--&gt;
&lt;!-- begin paragraph 35c65a3f-641b-4b93-a176-ec0a8badfc31--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35c65a3f-641b-4b93-a176-ec0a8badfc31&quot;&gt;Écrivons maintenant&lt;/p&gt;
&lt;!-- end paragraph 35c65a3f-641b-4b93-a176-ec0a8badfc31--&gt;
&lt;!-- begin code 0e2964d1-729a-4b49-9f82-ea481c5385a4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0e2964d1-729a-4b49-9f82-ea481c5385a4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def divide(a: Int, b: Int): Option[Int]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0e2964d1-729a-4b49-9f82-ea481c5385a4--&gt;
&lt;!-- begin paragraph 86fdfed3-20d7-4808-9d34-337fc943c89c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86fdfed3-20d7-4808-9d34-337fc943c89c&quot;&gt;Cette version indique clairement, que dans certains cas nous n’aurons pas la réponse attendue : None. En plus, le compilateur va nous obliger à gérer le cas où nous obtiendrons None. Ce ne sera pas avec une structure syntaxique lourde (try... catch) mais avec avec des opérations qu’on peut intégrer dans des expressions (map, flatMap, getOrElse...) et le &amp;quot;if&amp;quot; n’est pas nécessaire.&lt;/p&gt;
&lt;!-- end paragraph 86fdfed3-20d7-4808-9d34-337fc943c89c--&gt;
&lt;!-- begin paragraph 8c2a4014-51a6-450e-986a-8a17acc27ba5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c2a4014-51a6-450e-986a-8a17acc27ba5&quot;&gt;Nous allons maintenant représenter des périodes de temps (où le temps est représenté par un entier pour des raisons de simplification).&lt;/p&gt;
&lt;!-- end paragraph 8c2a4014-51a6-450e-986a-8a17acc27ba5--&gt;
&lt;!-- begin code 872ce99f-fd13-4cd0-b460-594bb3008c1c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-872ce99f-fd13-4cd0-b460-594bb3008c1c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Period(start: Int, end: Int)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 872ce99f-fd13-4cd0-b460-594bb3008c1c--&gt;
&lt;!-- begin paragraph d1af835b-9d32-4241-91f7-3c80e20e6062--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d1af835b-9d32-4241-91f7-3c80e20e6062&quot;&gt;Nous allons éluder le cas où start &amp;gt; end.&lt;/p&gt;
&lt;!-- end paragraph d1af835b-9d32-4241-91f7-3c80e20e6062--&gt;
&lt;!-- begin paragraph 71552ed5-d519-421a-a95a-9b720bc5bab6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71552ed5-d519-421a-a95a-9b720bc5bab6&quot;&gt;Je veux constituer une liste de Period qui ne se chevauchent pas plus par rapport à un certain timing.&lt;/p&gt;
&lt;!-- end paragraph 71552ed5-d519-421a-a95a-9b720bc5bab6--&gt;
&lt;!-- begin paragraph be91353f-970a-4bbc-8e2b-f2f8878911f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be91353f-970a-4bbc-8e2b-f2f8878911f9&quot;&gt;Avant de parler de cas nominaux et cas exceptionnels, nous allons d’abord chercher à qualifier les différents contextes. Nous avons donc des périodes qui ne se chevauchent pas, des périodes qui se chevauchent un peu et des périodes qui se chevauchent beaucoup ou complètement.&lt;/p&gt;
&lt;!-- end paragraph be91353f-970a-4bbc-8e2b-f2f8878911f9--&gt;
&lt;!-- begin paragraph 2c19a66b-2d8e-49ae-af18-df24e2054868--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c19a66b-2d8e-49ae-af18-df24e2054868&quot;&gt;Je vous propose de représenter quelques cas en utilisant des têtes de chiots pour représenter le temps passé dans les périodes, parce que pourquoi pas !&lt;/p&gt;
&lt;!-- end paragraph 2c19a66b-2d8e-49ae-af18-df24e2054868--&gt;
&lt;!-- begin paragraph 71a6533d-f05d-4389-affb-2230cdf9ecdf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71a6533d-f05d-4389-affb-2230cdf9ecdf&quot;&gt;Voici les cas où il n&amp;#39;y a pas de chevauchement.&lt;/p&gt;
&lt;!-- end paragraph 71a6533d-f05d-4389-affb-2230cdf9ecdf--&gt;
&lt;!-- begin code 37078596-c74c-4d2b-8c43-8b542aa15e5a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-37078596-c74c-4d2b-8c43-8b542aa15e5a&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;# period 1 before period 2
period 1 [🐶🐶🐶]
period 2          [🐶🐶🐶]

# period 1 after period 2
period 1          [🐶🐶🐶]
period 2 [🐶🐶🐶]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 37078596-c74c-4d2b-8c43-8b542aa15e5a--&gt;
&lt;!-- begin paragraph 0e83c9d5-22ae-4c48-aefc-aae430fdb6b5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e83c9d5-22ae-4c48-aefc-aae430fdb6b5&quot;&gt;Voici les cas avec chevauchement&lt;/p&gt;
&lt;!-- end paragraph 0e83c9d5-22ae-4c48-aefc-aae430fdb6b5--&gt;
&lt;!-- begin code 85d35c21-e716-42ff-bc5c-0a8fd473c94b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-85d35c21-e716-42ff-bc5c-0a8fd473c94b&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;# period 2 overlaps period 1 by 2 🐶
period 1 [🐶🐶🐶🐶]
period 2    [🐶🐶🐶🐶]

# period 1 overlaps period 2 by 3 🐶
period 1    [🐶🐶🐶🐶]
period 2  [🐶🐶🐶🐶]

# period 1 includes period 2
[🐶🐶🐶🐶🐶]
  [🐶🐶]

# period 1 == period 2
[🐶🐶🐶]
[🐶🐶🐶]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 85d35c21-e716-42ff-bc5c-0a8fd473c94b--&gt;
&lt;!-- begin paragraph ae03dc13-4ede-4a6d-ad99-297240c955fc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ae03dc13-4ede-4a6d-ad99-297240c955fc&quot;&gt;Si on regroupe d&amp;#39;un côté les deux premiers cas (chevauchement partiel) et de l&amp;#39;autre les deux derniers cas (chevauchement total), nous avons au total trois cas possibles.&lt;/p&gt;
&lt;!-- end paragraph ae03dc13-4ede-4a6d-ad99-297240c955fc--&gt;
&lt;!-- begin code 74ee68ea-7bb9-4e5f-b073-287d2aa2aec9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-74ee68ea-7bb9-4e5f-b073-287d2aa2aec9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;sealed trait OverlappingPeriodQualification
case class PeriodsDoNotOverlap(  period1: Period, period2: Period)          extends OverlappingPeriodQualification
case class PeriodsPartiallyOverlap(period1: Period, period2: Period, by: Int) extends OverlappingPeriodQualification
case class PeriodsFullyOverlap(    period1: Period, period2: Period)          extends OverlappingPeriodQualification&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 74ee68ea-7bb9-4e5f-b073-287d2aa2aec9--&gt;
&lt;!-- begin paragraph e06ee171-8b53-418c-9c41-842e296008c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e06ee171-8b53-418c-9c41-842e296008c1&quot;&gt;À partir de là, nous pouvons effectuer la qualification.&lt;/p&gt;
&lt;!-- end paragraph e06ee171-8b53-418c-9c41-842e296008c1--&gt;
&lt;!-- begin code dd1d0757-fc08-40a1-a346-75fe592cdd9d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dd1d0757-fc08-40a1-a346-75fe592cdd9d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def qualify(period1: Period, period2: Period): OverlappingPeriodQualification =
  if ((period1.end &amp;lt;= period2.start) || (period2.end &amp;lt;= period1.start))
    PeriodsDoNotOverlap(period1, period2)
  else if (period1.start &amp;gt;= period2.start) {
    if (period1.end &amp;lt;= period2.end)
      PeriodsFullyOverlap(period1, period2)
    else
      PeriodsPartiallyOverlap(period1, period2, period2.end - period1.start)
  }
  else if (period2.start &amp;gt;= period1.start) {
    if (period2.end &amp;lt;= period1.end)
      PeriodsFullyOverlap(period1, period2)
    else
      PeriodsPartiallyOverlap(period1, period2, period1.end - period2.start)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dd1d0757-fc08-40a1-a346-75fe592cdd9d--&gt;
&lt;!-- begin paragraph fa99f805-6d3a-45b3-9cc5-48a1e454a26d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa99f805-6d3a-45b3-9cc5-48a1e454a26d&quot;&gt;Une fois la qualification réalisée, on peut passer à l&amp;#39;interprétation des valeurs obtenues. La phase d&amp;#39;interprétation consiste à convertir des valeurs en effets. C&amp;#39;est en quelque sorte la partie présentation. Le code ci-dessous correspond à une interprétation possible des valeurs de OverlappingPeriodQualification.&lt;/p&gt;
&lt;!-- end paragraph fa99f805-6d3a-45b3-9cc5-48a1e454a26d--&gt;
&lt;!-- begin code 2255c7a6-83c4-4bff-93d5-a13c6f2bad30--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2255c7a6-83c4-4bff-93d5-a13c6f2bad30&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def interpret(qualification: OverlappingPeriodQualification): Try[Unit] =
  qualification match {
    case PeriodsDoNotOverlap(_, _) =&amp;gt; Success(())
    case PeriodsFullyOverlap(p1, p2) =&amp;gt; Failure(new Exception(&amp;quot;periods overlap&amp;quot;))
    case PeriodsPartiallyOverlap(p1, p2, delta) =&amp;gt;
      if (delta &amp;gt; 2) Failure(new Exception(&amp;quot;periods overlap&amp;quot;))
      else Success(())
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2255c7a6-83c4-4bff-93d5-a13c6f2bad30--&gt;
&lt;!-- begin paragraph 5a582b1a-e1de-470f-bab6-76e0c786d077--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a582b1a-e1de-470f-bab6-76e0c786d077&quot;&gt;Il est possible d&amp;#39;ajouter d&amp;#39;autres interpréteurs qui envoient des exceptions, qui permettent de tracer les qualifications, qui effectuent d&amp;#39;autres actions. Le mieux est de faire en sorte que cette phase d&amp;#39;interprétation est lieu le plus tard possible dans le code.&lt;/p&gt;
&lt;!-- end paragraph 5a582b1a-e1de-470f-bab6-76e0c786d077--&gt;
&lt;!-- begin paragraph 133d0b57-ddfa-4c03-9aaf-99f3473e90cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-133d0b57-ddfa-4c03-9aaf-99f3473e90cf&quot;&gt;La séparation qualification / interprétation offre une certaine vertu, en particulier, de permettre d&amp;#39;avoir une souplesse pour les tests, pour les modifications du code et de délimiter plus clairement un domaine métier. L&amp;#39;objectif est avant tout de clarifier le code et d&amp;#39;aider le développeur à intervenir plus efficacement sur les applications.&lt;/p&gt;
&lt;!-- end paragraph 133d0b57-ddfa-4c03-9aaf-99f3473e90cf--&gt;
&lt;!-- begin paragraph c3f6b471-a890-4381-9a52-19f9cd9d63c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c3f6b471-a890-4381-9a52-19f9cd9d63c0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c3f6b471-a890-4381-9a52-19f9cd9d63c0--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:c395f7c0b4874ac79700ca7002c1d144</id>
    <title>Quoi de neuf dans Kafka 2.4</title>
    <updated>2020-02-24T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/quoi-de-neuf-dans-kafka-2-4.html"/>
    <!--summary Résumé de quelques fonctionnalités / bugfix ajoutés dans la version 2.4 de Kafka.-->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>        <category term="Kafka"></category>    <category term="Changelog"></category>    <category term="Kafka Connect"></category>    <category term="Kafka Streams"></category>    <content type="html">
&lt;!-- begin paragraph daee76c9-762a-44ea-9686-1d1b04b53b67--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-daee76c9-762a-44ea-9686-1d1b04b53b67&quot;&gt;Si comme moi vous avez raté le train pour la release de Kafka 2.4, voici un petit résumé de quelques fonctionnalités ajoutées et bugfix :)&lt;/p&gt;
&lt;!-- end paragraph daee76c9-762a-44ea-9686-1d1b04b53b67--&gt;
&lt;!-- begin heading_1 59602157-6940-438c-9dca-77ee3bbf24b9--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-59602157-6940-438c-9dca-77ee3bbf24b9&quot;&gt;Consommateurs / Producteurs / Brokers&lt;/h2&gt;
&lt;!-- end heading_1 59602157-6940-438c-9dca-77ee3bbf24b9--&gt;
&lt;!-- begin heading_2 7b7f4913-1162-4b96-adce-6052d891e82a--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-7b7f4913-1162-4b96-adce-6052d891e82a&quot;&gt;Une meilleure optimisation niveau réseau&lt;/h3&gt;
&lt;!-- end heading_2 7b7f4913-1162-4b96-adce-6052d891e82a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-392%3A+Allow+consumers+to+fetch+from+closest+replica&quot;&gt;KIP-392&lt;/a&gt; : Allow consumers to fetch from closest replica&lt;/li&gt;&lt;li&gt;Motivation : &lt;a href=&quot;https://www.confluent.io/blog/multi-region-data-replication/&quot;&gt;https://www.confluent.io/blog/multi-region-data-replication/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;How-To :&lt;ol class=&quot;block-numbered_list_item level-1&quot;&gt;&lt;li&gt;Configurer broker.rack et replica.selector.class (tous deux configurations du broker)&lt;/li&gt;&lt;li&gt;Configurer client.rack (configuration du consommateur)&lt;/li&gt;&lt;li&gt;???&lt;/li&gt;&lt;li&gt;Profit&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph a061b206-863c-44b5-aa98-3b18ccec5915--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-a061b206-863c-44b5-aa98-3b18ccec5915&quot;&gt;En mettant ces deux configurations, lorsqu&amp;#39;un consommateur essaye de récupérer des données de la partition leader, la partition regarde s&amp;#39;il y a un rack préféré et si une class selector est renseignée, puis redirige le consommateur vers le replica préféré (de préférence un qui soit proche du consommateur pour avoir le moins de trafic réseau possible, mais cela dépend de la configuration fournie dans replica.selector.class).&lt;/p&gt;
&lt;!-- end paragraph a061b206-863c-44b5-aa98-3b18ccec5915--&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 6741dbbe-d738-4ffb-aeb7-9b15b7696912--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6741dbbe-d738-4ffb-aeb7-9b15b7696912&quot;&gt;Diminution du temps de rebalancing des partitions des consommateurs&lt;/h3&gt;
&lt;!-- end heading_2 6741dbbe-d738-4ffb-aeb7-9b15b7696912--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-429%3A+Kafka+Consumer+Incremental+Rebalance+Protocol&quot;&gt;KIP-429&lt;/a&gt; : Kafka Consumer Incremental Rebalance Protocol&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.confluent.io/blog/incremental-cooperative-rebalancing-in-kafka/&quot;&gt;https://www.confluent.io/blog/incremental-cooperative-rebalancing-in-kafka/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Motivation : Réduire l&amp;#39;effet &amp;quot;stop-the-world&amp;quot;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/c395f7c0-b487-4ac7-9700-ca7002c1d144/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 46e0c0ea-89f1-465b-8360-d532a23bb105--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-46e0c0ea-89f1-465b-8360-d532a23bb105&quot;&gt;Le principe de stop-the-world est connu notamment avec le garbage collector : le programme s&amp;#39;arrête complètement afin de ne pas accidentellement rendre des objets inutilisables.&lt;/p&gt;
&lt;!-- end paragraph 46e0c0ea-89f1-465b-8360-d532a23bb105--&gt;
&lt;!-- begin paragraph 8506fc6e-56d0-49e9-803e-220d95f1edbd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8506fc6e-56d0-49e9-803e-220d95f1edbd&quot;&gt;Le même principe s&amp;#39;applique avec le load balancing dans Kafka.&lt;/p&gt;
&lt;!-- end paragraph 8506fc6e-56d0-49e9-803e-220d95f1edbd--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/c395f7c0-b487-4ac7-9700-ca7002c1d144/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 4e985dcf-b40b-43ff-b1eb-d3ec2683c687--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e985dcf-b40b-43ff-b1eb-d3ec2683c687&quot;&gt;Quelles que soient les raisons amenant à un load balancing des clients Kafka (scaling up/down des clients, perte temporaire d’un broker, rolling upgrade...), les processus s’arrêtent le temps d’équilibrer la charge des partitions. Ce qui pose des soucis au niveau de la performance.&lt;/p&gt;
&lt;!-- end paragraph 4e985dcf-b40b-43ff-b1eb-d3ec2683c687--&gt;
&lt;!-- begin paragraph 3f6b1c63-080b-4b3b-aad1-0e4dbc8af6da--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3f6b1c63-080b-4b3b-aad1-0e4dbc8af6da&quot;&gt;Une solution implémentée par la KIP-429 est donc le protocole incrémental de rebalancing. Celui-ci se repose sur deux idées :&lt;/p&gt;
&lt;!-- end paragraph 3f6b1c63-080b-4b3b-aad1-0e4dbc8af6da--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le rebalancing doit se faire de manière incrémentale, c’est-à-dire qu’il n’y a pas besoin d’avoir un rééquilibrage complet des charges à la fin d’un load balancing. Celui-ci se fait petit à petit, ce qui permet de ne pas &amp;quot;déranger&amp;quot; tous les processus à la fois en les stoppant.&lt;/li&gt;&lt;li&gt;Le rebalancing doit se faire de manière coopérative puisque chaque client de chaque groupe doit pouvoir être en mesure de relâcher des ressources, pour que les autres clients arrivants puissent prendre ces dernières.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 60ad6e3f-64d8-4ea6-91b8-86294837fe51--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-60ad6e3f-64d8-4ea6-91b8-86294837fe51&quot;&gt;Optimisation lorsqu&amp;#39;il n&amp;#39;y a pas de clé et de partitioning spécifiés&lt;/h3&gt;
&lt;!-- end heading_2 60ad6e3f-64d8-4ea6-91b8-86294837fe51--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-480%3A+Sticky+Partitioner&quot;&gt;KIP-480&lt;/a&gt; : Sticky Partitioner&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/&quot;&gt;https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Motivation : Remplacer le mécanisme round-robin par défaut lorsque les clés des records sont nulles et qu&amp;#39;il n&amp;#39;y a pas de partitioning spécifié.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 64a167dd-3fc7-4a6f-8709-63988e38cd2a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-64a167dd-3fc7-4a6f-8709-63988e38cd2a&quot;&gt;Lors de la production des messages dans Kafka, si la clé est &amp;quot;null&amp;quot; et si les messages ne sont pas partitionnés de manière spécifiques via un partitionner, les messages sont normalement distribués en utilisant le “round-robin” : chaque message est envoyé individuellement dans les différentes partitions en suivant un cycle, le but étant que chaque partition reçoit équitablement le même nombre de message. Ceci crée un problème de latence car les batch de messages à envoyer vers Kafka ne contiennent dans ce cas-là qu’un seul message. Conséquence : il y a plus de batch à envoyer. Pour rappel, plus il y a de batch à envoyer, plus la latence est grande, puisque envoyer un batch requiert une requête et une mise en queue.&lt;/p&gt;
&lt;!-- end paragraph 64a167dd-3fc7-4a6f-8709-63988e38cd2a--&gt;
&lt;!-- begin paragraph bcff60ad-5d0c-49c1-9574-55fe4a7c54f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bcff60ad-5d0c-49c1-9574-55fe4a7c54f2&quot;&gt;Le but du KIP-480 est donc de diminuer cette latence en revisitant le &amp;quot;round-robin&amp;quot; appliqué par défaut lorsqu’on se trouve dans cette situation. La stratégie du &amp;quot;sticky partitioner&amp;quot; vise à ce qu’un batch de messages soit envoyé dans une partition dite &amp;quot;sticky&amp;quot;. Puis pour le prochain batch de messages, une autre partition est désignée comme &amp;quot;sticky&amp;quot;. Dans ce cas, les batchs sont créés avec les options du producteur : linger.ms et batch.size. Ceci a pour effet d’augmenter la taille des batchs et donc d’avoir moins de batchs à envoyer : on diminue la latence. Netflix avait une idée similaire au sticky partitionner qui se basait seulement sur le temps (linger.ms).&lt;/p&gt;
&lt;!-- end paragraph bcff60ad-5d0c-49c1-9574-55fe4a7c54f2--&gt;
&lt;!-- begin paragraph 4a2ca7de-b86b-4342-b760-a3e19901fd4e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a2ca7de-b86b-4342-b760-a3e19901fd4e&quot;&gt;Note rappel :&lt;/p&gt;
&lt;!-- end paragraph 4a2ca7de-b86b-4342-b760-a3e19901fd4e--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;linger.ms : maximum de temps à attendre avant d’envoyer un batch de message&lt;/li&gt;&lt;li&gt;batch.size : maximum de taille que le batch peut avoir avant d’être envoyé dans une partition d’un topic de Kafka&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 214ff274-1b6e-4470-8e44-db70971c4bbd--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-214ff274-1b6e-4470-8e44-db70971c4bbd&quot;&gt;Toujours plus pour se débarasser de Zookeeper&lt;/h3&gt;
&lt;!-- end heading_2 214ff274-1b6e-4470-8e44-db70971c4bbd--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-455%3A+Create+an+Administrative+API+for+Replica+Reassignment&quot;&gt;KIP-455&lt;/a&gt; : Create an Administrative API for Replica Reassignment&lt;/li&gt;&lt;li&gt;Motivation : Remplacer l’API de Zookeeper s’occupant de réassigner les partitions répliquées.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph eb141f20-9441-428f-8cd2-50592f181ec4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eb141f20-9441-428f-8cd2-50592f181ec4&quot;&gt;L’API de Zookeeper possède plusieurs défauts : des codes d’erreurs non exploitables, un manque de sécurité,... L’API ne réassigne pas non plus les partitions de manière incrémentale, et il n’est pas possible de stopper une opération de réassignation des partitions en cours.&lt;/p&gt;
&lt;!-- end paragraph eb141f20-9441-428f-8cd2-50592f181ec4--&gt;
&lt;!-- begin paragraph f50bc600-f9d8-4c76-bad0-11f3c3369625--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f50bc600-f9d8-4c76-bad0-11f3c3369625&quot;&gt;Le but de ce KIP-455 est donc de créer une API Kafka AdminClient permettant ceci, avec plus de sécurité et des codes d’erreurs plus exploitables. Pour cela, deux nouvelles APIs AdminClient ont été créées : alterPartitionAssignments et listPartitionReassignments. La première permet de réassigner les partitions, et la seconde permet de lister les partitions en cours de réassignement. En plus de cela, des RPC ont été ajoutés de manière à stopper le réassignement (AlterPartitionReassignmentsRequest) ou encore obtenir la réponse de cette requête (AlterPartitionReassignmentsResponse), etc.&lt;/p&gt;
&lt;!-- end paragraph f50bc600-f9d8-4c76-bad0-11f3c3369625--&gt;
&lt;!-- begin paragraph 2b8e2f60-ae1c-4820-8e89-39f1f346a635--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2b8e2f60-ae1c-4820-8e89-39f1f346a635&quot;&gt;Avec cette KIP-455, le script kafka-reassing-partitions.sh utilisé pour réassigner les partitions utilise maintenant l’API de l’AdminClient, et l’option zookeeper devient déprécié. Le script kafka-topics.sh permettant de créer/modifier des topics se voit ajouter deux options : addingReplicas et removingReplicas.&lt;/p&gt;
&lt;!-- end paragraph 2b8e2f60-ae1c-4820-8e89-39f1f346a635--&gt;
&lt;!-- begin heading_2 98652bf9-cbab-43da-a393-0e545fbf8142--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-98652bf9-cbab-43da-a393-0e545fbf8142&quot;&gt;Bug fix : KafkaConsummer ne se débarasse plus des données pré-extraites lorsque les partitions sont mises en pauses&lt;/h3&gt;
&lt;!-- end heading_2 98652bf9-cbab-43da-a393-0e545fbf8142--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-7548&quot;&gt;https://issues.apache.org/jira/browse/KAFKA-7548&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 4897af48-f106-4146-8751-1ab54c9b4c51--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4897af48-f106-4146-8751-1ab54c9b4c51&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4897af48-f106-4146-8751-1ab54c9b4c51--&gt;
&lt;!-- begin paragraph 91d2de30-2bee-4b9b-914d-477b65232867--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-91d2de30-2bee-4b9b-914d-477b65232867&quot;&gt;Les consommateurs Kafka ont la possibilité d’être stoppés et redémarrés avec les méthodes &lt;code&gt;.pause()&lt;/code&gt; et &lt;code&gt;.resume()&lt;/code&gt;. Lorsqu’un client consomme des messages avec la méthode &lt;code&gt;.poll()&lt;/code&gt;, il le fait de manière asynchrone et met les messages dans un buffer local au client consommateur.&lt;/p&gt;
&lt;!-- end paragraph 91d2de30-2bee-4b9b-914d-477b65232867--&gt;
&lt;!-- begin paragraph 48754961-f112-43d7-883a-9887570e5e02--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-48754961-f112-43d7-883a-9887570e5e02&quot;&gt;Ce qui veut dire que la situation suivante peut arriver : un client stoppe la consommation d’une partition d’un topic et ensuite appelle le &lt;code&gt;.poll()&lt;/code&gt; de cette partition. Dans ce cas, le &lt;code&gt;.poll()&lt;/code&gt; met des données dans le buffer local et, lorsque le client reprend sa consommation, celui-ci refait une requête des données vers la partition en ignorant les données mise dans le buffer local. Ceci a des conséquences sur la performance des clients lorsque ceux-ci effectuent beaucoup de pause/resume. Ce problème est maintenant résolu grâce à KAFKA-7548.&lt;/p&gt;
&lt;!-- end paragraph 48754961-f112-43d7-883a-9887570e5e02--&gt;
&lt;!-- begin heading_1 7f0202eb-8451-46f0-9d8e-18f60f49bde7--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-7f0202eb-8451-46f0-9d8e-18f60f49bde7&quot;&gt;Kafka Connect&lt;/h2&gt;
&lt;!-- end heading_1 7f0202eb-8451-46f0-9d8e-18f60f49bde7--&gt;
&lt;!-- begin paragraph 9a014c44-da33-4d16-b806-9d2fa1de6963--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9a014c44-da33-4d16-b806-9d2fa1de6963&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9a014c44-da33-4d16-b806-9d2fa1de6963--&gt;
&lt;!-- begin heading_2 da3f1978-bbc1-4031-bfc9-3526fe4197c1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-da3f1978-bbc1-4031-bfc9-3526fe4197c1&quot;&gt;Mirror Maker 2.0&lt;/h3&gt;
&lt;!-- end heading_2 da3f1978-bbc1-4031-bfc9-3526fe4197c1--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-382%3A+MirrorMaker+2.0&quot;&gt;KIP-382&lt;/a&gt; : MirrorMaker 2.0&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 4d46d81c-6f64-4586-b2c4-fc83aa5f591f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4d46d81c-6f64-4586-b2c4-fc83aa5f591f&quot;&gt;MirrorMaker permet de créer un “miroir” d’un cluster Kafka entier. Sur papier, cela semble être un bon outil pour répliquer un cluster, néanmoins il possède plusieurs défauts. Pour en citer quelques un : les topics miroirs sont créés avec une configuration par défaut, les ACLs (Access Control Lists) ne sont pas répliqués, les messages sont re-partitionnés avec la “DefaultPartitionner”, etc.&lt;/p&gt;
&lt;!-- end paragraph 4d46d81c-6f64-4586-b2c4-fc83aa5f591f--&gt;
&lt;!-- begin paragraph deafe02f-6959-4e66-9cb3-9c9a7acea533--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-deafe02f-6959-4e66-9cb3-9c9a7acea533&quot;&gt;MirrorMaker 2.0 se base sur Kafka Connect afin de répliquer le mieux possible un cluster Kafka.&lt;/p&gt;
&lt;!-- end paragraph deafe02f-6959-4e66-9cb3-9c9a7acea533--&gt;
&lt;!-- begin paragraph 197103e2-69df-478c-aece-5b4d50be7b71--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-197103e2-69df-478c-aece-5b4d50be7b71&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 197103e2-69df-478c-aece-5b4d50be7b71--&gt;
&lt;!-- begin heading_2 5dd4e8f9-168c-44d9-ac9e-434044dcd46c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-5dd4e8f9-168c-44d9-ac9e-434044dcd46c&quot;&gt;Ajout de headers dans Kafka Connect&lt;/h3&gt;
&lt;!-- end heading_2 5dd4e8f9-168c-44d9-ac9e-434044dcd46c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-440%3A+Extend+Connect+Converter+to+support+headers&quot;&gt;KIP-440&lt;/a&gt; : Extend Connect Converter to support headers&lt;/li&gt;&lt;li&gt;Les headers dans Kafka permettent d’enrichir les messages Kafka de metadonnées additionnelles. Par exemple transmettre le schéma ID si le message est en avro. Cette KIP-440 vise à également enrichir le Kafka Connect Converter afin que celui-ci puisse être utilisé en tandem avec les clients producteurs/consommateurs/streams utilisant les headers.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 d16dbac8-98db-4e27-9c95-0a1837b13139--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d16dbac8-98db-4e27-9c95-0a1837b13139&quot;&gt;Kafka Streams&lt;/h2&gt;
&lt;!-- end heading_1 d16dbac8-98db-4e27-9c95-0a1837b13139--&gt;
&lt;!-- begin heading_2 ab351f7a-643b-4e4b-874d-91e8703c5e08--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ab351f7a-643b-4e4b-874d-91e8703c5e08&quot;&gt;Support des join sans clé dans les KTable&lt;/h3&gt;
&lt;!-- end heading_2 ab351f7a-643b-4e4b-874d-91e8703c5e08--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-213+Support+non-key+joining+in+KTable&quot;&gt;KIP-213&lt;/a&gt; : Support non-key joining in KTable&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f17d22ba-da19-4680-bc86-3d9bf3aa8cbf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f17d22ba-da19-4680-bc86-3d9bf3aa8cbf&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph f17d22ba-da19-4680-bc86-3d9bf3aa8cbf--&gt;
&lt;!-- begin heading_2 d2abfb55-dd91-4fca-afc5-35b5929c13f6--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d2abfb55-dd91-4fca-afc5-35b5929c13f6&quot;&gt;Possibilité de tester rapidement grâce au TopologyTestDriver&lt;/h3&gt;
&lt;!-- end heading_2 d2abfb55-dd91-4fca-afc5-35b5929c13f6--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-470%3A+TopologyTestDriver+test+input+and+output+usability+improvements&quot;&gt;KIP-470&lt;/a&gt; : TopologyTestDriver test input and output usability improvements&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 939b2467-700d-4a63-8f54-45d46816233e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-939b2467-700d-4a63-8f54-45d46816233e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 939b2467-700d-4a63-8f54-45d46816233e--&gt;
&lt;!-- begin heading_1 342d4ced-cb65-40ab-9996-89fd1a04498a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-342d4ced-cb65-40ab-9996-89fd1a04498a&quot;&gt;Misc&lt;/h2&gt;
&lt;!-- end heading_1 342d4ced-cb65-40ab-9996-89fd1a04498a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Support scala 2.13&lt;/li&gt;&lt;li&gt;Upgrade zookeeper ⇒ 3.5.X&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 69ba7c0f-5e6d-406a-980b-a7796f12c5e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69ba7c0f-5e6d-406a-980b-a7796f12c5e0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 69ba7c0f-5e6d-406a-980b-a7796f12c5e0--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:d0a9bc2641ee4292be045c5e2b7319fc</id>
    <title>ZIO : Programmation concurrente à la carte</title>
    <updated>2020-02-23T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/ZIO-prog-a-la-carte.html"/>
    <!--summary -->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="ZIO"></category>    <category term="Scala"></category>    <category term="Programmation concurrente"></category>    <content type="html">
&lt;!-- begin paragraph c6dfc0ee-7f45-4d3e-9f9d-904d46eef874--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c6dfc0ee-7f45-4d3e-9f9d-904d46eef874&quot;&gt;On a déjà parlé plusieurs fois de &lt;a href=&quot;https://univalence.io/blog/categories/zio/&quot;&gt;ZIO sur le blog, (et on va continuer ! 😃)&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph c6dfc0ee-7f45-4d3e-9f9d-904d46eef874--&gt;
&lt;!-- begin paragraph 413f499a-7974-4586-bc9e-151bde697004--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-413f499a-7974-4586-bc9e-151bde697004&quot;&gt;Au-delà d&amp;#39;être la bibliothèque que l&amp;#39;on attendait pour faire du Functional Programming, ou une façon de faire du Scala sans rentrer dans le polymorphisme ad-hoc ou d&amp;#39;autres concepts fins néanmoins abstraits, ZIO ouvre un nouveau chemin de traverse dans la programmation concurrente.&lt;/p&gt;
&lt;!-- end paragraph 413f499a-7974-4586-bc9e-151bde697004--&gt;
&lt;!-- begin divider a08ef0d1-e6b1-4fd8-bbdc-a56b8755d3bc--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-a08ef0d1-e6b1-4fd8-bbdc-a56b8755d3bc&quot;&gt;
&lt;!-- end divider a08ef0d1-e6b1-4fd8-bbdc-a56b8755d3bc--&gt;
&lt;!-- begin paragraph 7e5662ab-2aaa-4c55-be07-4496b84d074f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7e5662ab-2aaa-4c55-be07-4496b84d074f&quot;&gt;Le &lt;a href=&quot;https://fr.wikipedia.org/wiki/Programmation_concurrente&quot;&gt;programmation concurrente&lt;/a&gt; est l&amp;#39;idée de gérer plusieurs exécutions hétérogènes simultanées de manière collaborative, une façon de faire plus courante dans le monde moderne avec le multi-cœur et les nouvelles architectures. Pour le moment, la boîte à outil de référence pour faire de la programmation concurrente sur la JVM, c&amp;#39;est &lt;a href=&quot;https://akka.io/&quot;&gt;Akka&lt;/a&gt;. &lt;/p&gt;
&lt;!-- end paragraph 7e5662ab-2aaa-4c55-be07-4496b84d074f--&gt;
&lt;!-- begin paragraph c29ebaf5-697e-4d3f-bad0-d97e8cdb5d82--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c29ebaf5-697e-4d3f-bad0-d97e8cdb5d82&quot;&gt;Akka permet de pousser assez loin la programmation concurrente, mais va être parfois assez irritant. D&amp;#39;un côté, on code du Akka, de l&amp;#39;autre du Scala. Puis, quand on a appris d&amp;#39;autres modèles de programmation concurrente que le &lt;a href=&quot;https://en.wikipedia.org/wiki/Actor_model&quot;&gt;modèle d&amp;#39;acteur&lt;/a&gt; (avec Golang ou Clojure), cela devient légèrement usant !&lt;/p&gt;
&lt;!-- end paragraph c29ebaf5-697e-4d3f-bad0-d97e8cdb5d82--&gt;
&lt;!-- begin code b6f92995-80a2-4135-8189-2e5bd65cf323--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b6f92995-80a2-4135-8189-2e5bd65cf323&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object AkkaAppp extends App {
  val greeterMain: ActorSystem[GreeterMain.SayHello] = 
    ActorSystem(GreeterMain(), &amp;quot;AkkaHelloWorld&amp;quot;)

  greeterMain ! SayHello(&amp;quot;Jon&amp;quot;)
  // bot : Hello Jon !

  greeterMain ! Bye
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b6f92995-80a2-4135-8189-2e5bd65cf323--&gt;
&lt;!-- begin divider fd33b10a-2c55-42aa-af41-ae749a0fc22c--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-fd33b10a-2c55-42aa-af41-ae749a0fc22c&quot;&gt;
&lt;!-- end divider fd33b10a-2c55-42aa-af41-ae749a0fc22c--&gt;
&lt;!-- begin paragraph 950570cd-1a5d-4ead-9396-c6d7b4bbf38e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-950570cd-1a5d-4ead-9396-c6d7b4bbf38e&quot;&gt;L&amp;#39;année dernière, on a eu le droit à un peu de magie avant Noël avec l&amp;#39;implémentation du modèle d&amp;#39;acteur au-dessus de ZIO : &lt;a href=&quot;https://zio.github.io/zio-actors/docs/overview/overview_basics&quot;&gt;Basic ZIO actors&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 950570cd-1a5d-4ead-9396-c6d7b4bbf38e--&gt;
&lt;!-- begin paragraph d9afeadb-eaa0-4c8b-acc3-0dda271b3186--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d9afeadb-eaa0-4c8b-acc3-0dda271b3186&quot;&gt;Ainsi on pourrait faire à terme avec ZIO ce que l&amp;#39;on pouvait faire en utilisant Akka. Cela étant dit, ZIO va plus loin sur la base, en particulier on peut faire de la composition en programmation concurrente.&lt;/p&gt;
&lt;!-- end paragraph d9afeadb-eaa0-4c8b-acc3-0dda271b3186--&gt;
&lt;!-- begin paragraph 96b51b26-5414-404c-a167-340b93b007fd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-96b51b26-5414-404c-a167-340b93b007fd&quot;&gt;En termes de composition, vous avez quelques types de base : ZIO, Ref, Fiber... Et au-dessus, on va avoir de nombreux outils pour combiner ces différents types.  Si vous voulez taper un peu dans la liste, voici le résumé approximatif :&lt;/p&gt;
&lt;!-- end paragraph 96b51b26-5414-404c-a167-340b93b007fd--&gt;
&lt;!-- begin code 5e898667-31a4-4dd5-a35d-8d8fe6943593--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5e898667-31a4-4dd5-a35d-8d8fe6943593&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt; /**
   * @tparam R ce dont j&amp;#39;ai besoin
   * @tparam E quand cela va mal
   * @tparam A quand tout se passe bien
   */
  trait ZIO[-R, +E, +A] {
    private[zio] def unsafeRun: R =&amp;gt; Either[E, A]

    def flatMap[R1 &amp;lt;: R, E1 &amp;gt;: E, B](f0: A =&amp;gt; ZIO[R1, E1, B]): ZIO[R1, E1, B]
    def fork: URIO[R, Fiber[E, A]]
  }

  //besoin de rien
  type IO[+E, +A] = ZIO[Any, E, A]

  //tout va bien se passer
  type UIO[+A] = ZIO[Any, Nothing, A]

  //avec R, on peut faire un A
  type URIO[-R, A] = ZIO[R, Nothing, A]

  //pourquoi ?
  type Exit[+E, +A] = Either[Cause[E], A]

  sealed trait Cause[+E]
  case class Fail[+E](value: E)               extends Cause[E]
  case class Die(value: Throwable)            extends Cause[Nothing]
  case class Interrupt(fiberId: (Long, Long)) extends Cause[Nothing]

  trait Fiber[+E, +A] {
    def interrupt: UIO[Exit[E, A]]
    def join: IO[E, A]
  }

  trait Promise[E, A] {
    def await: IO[E, A]
    def complete(io: IO[E, A]): UIO[Boolean]
  }

  trait Queue[-R, +E, A] {
    def isShutdown: UIO[Boolean]
    def offer(a: A): ZIO[R, E, Boolean]
    def take: ZIO[R, E, A]
  }

  trait Ref[A] {
    def get: UIO[A]
    def modify[B](f: A =&amp;gt; (B, A)): UIO[B]
  }

  case class Reservation[-R, +E, +A](
    acquire: ZIO[R, E, A], 
    release: Exit[Any, Any] =&amp;gt; ZIO[R, Nothing, Any])

  trait ZManaged[-R, +E, +A] {
    def reservation: ZIO[R, E, Reservation[R, E, A]]

    def flatMap[R1 &amp;lt;: R, E1 &amp;gt;: E, B](f0: A =&amp;gt; ZManaged[R1, E1, B]): ZManaged[R1, E1, B]
  }

  trait Stream[-R, +E, +A] {
    //1. on &amp;quot;acquire&amp;quot; la stream
    //2. on &amp;quot;pull&amp;quot; les éléments un par un (ZIO[R, Option[E], A])
    //3. si on a un None dans le Option[E], on arrête
    //4. on &amp;quot;release&amp;quot;
    def process: ZManaged[R, Nothing, ZIO[R, Option[E], A]]

    def flatMap[R1 &amp;lt;: R, E1 &amp;gt;: E, B](f0: A =&amp;gt; Stream[R1, E1, B]): Stream[R1, E1, B]
  }

  trait Semaphore {
    def available: UIO[Long]
    def withPermits[R, E, A](n: Long)(task: ZIO[R, E, A]): ZIO[R, E, A]
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5e898667-31a4-4dd5-a35d-8d8fe6943593--&gt;
&lt;!-- begin paragraph 87a87f43-7415-46cf-a86b-0c434c2dd3df--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-87a87f43-7415-46cf-a86b-0c434c2dd3df&quot;&gt;Au-dessus de ces types, on va pouvoir construire le reste des opérateurs pour manipuler les effets (même si on va plutôt &amp;quot;inliner&amp;quot; pour des histoires de performance). &lt;/p&gt;
&lt;!-- end paragraph 87a87f43-7415-46cf-a86b-0c434c2dd3df--&gt;
&lt;!-- begin divider cb94eac5-029a-4a6b-ab06-aab8c4369699--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-cb94eac5-029a-4a6b-ab06-aab8c4369699&quot;&gt;
&lt;!-- end divider cb94eac5-029a-4a6b-ab06-aab8c4369699--&gt;
&lt;!-- begin paragraph 25a2d94a-5b6d-4e85-b5e5-4c9163278897--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-25a2d94a-5b6d-4e85-b5e5-4c9163278897&quot;&gt;Ces dernières semaines, on a eu quelques articles intéressants sur des cas d&amp;#39;utilisation ou des combinateurs au-dessus de ZIO.&lt;/p&gt;
&lt;!-- end paragraph 25a2d94a-5b6d-4e85-b5e5-4c9163278897--&gt;
&lt;!-- begin heading_2 825b6e45-158f-41d7-a1a9-e8a66c054d03--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-825b6e45-158f-41d7-a1a9-e8a66c054d03&quot;&gt;Happy Eyeballs&lt;/h3&gt;
&lt;!-- end heading_2 825b6e45-158f-41d7-a1a9-e8a66c054d03--&gt;
&lt;!-- begin bookmark 47928d9e-4419-4b67-a4d2-9f4043da09ae--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-47928d9e-4419-4b67-a4d2-9f4043da09ae&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://blog.softwaremill.com/happy-eyeballs-algorithm-using-zio-120997ba5152?gi=343b946500e4&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1200/1*FhZ9Ur1h_oZ8ULRvayBpEg.png&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Happy eyeballs algorithm using ZIO&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Happy eyeballs algorithm using ZIO While researching structured concurrency for the article on Project Loom &amp; Fibers, one of the most valuable resource were blogs (1, 2) by Nathaniel J. Smith …&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;&quot;&gt; https://blog.softwaremill.com/happy-eyeballs-algorithm-using-zio-120997ba5152?gi=343b946500e4&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 47928d9e-4419-4b67-a4d2-9f4043da09ae--&gt;
&lt;!-- begin paragraph e2ed54c9-c468-4908-b042-98203b010c2a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2ed54c9-c468-4908-b042-98203b010c2a&quot;&gt;L&amp;#39;article part d&amp;#39;un code qui a été fait avec &lt;a href=&quot;https://trio.readthedocs.io/en/stable/&quot;&gt;Trio (Python)&lt;/a&gt;, pour voir comment on peut le faire en Scala.&lt;/p&gt;
&lt;!-- end paragraph e2ed54c9-c468-4908-b042-98203b010c2a--&gt;
&lt;!-- begin paragraph 4b1e583b-4516-466b-858b-9ccc26df231d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4b1e583b-4516-466b-858b-9ccc26df231d&quot;&gt;L&amp;#39;idée est de pouvoir mettre dans la course &lt;b&gt;n&lt;/b&gt; tâches (dans une List), pour récupérer le premier résultat, sans lancer plus de tâches que nécessaire, en optimisant le temps d&amp;#39;exécution. C&amp;#39;est interruptible, donc les tâches qui ne sont pas finies sont stoppées. Il n&amp;#39;y a pas de limite dans le panache ! 😃&lt;/p&gt;
&lt;!-- end paragraph 4b1e583b-4516-466b-858b-9ccc26df231d--&gt;
&lt;!-- begin paragraph d3f43baa-7296-45d3-ab8c-33e1ff8ea36d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3f43baa-7296-45d3-ab8c-33e1ff8ea36d&quot;&gt;&amp;quot;Suivez les types&amp;quot;&lt;/p&gt;
&lt;!-- end paragraph d3f43baa-7296-45d3-ab8c-33e1ff8ea36d--&gt;
&lt;!-- begin code 1c1587fe-0b5d-4ea5-bae7-9118dde47f52--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1c1587fe-0b5d-4ea5-bae7-9118dde47f52&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def happyEyeballs[R, E, T](tasks: List[ZIO[R, E, T]], delay: Duration): 
                           ZIO[R with Clock, Option[E], T]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1c1587fe-0b5d-4ea5-bae7-9118dde47f52--&gt;
&lt;!-- begin paragraph 2c5177de-b313-46d1-8f19-19376a924b90--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c5177de-b313-46d1-8f19-19376a924b90&quot;&gt;Pour les types &lt;code&gt;R&lt;/code&gt;, &lt;code&gt;E&lt;/code&gt; et &lt;code&gt;T&lt;/code&gt;:  &lt;/p&gt;
&lt;!-- end paragraph 2c5177de-b313-46d1-8f19-19376a924b90--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;L’algorithme prend une liste de tâches &lt;code&gt;ZIO[R, E, T]&lt;/code&gt;, donc des tâches qui dépendent d&amp;#39;un R, vont renvoyer plus tard un T si cela se passe bien ou un E si cela se passe mal.&lt;/li&gt;&lt;li&gt;Un délai&lt;/li&gt;&lt;li&gt;Pour renvoyer une seule tâche &lt;code&gt;ZIO[R with Clock, Option[E], T]&lt;/code&gt;, donc une tâche qui dépend de R &lt;b&gt;et&lt;/b&gt; d&amp;#39;une horloge &lt;code&gt;Clock&lt;/code&gt; (pour gérer le délai), qui va renvoyer le premier T si cela s&amp;#39;est passé au moins une fois ou &lt;code&gt;Some[E]&lt;/code&gt; si cela se passe mal (Le &lt;code&gt;None&lt;/code&gt; est pour le cas où la liste de tâche est vide).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 208a58e7-204e-4cc7-b98c-45ab92877041--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-208a58e7-204e-4cc7-b98c-45ab92877041&quot;&gt;L&amp;#39;article d&amp;#39;Adam est très précis et on peut porter quelques petites modifications à la solution proposée. Je suis parti sur une version : &lt;/p&gt;
&lt;!-- end paragraph 208a58e7-204e-4cc7-b98c-45ab92877041--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Plus typée : avec un paramètre de type &lt;code&gt;E&lt;/code&gt; en plus et en mettant cette histoire de liste vide dans un &lt;code&gt;IO.fail(None)&lt;/code&gt;. C&amp;#39;est un choix, je trouve que cela paye après (même dans les articles de blog).&lt;/li&gt;&lt;li&gt;Plus documentée : les variables pour les effets que l&amp;#39;on enchaîne ont à la fois un nom et un type (&lt;code&gt;signalFailed&lt;/code&gt;, &lt;code&gt;waitOrAlreadyFailed&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 27a06f37-f628-4c98-9a83-bd1485a324fc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-27a06f37-f628-4c98-9a83-bd1485a324fc&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def happyEyeballs[R, E, A](tasks: List[ZIO[R, E, A]], delay: Duration): ZIO[R with Clock, Option[E], A] =
    tasks match {
      case Nil         =&amp;gt; IO.fail(None)
      case task :: Nil =&amp;gt; task.asSomeError
      case task :: otherTasks =&amp;gt;
        Promise.make[Nothing, Unit].flatMap { isFailed =&amp;gt;
          val signalFailed: UIO[Unit]                = isFailed.succeed(Unit).ignore
          val waitOrAlreadyFailed: URIO[Clock, Unit] = isFailed.await.timeout(delay).ignore
          
          task
            .tapError(_ =&amp;gt; signalFailed)
            .asSomeError
            .race(waitOrAlreadyFailed *&amp;gt; happyEyeballs(otherTasks, delay))
        }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 27a06f37-f628-4c98-9a83-bd1485a324fc--&gt;
&lt;!-- begin paragraph d2360c60-900e-41f1-976e-b21276368f64--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d2360c60-900e-41f1-976e-b21276368f64&quot;&gt;Ce qui est assez fascinant, c&amp;#39;est comment un algorithme relativement complexe compose avec si peu de lignes de ZIO.&lt;/p&gt;
&lt;!-- end paragraph d2360c60-900e-41f1-976e-b21276368f64--&gt;
&lt;!-- begin heading_2 ab052fbe-2b89-441c-a4b8-9707df3c8c97--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ab052fbe-2b89-441c-a4b8-9707df3c8c97&quot;&gt;CountDownLatch&lt;/h3&gt;
&lt;!-- end heading_2 ab052fbe-2b89-441c-a4b8-9707df3c8c97--&gt;
&lt;!-- begin bookmark 43265d7b-d152-406f-8af0-418d733d0bf5--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-43265d7b-d152-406f-8af0-418d733d0bf5&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://medium.com/wix-engineering/creating-a-dead-simple-countdownlatch-with-zio-c4212ecf9198&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://miro.medium.com/v2/da:true/resize:fit:1200/0*yeo6n0c1mwTsSp08&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Creating a dead simple CountDownLatch with ZIO&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Creating a dead simple CountDownLatch with ZIO In this post I’ll explain what a CountDownLatch is, and how ZIO enables you to create concurrency primitives which are efficient, non-blocking and …&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;&quot;&gt; https://medium.com/wix-engineering/creating-a-dead-simple-countdownlatch-with-zio-c4212ecf9198&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 43265d7b-d152-406f-8af0-418d733d0bf5--&gt;
&lt;!-- begin paragraph 9a0048c7-4cd2-49f8-825a-4cbfc4d9e972--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9a0048c7-4cd2-49f8-825a-4cbfc4d9e972&quot;&gt;L&amp;#39;article se présente dans le contexte des tests autour de ZIO, où l&amp;#39;on doit attendre plusieurs signaux pour débloquer la barrière, afin de passer à la suite du test. (🤔 Mais pourquoi il n&amp;#39;y a pas juste un &lt;code&gt;ZIO.race&lt;/code&gt; ? Le cas d&amp;#39;utilisation doit être plus complexe dans la base de code.)&lt;/p&gt;
&lt;!-- end paragraph 9a0048c7-4cd2-49f8-825a-4cbfc4d9e972--&gt;
&lt;!-- begin paragraph 8505cfd4-c6f5-46f5-a655-d1e825e96af6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8505cfd4-c6f5-46f5-a655-d1e825e96af6&quot;&gt;Voici un &amp;quot;ZIOLatch&amp;quot;, pour le reste (timeout, ...), on peut passer par les combinateurs classiques de ZIO !&lt;/p&gt;
&lt;!-- end paragraph 8505cfd4-c6f5-46f5-a655-d1e825e96af6--&gt;
&lt;!-- begin code 52c07a2f-104c-4707-974c-b08e05d0cff3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-52c07a2f-104c-4707-974c-b08e05d0cff3&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait CountDownLatch {
  def countDown: UIO[Unit]
  def await: UIO[Unit]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 52c07a2f-104c-4707-974c-b08e05d0cff3--&gt;
&lt;!-- begin paragraph 35ea7fc4-8598-46bf-bfe7-744938279fc9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35ea7fc4-8598-46bf-bfe7-744938279fc9&quot;&gt;Pour faire l’implémentation, on pourrait faire une coquille autour de l&amp;#39;API Java existante ou utiliser les briques de base de ZIO pour faire le CountDownLatch.&lt;/p&gt;
&lt;!-- end paragraph 35ea7fc4-8598-46bf-bfe7-744938279fc9--&gt;
&lt;!-- begin paragraph 66fdb0b8-eaa3-49bd-bc41-e43da73cfbbe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-66fdb0b8-eaa3-49bd-bc41-e43da73cfbbe&quot;&gt;L&amp;#39;article propose cette implémentation : &lt;/p&gt;
&lt;!-- end paragraph 66fdb0b8-eaa3-49bd-bc41-e43da73cfbbe--&gt;
&lt;!-- begin code 1ed9bcb9-106c-449c-9636-82c5075e3adf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1ed9bcb9-106c-449c-9636-82c5075e3adf&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object CountDownLatch {
  def make(count: Int): UIO[CountDownLatch] = for {
    ready &amp;lt;- Promise.make[Nothing, Unit]
    ref   &amp;lt;- Ref.make(count)
  } yield new CountDownLatch {
    override def countDown: UIO[Unit] =
      ref.updateAndGet(_ - 1).flatMap {
        case 0 =&amp;gt; ready.succeed(()).unit
        case _ =&amp;gt; ZIO.unit
      }

    override def await: UIO[Unit] = ready.await
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1ed9bcb9-106c-449c-9636-82c5075e3adf--&gt;
&lt;!-- begin paragraph 754bb181-5a89-471a-a2e5-65f59ecd4da2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-754bb181-5a89-471a-a2e5-65f59ecd4da2&quot;&gt;Je ne suis pas vraiment convaincu. Qu&amp;#39;est-ce qu&amp;#39;il arrive si l&amp;#39;appel à &lt;code&gt;countDown&lt;/code&gt; est interrompu au mauvais moment ? C&amp;#39;est-à-dire dans le chat de l’aiguille entre le succès de l&amp;#39;appel à &lt;code&gt;updateAndGet&lt;/code&gt; et la suite où pour 0, on va compléter la Promise &lt;code&gt;ready&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 754bb181-5a89-471a-a2e5-65f59ecd4da2--&gt;
&lt;!-- begin paragraph c02b09f5-90eb-4cbc-bdfe-84a7cbd8c706--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c02b09f5-90eb-4cbc-bdfe-84a7cbd8c706&quot;&gt;On pourrait se dire que si un &lt;code&gt;interrupt&lt;/code&gt; se glisse ici, c&amp;#39;est vraiment la faute à pas de chance, jusqu&amp;#39;à que dans d&amp;#39;autres cas :&lt;/p&gt;
&lt;!-- end paragraph c02b09f5-90eb-4cbc-bdfe-84a7cbd8c706--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;La prod est bloquée dans un état de stase,&lt;/li&gt;&lt;li&gt;Des philosophes meurent de faim,&lt;/li&gt;&lt;li&gt;Que l&amp;#39;on perde plusieurs 100k€ ...&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20171215205344/http://courses.cs.vt.edu/~cs3604/lib/Therac_25/Therac_1.html&quot;&gt;Ou la vie ?&lt;/a&gt; 🙀&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 1ef5e7a0-eabe-4c70-9ce4-f9a0fa00a81d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1ef5e7a0-eabe-4c70-9ce4-f9a0fa00a81d&quot;&gt;Donc on va essayer de tester le truc que l&amp;#39;on vient de créer pour tester du code... #Meta&lt;/p&gt;
&lt;!-- end paragraph 1ef5e7a0-eabe-4c70-9ce4-f9a0fa00a81d--&gt;
&lt;!-- begin heading_3 dc01aa3c-48e0-4bfc-acb2-ddee7fab6ad6--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-dc01aa3c-48e0-4bfc-acb2-ddee7fab6ad6&quot;&gt;Testons les tests&lt;/h4&gt;
&lt;!-- end heading_3 dc01aa3c-48e0-4bfc-acb2-ddee7fab6ad6--&gt;
&lt;!-- begin paragraph 6813ce8a-c252-48c8-9921-75f98a1c3f64--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6813ce8a-c252-48c8-9921-75f98a1c3f64&quot;&gt;Voici la spec du CountDownLatch en utilisant zio-tests&lt;/p&gt;
&lt;!-- end paragraph 6813ce8a-c252-48c8-9921-75f98a1c3f64--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le scénario basique
&lt;!-- begin code 7469c020-8d15-4cba-90e6-a3872d427daf--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-7469c020-8d15-4cba-90e6-a3872d427daf&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def make(n: Int): UIO[CountDownLatch]

testM(&amp;quot;basic scenario&amp;quot;)(for {
  l &amp;lt;- make(2)
  _ &amp;lt;- l.countDown
  _ &amp;lt;- l.countDown
  _ &amp;lt;- l.await
} yield {
  assertCompletes
})&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7469c020-8d15-4cba-90e6-a3872d427daf--&gt;
&lt;!-- begin paragraph d400049f-aaed-4076-b8e8-75789ee9951b--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-d400049f-aaed-4076-b8e8-75789ee9951b&quot;&gt;Donc à la base, on initialise un countDownLatch à 2, on fait 2 countDown. Normalement, on doit pouvoir passer tranquillement.&lt;/p&gt;
&lt;!-- end paragraph d400049f-aaed-4076-b8e8-75789ee9951b--&gt;&lt;/li&gt;&lt;li&gt;Le scénario dans le domaine
&lt;!-- begin code b99ccaa4-f250-489b-9c9c-32f2c6d78225--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-b99ccaa4-f250-489b-9c9c-32f2c6d78225&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;testM(&amp;quot;make 0 should be available&amp;quot;)({
  for {
    countDownLatch &amp;lt;- make(0)
    r              &amp;lt;- completedOrError(countDownLatch.await, &amp;quot;should be completed&amp;quot;)
  } yield r
})&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b99ccaa4-f250-489b-9c9c-32f2c6d78225--&gt;
&lt;!-- begin paragraph 6f220858-5201-489c-bf52-fd7633fcaaaf--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-6f220858-5201-489c-bf52-fd7633fcaaaf&quot;&gt;Un peu différent, ici on teste que si l&amp;#39;on part de 0, alors on doit pouvoir passer la barrière directement, sinon c&amp;#39;est une erreur. Ce scénario n&amp;#39;est pas prévu pour l’implémentation actuelle, mais ce n&amp;#39;est pas complexe à rajouter.&lt;/p&gt;
&lt;!-- end paragraph 6f220858-5201-489c-bf52-fd7633fcaaaf--&gt;&lt;/li&gt;&lt;li&gt;On vérifie que cela bloque comme attendu
&lt;!-- begin code a36ba0f0-bcd0-4eab-9886-916015113fd7--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-a36ba0f0-bcd0-4eab-9886-916015113fd7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;testM(&amp;quot;keep the gate closed&amp;quot;)(for {
  l  &amp;lt;- make(2)
  _  &amp;lt;- l.countDown
  r1 &amp;lt;- runningOrError(l.await, &amp;quot;shoud be running&amp;quot;)
  _  &amp;lt;- l.countDown
  r2 &amp;lt;- completedOrError(l.await, &amp;quot;should be completed&amp;quot;)
} yield {
  r1 &amp;amp;&amp;amp; r2
})&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a36ba0f0-bcd0-4eab-9886-916015113fd7--&gt;&lt;/li&gt;&lt;li&gt;On perturbe les effets
&lt;!-- begin code a677267a-7132-4df9-a374-ad07649ecf2c--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-a677267a-7132-4df9-a374-ad07649ecf2c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;testM(&amp;quot;basic scenario with disturb&amp;quot;)(
  checkAllM(Gen.fromIterable(0 to 10))(
    steps =&amp;gt;
      for {
        cdl &amp;lt;- make(2)
        _   &amp;lt;- cdl.countDown
        _   &amp;lt;- Disturb.interruptAfterNSteps(cdl.countDown, steps).orElse(cdl.countDown)
        r   &amp;lt;- completedOrError(cdl.await, &amp;quot;should be completed&amp;quot;)
      } yield {
        r
      }
  )
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a677267a-7132-4df9-a374-ad07649ecf2c--&gt;
&lt;!-- begin paragraph 70242ae0-4d3c-4fae-b434-0cd270e73fe1--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-70242ae0-4d3c-4fae-b434-0cd270e73fe1&quot;&gt;Ce test est beaucoup plus complexe. On perturbe la deuxième exécution de &lt;code&gt;.countDown&lt;/code&gt;, et si cette exécution est en erreur, on relance un &lt;code&gt;.countDown&lt;/code&gt; avant de vérifier si &lt;code&gt;.await&lt;/code&gt; est disponible.&lt;/p&gt;
&lt;!-- end paragraph 70242ae0-4d3c-4fae-b434-0cd270e73fe1--&gt;
&lt;!-- begin paragraph f6602f66-8d16-483f-9eb4-ec4e10a5cd81--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-f6602f66-8d16-483f-9eb4-ec4e10a5cd81&quot;&gt;Afin de définir ce test, il faut réussir à pouvoir perturber des effets. ZIO est basé sur de la composition de représentations différentes des effets, que l&amp;#39;on peut déstructurer en exécution pas à pas, dans du code (super-)utilisateur.&lt;/p&gt;
&lt;!-- end paragraph f6602f66-8d16-483f-9eb4-ec4e10a5cd81--&gt;
&lt;!-- begin paragraph 04ff8317-0da1-4b90-88af-e58933be99a9--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-04ff8317-0da1-4b90-88af-e58933be99a9&quot;&gt;Sans expliquer jusqu&amp;#39;au bout l&amp;#39;approche, on peut taper dans le code interne de ZIO pour faire une perturbation des effets, qui va arrêter l&amp;#39;exécution après N étapes.&lt;/p&gt;
&lt;!-- end paragraph 04ff8317-0da1-4b90-88af-e58933be99a9--&gt;
&lt;!-- begin code 947b5303-8e6d-4eb0-ab3d-40586dbf52a1--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-947b5303-8e6d-4eb0-ab3d-40586dbf52a1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Disturb {
  def interruptAfterNSteps[R, E, A](effect: ZIO[R, E, A], maxExecutionSteps: Int): ZIO[R, Option[E], A]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 947b5303-8e6d-4eb0-ab3d-40586dbf52a1--&gt;
&lt;!-- begin paragraph ca3028ca-dd95-495d-96dd-110543851c06--&gt;
&lt;p class=&quot;block-paragraph level-1&quot; id=&quot;p-ca3028ca-dd95-495d-96dd-110543851c06&quot;&gt;Et caler ça dans un test avec un peu de property-based-testing :&lt;/p&gt;
&lt;!-- end paragraph ca3028ca-dd95-495d-96dd-110543851c06--&gt;
&lt;!-- begin code ce18c749-0d05-4a16-8bff-24b8eb83ecea--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-ce18c749-0d05-4a16-8bff-24b8eb83ecea&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;testM(&amp;quot;basic scenario with disturb&amp;quot;)(
  checkAllM(Gen.fromIterable(0 to 10))(
    steps =&amp;gt;
      for {
        cdl &amp;lt;- make(2)
        _   &amp;lt;- cdl.countDown
        _   &amp;lt;- Disturb.maxSteps(cdl.countDown, steps).orElse(cdl.countDown)
        r   &amp;lt;- completedOrError(cdl.await, &amp;quot;should be completed&amp;quot;)
      } yield {
        r
      }
  )
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ce18c749-0d05-4a16-8bff-24b8eb83ecea--&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 38244976-f62c-4d7a-806d-4c137771323c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38244976-f62c-4d7a-806d-4c137771323c&quot;&gt;La suite de tests permet de confirmer les 2 &amp;quot;erreurs&amp;quot; (on les a cherchées).&lt;/p&gt;
&lt;!-- end paragraph 38244976-f62c-4d7a-806d-4c137771323c--&gt;
&lt;!-- begin code 9185c589-c016-4df2-8f37-d46a4cfae389--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9185c589-c016-4df2-8f37-d46a4cfae389&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;- finish
  + basic scenario
  + keep the gate closed
  - make 0 should be available
  - basic scenario with disturb&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9185c589-c016-4df2-8f37-d46a4cfae389--&gt;
&lt;!-- begin heading_3 0bc85ca5-d630-4321-857f-dd54c6982944--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-0bc85ca5-d630-4321-857f-dd54c6982944&quot;&gt;Fix de l’implémentation&lt;/h4&gt;
&lt;!-- end heading_3 0bc85ca5-d630-4321-857f-dd54c6982944--&gt;
&lt;!-- begin paragraph 12ac51bb-a95a-4737-afbe-4ef883e27fbf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-12ac51bb-a95a-4737-afbe-4ef883e27fbf&quot;&gt;Un moyen facile pour fixer l&amp;#39;implémentation, c&amp;#39;est de : &lt;/p&gt;
&lt;!-- end paragraph 12ac51bb-a95a-4737-afbe-4ef883e27fbf--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Faire une implémentation pour la valeur manquante dans le domaine (&lt;code&gt;make(0)&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;Passer en non-interruptible la section du code qui ne doit pas être coupé en deux.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code a8834e0c-3f36-4cf8-9022-8fe2f605d581--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a8834e0c-3f36-4cf8-9022-8fe2f605d581&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object CountDownLatch {
  def make(count: Int): UIO[CountDownLatch] =
    if (count &amp;lt;= 0)
      UIO(new CountDownLatch {
        override def countDown: UIO[Unit] = ZIO.unit
        override def await: UIO[Unit]     = ZIO.unit
      })
    else
      for {
        ready &amp;lt;- Promise.make[Nothing, Unit]
        ref   &amp;lt;- Ref.make(count)
      } yield new CountDownLatch {
        final override def countDown: UIO[Unit] =
          (ref.updateAndGet(_ - 1) &amp;gt;&amp;gt;= {
            case 0 =&amp;gt; ready.succeed(()).ignore
            case _ =&amp;gt; ZIO.unit
          }).uninterruptible

        final override def await: UIO[Unit] = ready.await
      }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a8834e0c-3f36-4cf8-9022-8fe2f605d581--&gt;
&lt;!-- begin paragraph 178ed517-b6f3-43b6-9cd9-78220bd0f2a1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-178ed517-b6f3-43b6-9cd9-78220bd0f2a1&quot;&gt;Néanmoins si on reprend le problème à la base, il y a probablement moyen de faire plus simple. La version Java utilise &amp;quot;juste&amp;quot; une &lt;code&gt;AtomicRef&lt;/code&gt; pour garder le comptage et fait une &amp;quot;petite&amp;quot; boucle pour l&amp;#39;attente. On peut faire pareil en ZIO, ce qui donne un truc relativement propre.&lt;/p&gt;
&lt;!-- end paragraph 178ed517-b6f3-43b6-9cd9-78220bd0f2a1--&gt;
&lt;!-- begin code 297be16c-c499-410a-beab-2423255936b0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-297be16c-c499-410a-beab-2423255936b0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object CountDownLatchJavaLike {
  def make(count: Int): UIO[CountDownLatch] =
    for {
      ref &amp;lt;- Ref.make(count)
    } yield new CountDownLatch {
      override def countDown: UIO[Unit] = ref.update(_ - 1)
      override def await: UIO[Unit]     = ref.get.doUntil(_ &amp;lt;= 0).unit
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 297be16c-c499-410a-beab-2423255936b0--&gt;
&lt;!-- begin paragraph 49dde8ce-ac13-4e0e-9001-d221bcbb83ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-49dde8ce-ac13-4e0e-9001-d221bcbb83ea&quot;&gt;Les perfs sur le await ne sont pas tops, c&amp;#39;est dommage de lancer un &amp;quot;Schedule&amp;quot; si le countDownLatch est déjà libéré. On pourrait faire un appel direct avant de faire un appel récurrent. &lt;/p&gt;
&lt;!-- end paragraph 49dde8ce-ac13-4e0e-9001-d221bcbb83ea--&gt;
&lt;!-- begin code ea11410f-9a65-4b5a-9b1e-22bbd0e9063d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ea11410f-9a65-4b5a-9b1e-22bbd0e9063d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def await: UIO[Unit] = ref.get flatMap {
  x =&amp;gt; if(x &amp;lt;= 0) ZIO.unit 
             else ref.get.doUntil(_ &amp;lt;= 0).unit
}
//ou faire un autre combinateur
@inline
def fastDoUntil[R,E,A](e: ZIO[R,E,A])(p: A =&amp;gt; Boolean) = 
  e flatMap {
    x =&amp;gt; if(p(x)) ZIO.succeed(x)
         else     e.doUntil(p)
  }

def await: UIO[Unit] = fastDoUntil(ref.get)(_ &amp;lt;= 0).unit&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ea11410f-9a65-4b5a-9b1e-22bbd0e9063d--&gt;
&lt;!-- begin paragraph 76291088-3ab9-4cfe-a278-3e486bf4e338--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-76291088-3ab9-4cfe-a278-3e486bf4e338&quot;&gt;Voici les liens vers le code : &lt;/p&gt;
&lt;!-- end paragraph 76291088-3ab9-4cfe-a278-3e486bf4e338--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/ahoy-jon/awesome-zio/blob/master/src/main/scala/io/univalence/zio_awesome/CountDownLatch.scala&quot;&gt;Les implémentations&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/ahoy-jon/awesome-zio/blob/master/src/test/scala/io/univalence/zio_awesome/CountDownLatchSpec.scala&quot;&gt;Test unitaire&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/ahoy-jon/awesome-zio/blob/master/src/test/scala/zio/test/Disturb.scala&quot;&gt;Disturb&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 8f7457c8-9991-49a0-aa5a-b399a9eee78c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-8f7457c8-9991-49a0-aa5a-b399a9eee78c&quot;&gt;Conclusion&lt;/h3&gt;
&lt;!-- end heading_2 8f7457c8-9991-49a0-aa5a-b399a9eee78c--&gt;
&lt;!-- begin paragraph 4bfcc17b-5118-4bdf-9903-43b5c3e6b132--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4bfcc17b-5118-4bdf-9903-43b5c3e6b132&quot;&gt;ZIO permet de faire beaucoup plus en programmation concurrente et s&amp;#39;il manque des éléments dans le menu, on peut toujours construire par-dessus à la carte.&lt;/p&gt;
&lt;!-- end paragraph 4bfcc17b-5118-4bdf-9903-43b5c3e6b132--&gt;
&lt;!-- begin paragraph 596e72f9-9456-4856-b13d-8bc41a53341c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-596e72f9-9456-4856-b13d-8bc41a53341c&quot;&gt;⚠️ Néanmoins cela nécessite de faire des tests plus poussés en environnement concurrent.&lt;/p&gt;
&lt;!-- end paragraph 596e72f9-9456-4856-b13d-8bc41a53341c--&gt;
&lt;!-- begin paragraph 4782097f-92f6-4454-a41d-3dce3da54305--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4782097f-92f6-4454-a41d-3dce3da54305&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4782097f-92f6-4454-a41d-3dce3da54305--&gt;
&lt;!-- begin paragraph fde33437-0e60-4338-8f6a-9a00f53eb6d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fde33437-0e60-4338-8f6a-9a00f53eb6d8&quot;&gt;&amp;lt;fiction&amp;gt;Pour le prochain article on regardera pour la conversion de TLA+ (PlusCal) vers ZIO. &amp;lt;/fiction&amp;gt;&lt;/p&gt;
&lt;!-- end paragraph fde33437-0e60-4338-8f6a-9a00f53eb6d8--&gt;
&lt;!-- begin paragraph fe43955e-1545-47ca-8364-403abcfc33c5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fe43955e-1545-47ca-8364-403abcfc33c5&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fe43955e-1545-47ca-8364-403abcfc33c5--&gt;
&lt;!-- begin paragraph ff5c5e41-1e8a-46ea-a1c4-06042abf01a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff5c5e41-1e8a-46ea-a1c4-06042abf01a0&quot;&gt;Quelques liens utiles :&lt;/p&gt;
&lt;!-- end paragraph ff5c5e41-1e8a-46ea-a1c4-06042abf01a0--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=d6WWmia0BPM&quot;&gt;ATOMICALLY { DELETE YOUR ACTORS } - John A De Goes &amp;amp; Wiem Zine Elabadine&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://youtu.be/TWdC7DhvD8M?t=2403&quot;&gt;Pour les philosophes qui meurent de faim&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 006ff8b7-87ce-45b8-8161-5270027d5a40--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-006ff8b7-87ce-45b8-8161-5270027d5a40&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 006ff8b7-87ce-45b8-8161-5270027d5a40--&gt;
&lt;!-- begin paragraph 8bcb77bd-1b19-4d72-9bc0-6f292051479b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8bcb77bd-1b19-4d72-9bc0-6f292051479b&quot;&gt;La version depuis &lt;code&gt;java.util.concurrent.CountDownLatch&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 8bcb77bd-1b19-4d72-9bc0-6f292051479b--&gt;
&lt;!-- begin code 7c1f9bac-bc25-4d9b-8a7b-c9a2a2a6d5d2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7c1f9bac-bc25-4d9b-8a7b-c9a2a2a6d5d2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio.blocking._

object CountDownLatchWrapJava {
  def make(count: Int): UIO[CountDownLatch] =
    for {
      cdl &amp;lt;- WrapImpure.wrapTotal(new java.util.concurrent.CountDownLatch(count))
    } yield {
      new CountDownLatch {
        override def countDown: UIO[Unit] = cdl.execTotal(_.countDown())
        override def await: UIO[Unit]     = cdl.execTotal(_.getCount()).doUntil(_ &amp;lt;= 0L).unit
      }
    }
}

//Ou une version plus technique avec l&amp;#39;aide de Adam Fraser
object CountDownLatchWrapJavaBlock {
  def make(count: Int): URIO[Blocking, CountDownLatch] =
    for {
      blocking &amp;lt;- ZIO.environment[Blocking]
      cdl      &amp;lt;- WrapImpure.wrapTotal(new java.util.concurrent.CountDownLatch(count))
    } yield {
      new CountDownLatch {
        override def countDown: UIO[Unit] = cdl.execTotal(_.countDown())
        override def await: UIO[Unit]     = cdl.execBlockingInterrupt(_.await()).orDie.provide(blocking)
      }
    }
}

//regarde maman, sans Kleisli
final class WrapImpure[B](private val value: B) {
  def exec[C](f: B =&amp;gt; C): Task[C]                           = ZIO.effect(f(value))
  def execTotal[C](f: B =&amp;gt; C): UIO[C]                       = ZIO.effectTotal(f(value))
  def execBlockingInterrupt[C](f: B =&amp;gt; C): RIO[Blocking, C] = effectBlockingInterrupt(f(value))
}

object WrapImpure {
  def wrap[B](b: =&amp;gt; B): Task[WrapImpure[B]]     = ZIO.effect(new WrapImpure(b))
  def wrapTotal[B](b: =&amp;gt; B): UIO[WrapImpure[B]] = ZIO.effectTotal(new WrapImpure(b))
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7c1f9bac-bc25-4d9b-8a7b-c9a2a2a6d5d2--&gt;
&lt;!-- begin divider 5fbd7382-4b6c-4c85-9950-9c68deab694c--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-5fbd7382-4b6c-4c85-9950-9c68deab694c&quot;&gt;
&lt;!-- end divider 5fbd7382-4b6c-4c85-9950-9c68deab694c--&gt;
&lt;!-- begin paragraph cf265c1b-81c4-46b9-8fe8-35e2b5da732d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf265c1b-81c4-46b9-8fe8-35e2b5da732d&quot;&gt;Crédit photo : &lt;a href=&quot;https://unsplash.com/photos/5AMSZcgN_cM&quot;&gt;unsplash / Sunrise Photos&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph cf265c1b-81c4-46b9-8fe8-35e2b5da732d--&gt;
&lt;!-- begin paragraph 780cc989-968f-4ad9-b86b-68ad077c2111--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-780cc989-968f-4ad9-b86b-68ad077c2111&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 780cc989-968f-4ad9-b86b-68ad077c2111--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:029b96fa9735472696318b2f779408e5</id>
    <title>Le feu, la roue, IO</title>
    <updated>2020-02-03T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/le-feu-la-roue-io.html"/>
    <!--summary Les applications doivent manipuler des effets pour être utiles, comme la communication avec le système, avec des utilisateurs ou un service tiers. Mais les effets créent des dépendances fortes dans le code qui ne favorisent pas sa maintenabilité. Le type IO permet de pallier cette difficulté.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="ZIO"></category>    <category term="Programmation fonctionnelle"></category>    <category term="Effet"></category>    <category term="IO"></category>    <content type="html">
&lt;!-- begin paragraph e54c00cc-4f1a-4474-bf4b-82dbc7d245a5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e54c00cc-4f1a-4474-bf4b-82dbc7d245a5&quot;&gt;En informatique, il est nécessaire de communiquer avec des services externes ou des utilisateurs pour avoir des applications utiles, ce qui implique dans l&amp;#39;état actuel de l&amp;#39;informatique de gérer le non-déterminisme et la mutabilité. Mais on sait aussi qu&amp;#39;immutabilité et déterminisme font bon ménage pour avoir des applications stables. Tellement stables qu&amp;#39;elles ne pourraient plus s&amp;#39;exécuter et deviendraient donc par essence inutiles ?&lt;/p&gt;
&lt;!-- end paragraph e54c00cc-4f1a-4474-bf4b-82dbc7d245a5--&gt;
&lt;!-- begin paragraph 7ef6b7e0-6cbf-4c54-b1b0-0f62591e8a82--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7ef6b7e0-6cbf-4c54-b1b0-0f62591e8a82&quot;&gt;Entre ça et le ruban infinie de la machine de Turing, l&amp;#39;informatique n’est en fait plus qu’un immense paradoxe voué à disparaître dans une nébuleuse de logique.&lt;/p&gt;
&lt;!-- end paragraph 7ef6b7e0-6cbf-4c54-b1b0-0f62591e8a82--&gt;
&lt;!-- begin paragraph ec5f8d0e-888a-43b3-a2ad-f68f01742fbb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec5f8d0e-888a-43b3-a2ad-f68f01742fbb&quot;&gt;Non ! Pour mettre sur pied des applications qui fonctionnent, il va juste se passer ce que nous faisons depuis toujours en informatique pour qu&amp;#39;elle ait une raison d&amp;#39;exister : des concessions. Par exemple, on peut tricher avec les limites humaines, pour qui un ordinateur fait des calculs instantanés, pour qui la mémoire est tellement volumineuse qu’elle en paraît infinie (même si une mémoire infinie, ce n’est jamais assez 🤡) et affiche sur l&amp;#39;écran des lignes continues. On va aussi jouer à &amp;quot;pas vu, pas pris&amp;quot;. Ainsi, on va dire qu&amp;#39;on est immutable, alors qu&amp;#39;en fait le processeur passe son temps à modifier ses registres, ses flags, ses caches... Toutes ces choses qu&amp;#39;on appelle des &lt;b&gt;effets&lt;/b&gt;. Mais ça, on ne le voit pas. Alors, on dit qu&amp;#39;on est immutable néanmoins, même avec un calcul méga complexe. L’immutabilité, c’est lorsque la mutabilité ne se voit pas !&lt;/p&gt;
&lt;!-- end paragraph ec5f8d0e-888a-43b3-a2ad-f68f01742fbb--&gt;
&lt;!-- begin paragraph 2a7a845d-2038-4601-803f-820da86fb9da--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2a7a845d-2038-4601-803f-820da86fb9da&quot;&gt;Mais il arrive un moment où les effets ça devient tellement gros qu&amp;#39;on ne peut plus les cacher... Gros comme un appel à une base de donnée pour modifier des données et le constater par exemple ou une interaction avec un client Web qui retourne des informations différentes à chaque fois (service météo, forex EUR/USD, les timelines...). Et ça, ces effets, on ne peut pas y échapper si on veut que notre application soit utile.&lt;/p&gt;
&lt;!-- end paragraph 2a7a845d-2038-4601-803f-820da86fb9da--&gt;
&lt;!-- begin paragraph 1f8b3c64-60aa-47bd-bacc-0221f5688191--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f8b3c64-60aa-47bd-bacc-0221f5688191&quot;&gt;Malheureusement, avec les effets, ce sont des impuretés qui s&amp;#39;incrustent dans notre code, incluant des bugs connus comme les actions à distance. Et un code impur, ça veut dire du code sur lequel le raisonnement est plus complexe. Il s&amp;#39;ensuit une baisse de la capacité de refactoring, de la lisibilité, de la testabilité... Au moins, la seule chose qui augmente, c&amp;#39;est le temps passé à déboguer.&lt;/p&gt;
&lt;!-- end paragraph 1f8b3c64-60aa-47bd-bacc-0221f5688191--&gt;
&lt;!-- begin heading_1 eb54c0c6-5c9e-4bec-8952-19fd322f3c21--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-eb54c0c6-5c9e-4bec-8952-19fd322f3c21&quot;&gt;IO lave plus blanc&lt;/h2&gt;
&lt;!-- end heading_1 eb54c0c6-5c9e-4bec-8952-19fd322f3c21--&gt;
&lt;!-- begin paragraph 76bb39b5-49c8-4211-8877-66646ef3852d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-76bb39b5-49c8-4211-8877-66646ef3852d&quot;&gt;Pour retrouver la pureté fonctionnelle de notre code, nous allons mettre en place &lt;del&gt;une hypocrisie&lt;/del&gt; une abstraction.&lt;/p&gt;
&lt;!-- end paragraph 76bb39b5-49c8-4211-8877-66646ef3852d--&gt;
&lt;!-- begin paragraph 4762ec1a-d283-4594-86c2-0afcf4c92ae0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4762ec1a-d283-4594-86c2-0afcf4c92ae0&quot;&gt;Le seul moyen d&amp;#39;avoir un code isolé des effets, c&amp;#39;est de repousser l&amp;#39;exécution de ces effets. Et pour ça, nous allons retarder cette exécution pour la déclencher à un moment plus opportun. C&amp;#39;est un peu comme dans certaines histoires où on emprisonne un.e bad guy/girl pour le/la sortir lorsqu&amp;#39;il n&amp;#39;y a plus d&amp;#39;autre choix pour sauver une situation 😎.&lt;/p&gt;
&lt;!-- end paragraph 4762ec1a-d283-4594-86c2-0afcf4c92ae0--&gt;
&lt;!-- begin paragraph db8996f8-a1d7-44f9-9cb3-cf8014e27792--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-db8996f8-a1d7-44f9-9cb3-cf8014e27792&quot;&gt;En programmation fonctionnelle, on fait ça avec une fonction.&lt;/p&gt;
&lt;!-- end paragraph db8996f8-a1d7-44f9-9cb3-cf8014e27792--&gt;
&lt;!-- begin paragraph 1f2d323a-937b-43d0-9fc5-96faf2c36c55--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f2d323a-937b-43d0-9fc5-96faf2c36c55&quot;&gt;Prenons l&amp;#39;expression suivante&lt;/p&gt;
&lt;!-- end paragraph 1f2d323a-937b-43d0-9fc5-96faf2c36c55--&gt;
&lt;!-- begin code 1929660e-6672-4023-8740-ea8c5ef17245--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1929660e-6672-4023-8740-ea8c5ef17245&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;List(println(&amp;quot;Hello&amp;quot;), println(&amp;quot;Hello&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1929660e-6672-4023-8740-ea8c5ef17245--&gt;
&lt;!-- begin paragraph 8b33bd18-2d94-4f1b-8c77-e883a3f93e03--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8b33bd18-2d94-4f1b-8c77-e883a3f93e03&quot;&gt;Son résultat donne&lt;/p&gt;
&lt;!-- end paragraph 8b33bd18-2d94-4f1b-8c77-e883a3f93e03--&gt;
&lt;!-- begin code 319fc690-4e7e-4d63-b3ec-27bc3fb31697--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-319fc690-4e7e-4d63-b3ec-27bc3fb31697&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;Hello
Hello
res1: List[Unit] = List((), ())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 319fc690-4e7e-4d63-b3ec-27bc3fb31697--&gt;
&lt;!-- begin paragraph 68d30a2c-03d6-431a-8d56-2e692a087c32--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-68d30a2c-03d6-431a-8d56-2e692a087c32&quot;&gt;Puisque &lt;code&gt;println&lt;/code&gt; est une fonction qui effectue un affichage à l&amp;#39;écran et ne retourne pas de valeur à part &lt;code&gt;()&lt;/code&gt; (de type Unit).&lt;/p&gt;
&lt;!-- end paragraph 68d30a2c-03d6-431a-8d56-2e692a087c32--&gt;
&lt;!-- begin paragraph 602754c9-d156-4f7a-8a1b-c523e6f5dbd8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-602754c9-d156-4f7a-8a1b-c523e6f5dbd8&quot;&gt;Par transparence référentielle (possibilité de remplacer une partie d&amp;#39;une expression par sa valeur), on devrait avoir le même comportement avec le code ci-dessous&lt;/p&gt;
&lt;!-- end paragraph 602754c9-d156-4f7a-8a1b-c523e6f5dbd8--&gt;
&lt;!-- begin code 816d8091-2e7c-4090-b9f0-8a449905b198--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-816d8091-2e7c-4090-b9f0-8a449905b198&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val result: Unit = println(&amp;quot;Hello&amp;quot;)
List(result, result)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 816d8091-2e7c-4090-b9f0-8a449905b198--&gt;
&lt;!-- begin paragraph 8c914766-6686-4fb2-ab52-87360177a9da--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c914766-6686-4fb2-ab52-87360177a9da&quot;&gt;Ce qui n&amp;#39;est pas le cas&lt;/p&gt;
&lt;!-- end paragraph 8c914766-6686-4fb2-ab52-87360177a9da--&gt;
&lt;!-- begin code a56ec4ba-d804-4b31-9a23-f99a966bac05--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a56ec4ba-d804-4b31-9a23-f99a966bac05&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;Hello
res1: List[Unit] = List((), ())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a56ec4ba-d804-4b31-9a23-f99a966bac05--&gt;
&lt;!-- begin paragraph 4e6ed7f1-46ba-46af-83e0-5c2a8573a83b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e6ed7f1-46ba-46af-83e0-5c2a8573a83b&quot;&gt;Puisque la variable &lt;code&gt;result&lt;/code&gt; ne récupère sa valeur (qui est &lt;code&gt;()&lt;/code&gt;) qu&amp;#39;après que &lt;code&gt;println&lt;/code&gt; ait été exécuté. Après quoi, l&amp;#39;utilisation de &lt;code&gt;result&lt;/code&gt; retourne &lt;code&gt;()&lt;/code&gt;, mais ne ré-appelle pas &lt;code&gt;println&lt;/code&gt;. Dans ce cas, notre code initial ne peut subir de refactoring.&lt;/p&gt;
&lt;!-- end paragraph 4e6ed7f1-46ba-46af-83e0-5c2a8573a83b--&gt;
&lt;!-- begin paragraph 5e38afde-007a-4b99-8707-3beb51758605--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e38afde-007a-4b99-8707-3beb51758605&quot;&gt;Pour retrouver un comportement équivalent avec la possibilité d&amp;#39;utiliser une variable intermédiaire, il faut intégrer l&amp;#39;appel à &lt;code&gt;println&lt;/code&gt; dans une fonction. Mais cela implique une petite modification après la construction de la liste. Ce n&amp;#39;est pas bien grave, dans la mesure où nous montrons qu&amp;#39;un effet est présent dans notre expression.&lt;/p&gt;
&lt;!-- end paragraph 5e38afde-007a-4b99-8707-3beb51758605--&gt;
&lt;!-- begin code f4641dca-f7fc-4cb4-baf4-5c5f9397189c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f4641dca-f7fc-4cb4-baf4-5c5f9397189c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val result: Unit =&amp;gt; Unit = () =&amp;gt; println(&amp;quot;Hello&amp;quot;)
List(result, result).map(f =&amp;gt; f())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f4641dca-f7fc-4cb4-baf4-5c5f9397189c--&gt;
&lt;!-- begin paragraph d116f5a2-0ad1-4edf-baf4-954b86ad398c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d116f5a2-0ad1-4edf-baf4-954b86ad398c&quot;&gt;Ce qui donne&lt;/p&gt;
&lt;!-- end paragraph d116f5a2-0ad1-4edf-baf4-954b86ad398c--&gt;
&lt;!-- begin code 2a4cb361-a88c-4b4f-8049-da687335e0be--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2a4cb361-a88c-4b4f-8049-da687335e0be&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;Hello
Hello
res1: List[Unit] = List((), ())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2a4cb361-a88c-4b4f-8049-da687335e0be--&gt;
&lt;!-- begin paragraph 6b9b7821-b762-4096-949c-161003db8da1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6b9b7821-b762-4096-949c-161003db8da1&quot;&gt;Nous pouvons utiliser cette forme avec d&amp;#39;autres effets&lt;/p&gt;
&lt;!-- end paragraph 6b9b7821-b762-4096-949c-161003db8da1--&gt;
&lt;!-- begin code d2d0f854-b698-4c41-91ba-ba42743af9be--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d2d0f854-b698-4c41-91ba-ba42743af9be&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def printMessage(message: String): () =&amp;gt; Unit = () =&amp;gt; println(message)
def readLine(): () =&amp;gt; Int = ???
def readFile(filename: String): () =&amp;gt; Try[String] = ???
def getUserTimeLine(twitterName: String): () =&amp;gt; Try[List[Tweet]] = ???&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d2d0f854-b698-4c41-91ba-ba42743af9be--&gt;
&lt;!-- begin heading_1 003de4c5-c4fb-43bc-9b11-d5381b1a3625--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-003de4c5-c4fb-43bc-9b11-d5381b1a3625&quot;&gt;De la fonction au type&lt;/h2&gt;
&lt;!-- end heading_1 003de4c5-c4fb-43bc-9b11-d5381b1a3625--&gt;
&lt;!-- begin paragraph 84289e56-3280-4619-b617-c1a756740b97--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-84289e56-3280-4619-b617-c1a756740b97&quot;&gt;Plutôt que d&amp;#39;utiliser une fonction, qui complique un peu notre écriture, nous allons représenter l&amp;#39;effet en créant un type nommé Effect. Ce type prend en paramètre la fonction que nous avons vu précédemment et la déclenche par un appel à &lt;code&gt;run&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 84289e56-3280-4619-b617-c1a756740b97--&gt;
&lt;!-- begin code 612f1c19-cee1-432e-9c5a-c3f63ccfb520--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-612f1c19-cee1-432e-9c5a-c3f63ccfb520&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Effect[A](run: () =&amp;gt; A)
object Effect {
  def pure[A](a: =&amp;gt; A): Effect[A] = Effect { () =&amp;gt; a }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 612f1c19-cee1-432e-9c5a-c3f63ccfb520--&gt;
&lt;!-- begin paragraph 793efbd5-bf2c-46cb-93c8-1db380b161fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-793efbd5-bf2c-46cb-93c8-1db380b161fb&quot;&gt;Le type Effect représente donc une opération ou un ensemble d&amp;#39;opérations contenant des effets.&lt;/p&gt;
&lt;!-- end paragraph 793efbd5-bf2c-46cb-93c8-1db380b161fb--&gt;
&lt;!-- begin code 21716f79-5aea-45f0-9f8e-38bc6d447b86--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-21716f79-5aea-45f0-9f8e-38bc6d447b86&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def printMessage(message: String): Effect[Unit] =
  Effect.pure { println(message) }

def readLine(): Effect[Int] =
  Effect { scala.io.StdIn.readLine() }

def readFile(filename: String): Effect[Try[String]] =
  Effect { scala.io.Source.fromFile(filename).mkString }

def getUserTimeLine(twitterName: String): Effect[Try[List[Tweet]]] = ???&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 21716f79-5aea-45f0-9f8e-38bc6d447b86--&gt;
&lt;!-- begin paragraph f429ebeb-edc2-4226-b8bd-392a6bc6f511--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f429ebeb-edc2-4226-b8bd-392a6bc6f511&quot;&gt;Le type Effect est en plus un foncteur et une monade. Il est donc possible pour ce type de construire les méthode map et flatMap, et de les utiliser dans un for-comprehension :&lt;/p&gt;
&lt;!-- end paragraph f429ebeb-edc2-4226-b8bd-392a6bc6f511--&gt;
&lt;!-- begin code f51389d9-4cc6-453b-b5f1-5cfd3da211f5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f51389d9-4cc6-453b-b5f1-5cfd3da211f5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val program: Effect[Unit] =
  for {
    _ &amp;lt;- printMessage(&amp;quot;WHAT... Is your name?&amp;quot;)
    name &amp;lt;- readLine()
    _ &amp;lt;- printMessage(&amp;quot;WHAT... Is your quest?&amp;quot;)
    answer &amp;lt;- readLine()
    _ &amp;lt;- printMessage(s&amp;quot;Are you sure, $name?&amp;quot;)
  } yield ()

program.run()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f51389d9-4cc6-453b-b5f1-5cfd3da211f5--&gt;
&lt;!-- begin paragraph e7f62182-95f1-4011-886b-3bf5e573748f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e7f62182-95f1-4011-886b-3bf5e573748f&quot;&gt;Par convention (surtout avec Haskell), Effect est appelé IO. Voici une implémentation &amp;quot;naïve&amp;quot; du type IO.&lt;/p&gt;
&lt;!-- end paragraph e7f62182-95f1-4011-886b-3bf5e573748f--&gt;
&lt;!-- begin code 942eef46-1cb7-4555-af2b-e4886fede0bf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-942eef46-1cb7-4555-af2b-e4886fede0bf&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class IO[A](run: () =&amp;gt; A) {
  def map[B](f: A =&amp;gt; B): IO[B] = IO.pure { f(run()) }
  def flatMap[B](f: A =&amp;gt; IO[B]): IO[B] = IO.pure { f(run()).run() }
}
object IO {
  def pure[A](a: =&amp;gt; A): IO[A] = IO { () =&amp;gt; a }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 942eef46-1cb7-4555-af2b-e4886fede0bf--&gt;
&lt;!-- begin heading_1 6a6432f5-9b30-43f1-9736-806f1fddbaf0--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6a6432f5-9b30-43f1-9736-806f1fddbaf0&quot;&gt;Vous aviez un imprévu ?&lt;/h2&gt;
&lt;!-- end heading_1 6a6432f5-9b30-43f1-9736-806f1fddbaf0--&gt;
&lt;!-- begin paragraph 22b02e1c-3d93-4acf-833c-4b8f46dae1ff--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-22b02e1c-3d93-4acf-833c-4b8f46dae1ff&quot;&gt;Maintenant, on peut tout à fait s&amp;#39;attendre à ce qu&amp;#39;un effet ait un comportement imprévu (eg. IOException, Timeout, 404, 500...). Pour représenter ce type de comportement, il y a deux écoles. La première considère qu&amp;#39;il y a déjà un type pour représenter ça, qui est le type &lt;code&gt;Either&lt;/code&gt;, et donc on va se retrouver avec &lt;code&gt;IO[Either[E, A]]&lt;/code&gt;, où &lt;code&gt;E&lt;/code&gt; représente le type de la situation exceptionnelle (Throwable, String, List[BusinessError]). Généralement, dans ce cas, on réécrit le type en &lt;code&gt;EitherT[IO, E, A]&lt;/code&gt; qui est un monad transformer. Ce qui n&amp;#39;est pas sans poser quelques soucis, en particulier des soucis de performance.&lt;/p&gt;
&lt;!-- end paragraph 22b02e1c-3d93-4acf-833c-4b8f46dae1ff--&gt;
&lt;!-- begin paragraph edacf975-2037-422a-bbcc-634a01e76ad9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-edacf975-2037-422a-bbcc-634a01e76ad9&quot;&gt;Dans une autre approche, nous pouvons considérer que la situation exceptionnelle est une partie intrinsèque de l&amp;#39;effet et qu&amp;#39;elle ne peut pas en être dissociée. Dans le mesure où un effet peut représenter un accès à une base de données, à une communication avec un service externe, à un traitement impliquant le système d&amp;#39;exploitation, etc., nous avons très souvent des cas où l&amp;#39;effet implique un comportement imprévu. Dans ce cas, il devient nécessaire de remonter cet aspect dans la signature du type.&lt;/p&gt;
&lt;!-- end paragraph edacf975-2037-422a-bbcc-634a01e76ad9--&gt;
&lt;!-- begin paragraph a4c64d09-da0c-4bfb-a214-750f7821258b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a4c64d09-da0c-4bfb-a214-750f7821258b&quot;&gt;Un point intéressant, si nous remontons &lt;a href=&quot;https://github.com/scala/scala/blob/d53c0dadb92173a6caeb6a1f31dafa92dcff6833/doc/reference/RationalePart.tex#L16-L42&quot;&gt;le passé de Scala&lt;/a&gt;, nous retrouvons cette argumentation lorsqu&amp;#39;il est question de communication distante en particulier. Et c&amp;#39;est ce qui est aussi inclus dans la notion d&amp;#39;effet.&lt;/p&gt;
&lt;!-- end paragraph a4c64d09-da0c-4bfb-a214-750f7821258b--&gt;
&lt;!-- begin paragraph 207d6322-6ea5-4bac-ad54-6d3898bbd213--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-207d6322-6ea5-4bac-ad54-6d3898bbd213&quot;&gt;Ce qui nous donne le type ci-dessous&lt;/p&gt;
&lt;!-- end paragraph 207d6322-6ea5-4bac-ad54-6d3898bbd213--&gt;
&lt;!-- begin code 8dd94e3d-6260-45cb-a9fc-58d83aea420a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8dd94e3d-6260-45cb-a9fc-58d83aea420a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait IO[E, A] {
  def run: () =&amp;gt; Either[E, A]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8dd94e3d-6260-45cb-a9fc-58d83aea420a--&gt;
&lt;!-- begin paragraph 719132f9-a202-41c9-9c85-739c0a9cc201--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-719132f9-a202-41c9-9c85-739c0a9cc201&quot;&gt;Nous pouvons retrouver une implémentation équivalente dans &lt;a href=&quot;https://zio.dev/docs/datatypes/datatypes_io&quot;&gt;ZIO&lt;/a&gt;. Cette bibliothèque définit aussi un type IO à deux paramètres. Par contre, il n&amp;#39;y a pas de méthode &lt;code&gt;run&lt;/code&gt; dans le type. ZIO fournit déjà un environnement d&amp;#39;exécution représenté par le trait App, à &amp;quot;étendre&amp;quot; en redéfinissant une méthode &lt;code&gt;run&lt;/code&gt; pour utiliser votre code incluant des effets. ZIO définit un type &lt;code&gt;Task[A]&lt;/code&gt; définit comme &lt;code&gt;IO[Throwable, A]&lt;/code&gt;, ce qui permet de simplifier la lecture du type.&lt;/p&gt;
&lt;!-- end paragraph 719132f9-a202-41c9-9c85-739c0a9cc201--&gt;
&lt;!-- begin heading_1 62fa7cf5-af87-4816-996c-648707b162ee--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-62fa7cf5-af87-4816-996c-648707b162ee&quot;&gt;IO avec ZIO, c&amp;#39;est BIO !&lt;/h2&gt;
&lt;!-- end heading_1 62fa7cf5-af87-4816-996c-648707b162ee--&gt;
&lt;!-- begin paragraph ed71c495-41c4-4d7f-bf53-2774040097a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ed71c495-41c4-4d7f-bf53-2774040097a7&quot;&gt;Partons d&amp;#39;un exemple : une fonction qui lit un entier sur l&amp;#39;entrée standard (stdin). Pour cette fonction, nous avons un cas imprévu si l&amp;#39;utilisateur fournit autre chose qu&amp;#39;un entier.&lt;/p&gt;
&lt;!-- end paragraph ed71c495-41c4-4d7f-bf53-2774040097a7--&gt;
&lt;!-- begin code a7d657ed-b0a7-42a2-9789-0df79ae81265--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a7d657ed-b0a7-42a2-9789-0df79ae81265&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def readInt: Task[Int] = Task { scala.io.StdIn.readInt() }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a7d657ed-b0a7-42a2-9789-0df79ae81265--&gt;
&lt;!-- begin paragraph 6f27fdee-f3fe-4653-b557-902e5fba81eb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6f27fdee-f3fe-4653-b557-902e5fba81eb&quot;&gt;Voici un exemple de programme ZIO où cette fonction est intégrée.&lt;/p&gt;
&lt;!-- end paragraph 6f27fdee-f3fe-4653-b557-902e5fba81eb--&gt;
&lt;!-- begin code d460b82a-6b96-41ac-afcd-76d969f042a9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d460b82a-6b96-41ac-afcd-76d969f042a9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import zio._
import zio.console._

object ReadIntMain extends App {

  override def run(args: List[String]) =
    program.absorb.fold(
      // in case of failure
      e =&amp;gt; { e.printStackTrace(); 1 },
      // in case of success
      _ =&amp;gt; 0)

  val program =
    for  {
      _      &amp;lt;- putStrLn(&amp;quot;Input an integer:&amp;quot;)
      number &amp;lt;- readInt
      _      &amp;lt;- putStrLn(s&amp;quot;value: $number&amp;quot;)
    } yield ()

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d460b82a-6b96-41ac-afcd-76d969f042a9--&gt;
&lt;!-- begin paragraph 47a6e60e-0e47-49ae-b7b6-ee5c2786fe68--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-47a6e60e-0e47-49ae-b7b6-ee5c2786fe68&quot;&gt;Quoiqu&amp;#39;il en soit, dans ce code, les effets sont uniquement exécutés une fois que le résultat de la méthode est récupéré par le main de App. En dehors de ça, les effets sont uniquement encapsulés et représentés dans le type IO.&lt;/p&gt;
&lt;!-- end paragraph 47a6e60e-0e47-49ae-b7b6-ee5c2786fe68--&gt;
&lt;!-- begin paragraph 2e8d2945-46f0-4c19-a518-88982cfc7b0a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2e8d2945-46f0-4c19-a518-88982cfc7b0a&quot;&gt;Ainsi, si nous revenons sur notre exemple initial au tout début de l&amp;#39;article, avec ZIO. Nous avons&lt;/p&gt;
&lt;!-- end paragraph 2e8d2945-46f0-4c19-a518-88982cfc7b0a--&gt;
&lt;!-- begin code 68c487bb-735a-4128-b4df-e999c06a60be--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-68c487bb-735a-4128-b4df-e999c06a60be&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val program =
  ZIO.collectAll(
    List(putStrLn(&amp;quot;Hello&amp;quot;), putStrLn(&amp;quot;Hello&amp;quot;))
  )&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 68c487bb-735a-4128-b4df-e999c06a60be--&gt;
&lt;!-- begin paragraph cf3792a1-22fd-43cc-abe5-b831caf51e0c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf3792a1-22fd-43cc-abe5-b831caf51e0c&quot;&gt;Qui donne&lt;/p&gt;
&lt;!-- end paragraph cf3792a1-22fd-43cc-abe5-b831caf51e0c--&gt;
&lt;!-- begin code 6014b5f7-fe2a-4336-a65d-50f4b0044b21--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6014b5f7-fe2a-4336-a65d-50f4b0044b21&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Hello
Hello&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6014b5f7-fe2a-4336-a65d-50f4b0044b21--&gt;
&lt;!-- begin paragraph 3998b8fe-25ef-420b-a0cd-d3fdda4a7e34--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3998b8fe-25ef-420b-a0cd-d3fdda4a7e34&quot;&gt;Par refactoring, on peut écrire le code suivant&lt;/p&gt;
&lt;!-- end paragraph 3998b8fe-25ef-420b-a0cd-d3fdda4a7e34--&gt;
&lt;!-- begin code 40a22b54-ba0c-481b-83d6-4df49428d93f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-40a22b54-ba0c-481b-83d6-4df49428d93f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val result = putStrLn(&amp;quot;Hello&amp;quot;)

val program =
  ZIO.collectAll(
    List(result, result)
  )&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 40a22b54-ba0c-481b-83d6-4df49428d93f--&gt;
&lt;!-- begin paragraph 926132eb-ec75-43b1-8f5f-7a27dcb3e63b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-926132eb-ec75-43b1-8f5f-7a27dcb3e63b&quot;&gt;Qui donne aussi&lt;/p&gt;
&lt;!-- end paragraph 926132eb-ec75-43b1-8f5f-7a27dcb3e63b--&gt;
&lt;!-- begin code a7011454-db1b-4936-8629-0bc2db6bb953--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a7011454-db1b-4936-8629-0bc2db6bb953&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Hello
Hello&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a7011454-db1b-4936-8629-0bc2db6bb953--&gt;
&lt;!-- begin paragraph 00ff7f7e-967a-4e99-995f-9e78c41976df--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-00ff7f7e-967a-4e99-995f-9e78c41976df&quot;&gt;Ce qui respecte le principe de transparence référentielle.&lt;/p&gt;
&lt;!-- end paragraph 00ff7f7e-967a-4e99-995f-9e78c41976df--&gt;
&lt;!-- begin paragraph 13b5f3d7-7586-4d85-8f91-d63ce85696f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-13b5f3d7-7586-4d85-8f91-d63ce85696f9&quot;&gt;À travers le type IO, les effets deviennent des valeurs manipulables comme tels, qui peuvent être intégrées dans des expressions et craignent d&amp;#39;autant moins le refactoring. Nous pouvons le constater avec la bibliothèque ZIO. Le code devient plus accessible, nous pouvons raisonner simplement dessus et intervenir dessus avec un meilleur niveau de confiance.&lt;/p&gt;
&lt;!-- end paragraph 13b5f3d7-7586-4d85-8f91-d63ce85696f9--&gt;
&lt;!-- begin paragraph 1e91184b-60e4-4fa3-9e26-e331fa720b8d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1e91184b-60e4-4fa3-9e26-e331fa720b8d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1e91184b-60e4-4fa3-9e26-e331fa720b8d--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:6703398e59f6437bb3313f497e417673</id>
    <title>DataXDay 2019 - Le retour de l&#039;équipe Univalence</title>
    <updated>2019-12-18T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/dataxday-le-retour-de-lequipe-univalence.html"/>
    <!--summary L&#039;équipe d&#039;Univalence a eu la chance de participer au DataXDay, un événement centré autour de la data et voici ce qui s&#039;est passé... TIN TIN-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Data"></category>    <category term="Conférence"></category>    <category term="DataXDay"></category>    <category term="Partage"></category>    <content type="html">
&lt;!-- begin paragraph e86a9033-3beb-4071-a9e5-44336bd00252--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e86a9033-3beb-4071-a9e5-44336bd00252&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e86a9033-3beb-4071-a9e5-44336bd00252--&gt;
&lt;!-- begin paragraph 2377be05-3421-4433-a27b-697992fab183--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2377be05-3421-4433-a27b-697992fab183&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2377be05-3421-4433-a27b-697992fab183--&gt;
&lt;!-- begin divider 374f7be2-f59e-4da1-b099-130c9cb67b92--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-374f7be2-f59e-4da1-b099-130c9cb67b92&quot;&gt;
&lt;!-- end divider 374f7be2-f59e-4da1-b099-130c9cb67b92--&gt;
&lt;!-- begin paragraph 4eccbd7d-9038-4b4e-bbc0-09d77cae7ec7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4eccbd7d-9038-4b4e-bbc0-09d77cae7ec7&quot;&gt;Nous sommes seulement une semaine après le Process Mining Camp 2019 et déjà un autre événement se présente à nous, le DataXDay. Un événement organisé par l&amp;#39;entreprise Xebia et regroupant des dizaines et des dizaines de Data Engineers et Data Scientists tous ayant soif de connaissances et de partages. &lt;/p&gt;
&lt;!-- end paragraph 4eccbd7d-9038-4b4e-bbc0-09d77cae7ec7--&gt;
&lt;!-- begin paragraph 386d65c4-25b8-4964-8a49-8c9cdee30731--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-386d65c4-25b8-4964-8a49-8c9cdee30731&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 386d65c4-25b8-4964-8a49-8c9cdee30731--&gt;
&lt;!-- begin paragraph 24f91942-02dd-4faa-be99-1ab153c47d96--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-24f91942-02dd-4faa-be99-1ab153c47d96&quot;&gt;Cette fois-ci tous les data ingénieurs d&amp;#39;Univalence ont ainsi pu y participer et l&amp;#39;un d&amp;#39;entre nous a même eu l&amp;#39;occasion de faire un talk, L&amp;#39;incroyable efficacité de l&amp;#39;unification des logs, qui aura très certainement un article qui lui est consacré dans le futur! Sur cette journée de conférence, il y a eu 22 talks centrés autour de la data ayant tous des sujets divers et variés parfois plus dédiés aux data scientists, parfois plus dédiés aux data engineers et parfois alliant les deux.&lt;/p&gt;
&lt;!-- end paragraph 24f91942-02dd-4faa-be99-1ab153c47d96--&gt;
&lt;!-- begin paragraph 9f202813-e815-46d2-bcff-fb27559e1108--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9f202813-e815-46d2-bcff-fb27559e1108&quot;&gt;NB: Tous les talks sont d&amp;#39;ores et déjà présents gratuitement sur &lt;a href=&quot;https://www.youtube.com/channel/UCDSbqKhAoWZARlWAPbf88rg&quot;&gt;Xebia TV&lt;/a&gt; ou plus simplement dans &lt;a href=&quot;https://dataxday.fr/videos-2019/&quot;&gt;la section vidéo du site DataXDay&lt;/a&gt; 🎉&lt;/p&gt;
&lt;!-- end paragraph 9f202813-e815-46d2-bcff-fb27559e1108--&gt;
&lt;!-- begin paragraph 09ac335e-59dd-4efb-9901-4f9f262863b2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-09ac335e-59dd-4efb-9901-4f9f262863b2&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 09ac335e-59dd-4efb-9901-4f9f262863b2--&gt;
&lt;!-- begin paragraph a7295a08-0ae5-4dc2-8fca-f7d38ec6f609--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a7295a08-0ae5-4dc2-8fca-f7d38ec6f609&quot;&gt;Nous allons, dans cet article, vous partager notre expérience et les différents talks auxquels nous avons participé. La conférence ayant deux tracks se déroulant en parallèle, je ne ferais part que des talks auxquels j&amp;#39;ai participé. Ainsi voici la liste des talks que je ne traiterais pas lors de cet article accompagné d&amp;#39;un lien pour les visionner sur Youtube :&lt;/p&gt;
&lt;!-- end paragraph a7295a08-0ae5-4dc2-8fca-f7d38ec6f609--&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/J8kr7dGRXmU&amp;feature=emb_logo?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 2cf1a949-b9e3-4c0d-a20c-eb74b0f15698--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2cf1a949-b9e3-4c0d-a20c-eb74b0f15698&quot;&gt;&lt;a href=&quot;https://youtu.be/J8kr7dGRXmU&quot;&gt;DataXDay - De Jupyter à Spark : les étapes de la mise en production du Machine Learning&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/nicolaspauline/&quot;&gt;Pauline Nicolas&lt;/a&gt; et &lt;a href=&quot;https://www.linkedin.com/in/th%E9o-bontempelli-ab69b2b7/&quot;&gt;Théo Bontempelli&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 2cf1a949-b9e3-4c0d-a20c-eb74b0f15698--&gt;
&lt;!-- begin divider 301f277c-ec82-46fc-a9de-31ed27bfcb02--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-301f277c-ec82-46fc-a9de-31ed27bfcb02&quot;&gt;
&lt;!-- end divider 301f277c-ec82-46fc-a9de-31ed27bfcb02--&gt;
&lt;!-- begin paragraph 2fae93a5-b87f-45a3-98bb-b2d3c6c874ec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2fae93a5-b87f-45a3-98bb-b2d3c6c874ec&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2fae93a5-b87f-45a3-98bb-b2d3c6c874ec--&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Z5JUI7UH2g4?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 9b347762-a36e-4b7f-96e6-e69ccc77be4e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b347762-a36e-4b7f-96e6-e69ccc77be4e&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Z5JUI7UH2g4&quot;&gt;Confident Data Migration: automatic regression testing&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/thomas-franquelin/fr/&quot;&gt;Thomas Franquelin&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 9b347762-a36e-4b7f-96e6-e69ccc77be4e--&gt;
&lt;!-- begin divider 21748545-0eca-48cf-9199-b0693a4c5ebf--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-21748545-0eca-48cf-9199-b0693a4c5ebf&quot;&gt;
&lt;!-- end divider 21748545-0eca-48cf-9199-b0693a4c5ebf--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=emV_Rudjnc4&quot;&gt;Kubeflow : Tensorflow on Kubernetes&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/laurentgrangeau&quot;&gt;Laurent Grangeau&lt;/a&gt; et &lt;a href=&quot;https://www.linkedin.com/in/slequeux/?originalSubdomain=fr&quot;&gt;Sylvain &lt;/a&gt;&lt;a href=&quot;https://www.linkedin.com/in/slequeux/&quot;&gt;Lequeux&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=W4piubfcit0&quot;&gt;How to use Apache Kafka to transform a batch pipeline into a Real-Time-One?&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/stephanemaarek/&quot;&gt;Stéphane Maarek&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=awLK5zd9FkA&quot;&gt;Comment mettre en œuvre une architecture de données permettant de visualiser l’impact de scores sur des événements en temps réel&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/nicolas-brigitte-alphonsine/&quot;&gt;Nicolas Brigitte-Alphonsine&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ymjSmNcOE3g&quot;&gt;Apache Cassandra™ et architectures cloud (hybrid-cloud, multi-cloud) ou comment migrer des applications dans le cloud sans interruption de service&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/davidleconte/&quot;&gt;David Leconte&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=HPQPEgdi9Bw&quot;&gt;Multilingual sentiment analysis &amp;amp; emotion detection based on social media data&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/alexisdutot0/&quot;&gt;Alexis Dutot&lt;/a&gt; et &lt;a href=&quot;https://www.linkedin.com/in/jadecopet/&quot;&gt;Jade Copet&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4ceZPfCZruQ&quot;&gt;How to scale Neural Network Architecture Search with RabbitMQ and Kubernetes&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/guillaume-michel-b4745b13/&quot;&gt;Guillaume Michel&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=IWbwL6j7jiw&amp;t=739s&quot;&gt;Quelle stratégie de test pour vos applicatifs de machine learning ?&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/victor-landeau-31bb4064/&quot;&gt;Victor Landeau&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XaZqC9xpV08&quot;&gt;Les nouveautés Serverless de GCP&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/glaforge/&quot;&gt;Guillaume Laforge&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=RQzUrkb0t9I&quot;&gt;Tensorflow 1.x n&amp;#39;est plus, vive Tensorflow 2.0&lt;/a&gt; par &lt;a href=&quot;https://www.linkedin.com/in/alexia-audevart/&quot;&gt;Alexia Audevart&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 591745e3-08ab-4c98-8395-d7f3d728e0a8--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-591745e3-08ab-4c98-8395-d7f3d728e0a8&quot;&gt;Keynote #1 : La confiance entre les humains et les données&lt;/h2&gt;
&lt;!-- end heading_1 591745e3-08ab-4c98-8395-d7f3d728e0a8--&gt;
&lt;!-- begin paragraph 89159e99-f63e-47d3-860c-3c358269115c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89159e99-f63e-47d3-860c-3c358269115c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 89159e99-f63e-47d3-860c-3c358269115c--&gt;
&lt;!-- begin paragraph 90afefe5-f765-46ec-b40a-ec290a414ad8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-90afefe5-f765-46ec-b40a-ec290a414ad8&quot;&gt;La journée a commencé avec un talk de &lt;a href=&quot;https://fr.linkedin.com/in/carolinegoulard&quot;&gt;Caroline Goulard&lt;/a&gt;, anciennement journaliste de la donnée qui, maintenant, est CEO à &lt;a href=&quot;https://dataveyes.com/#!/fr&quot;&gt;Dataveyes&lt;/a&gt;, une entreprise qui, comme elle le dit si bien, tant à vouloir &amp;quot;rendre l&amp;#39;invisible visible&amp;quot;.&lt;/p&gt;
&lt;!-- end paragraph 90afefe5-f765-46ec-b40a-ec290a414ad8--&gt;
&lt;!-- begin paragraph 223efa8e-bd79-49a2-a42e-da1634eb13eb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-223efa8e-bd79-49a2-a42e-da1634eb13eb&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 223efa8e-bd79-49a2-a42e-da1634eb13eb--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/6703398e-59f6-437b-b331-3f497e417673/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph cd73cbb7-2d29-4f53-9d70-d9c2417b6501--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd73cbb7-2d29-4f53-9d70-d9c2417b6501&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=HtYf2brLIXw&quot;&gt;https://www.youtube.com/watch?v=HtYf2brLIXw&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph cd73cbb7-2d29-4f53-9d70-d9c2417b6501--&gt;
&lt;!-- begin paragraph 1e4a99e3-3eab-41fb-8cf7-aaf7f874ffa6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1e4a99e3-3eab-41fb-8cf7-aaf7f874ffa6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1e4a99e3-3eab-41fb-8cf7-aaf7f874ffa6--&gt;
&lt;!-- begin paragraph 594b351f-4011-4537-87fd-e2a1a008998d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-594b351f-4011-4537-87fd-e2a1a008998d&quot;&gt;D&amp;#39;ailleurs son talk était justement basé sur ce postulat, aujourd&amp;#39;hui nous sommes dans l&amp;#39;air de la donnée et malgré ce fait nous sommes la plupart du temps incapables de voir ce qu&amp;#39;elle représente vraiment. La visualisation de la donnée donne un sens à cette dernière et la rend accessible au plus grand nombre. J&amp;#39;ai d&amp;#39;ailleurs particulièrement apprécié les travaux qu&amp;#39;ils ont effectués notamment en visualisant l&amp;#39;impact sonore des différentes lignes de métro sur Paris. Le résultat étant, de par sa qualité, très impactant pour l&amp;#39;utilisateur. Un autre exemple sur &lt;a href=&quot;https://dataveyes.com/#!/en/case-studies/maquette-pedagogique&quot;&gt;la gestion de l’énergie d&amp;#39;une maison&lt;/a&gt; m&amp;#39;a aussi énormément interpellé, notamment grâce à la mise en place de la gamification au sein même du système. Ce dernier aspect rend la présentation globale de notre data plus actifs que jamais. Une idée incroyable, participant à réduire la consommation des personnes de manière ludique. &lt;/p&gt;
&lt;!-- end paragraph 594b351f-4011-4537-87fd-e2a1a008998d--&gt;
&lt;!-- begin paragraph 49a8e349-323a-4065-bb0d-b2419ac93795--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-49a8e349-323a-4065-bb0d-b2419ac93795&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 49a8e349-323a-4065-bb0d-b2419ac93795--&gt;
&lt;!-- begin paragraph 2d5a6fd2-fde4-4565-9a10-70320ceb1730--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2d5a6fd2-fde4-4565-9a10-70320ceb1730&quot;&gt;Somme toute un très bon talk pour commencer la journée (un de mes préférés) même si je regrette un peu qu&amp;#39;il n&amp;#39;eût aucune technologie de cité, afin d&amp;#39;occuper mes dimanches à donner de la vie à de la donnée (Mon petit doigt me dit que l&amp;#39;utilisation de l&amp;#39;API WebGL et &lt;a href=&quot;https://d3js.org/&quot;&gt;d3.js&lt;/a&gt; doit être de mise 😉).&lt;/p&gt;
&lt;!-- end paragraph 2d5a6fd2-fde4-4565-9a10-70320ceb1730--&gt;
&lt;!-- begin heading_1 f2cc13ea-3b4a-4120-bd50-3c029a64d693--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f2cc13ea-3b4a-4120-bd50-3c029a64d693&quot;&gt;Keynote #2 : Database Infrastructure at Instagram Scale&lt;/h2&gt;
&lt;!-- end heading_1 f2cc13ea-3b4a-4120-bd50-3c029a64d693--&gt;
&lt;!-- begin paragraph ebc36e52-294f-49ec-950f-36af6999c495--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ebc36e52-294f-49ec-950f-36af6999c495&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ebc36e52-294f-49ec-950f-36af6999c495--&gt;
&lt;!-- begin paragraph dff09a96-25da-4b8c-83ba-6b5cf2bec9de--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dff09a96-25da-4b8c-83ba-6b5cf2bec9de&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph dff09a96-25da-4b8c-83ba-6b5cf2bec9de--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/6703398e-59f6-437b-b331-3f497e417673/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 350de82b-6b04-4e4c-81e8-c568f25ed27a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-350de82b-6b04-4e4c-81e8-c568f25ed27a&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=b74cU-afJHM&quot;&gt;https://www.youtube.com/watch?v=b74cU-afJHM&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 350de82b-6b04-4e4c-81e8-c568f25ed27a--&gt;
&lt;!-- begin paragraph 32d617a5-271c-4719-b972-3a820a46b8fe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-32d617a5-271c-4719-b972-3a820a46b8fe&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 32d617a5-271c-4719-b972-3a820a46b8fe--&gt;
&lt;!-- begin paragraph bcc10bff-efa4-4f25-8af6-45f26c7aa746--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bcc10bff-efa4-4f25-8af6-45f26c7aa746&quot;&gt;S&amp;#39;ensuit un talk très intéressant pour tout les data ingénieurs, un talk de &lt;a href=&quot;https://twitter.com/mfiguiere?lang=fr&quot;&gt;Michaël Figuière&lt;/a&gt; travaillant anciennement chez Instagram et actuellement Data Infrastructure Engineer chez Facebook. Ce dernier a notamment attiré notre attention car Michaël nous partage certaines problématiques  qu&amp;#39;une architecture d&amp;#39;une ampleur considérable peut créer. Instagram est très certainement l&amp;#39;application django la plus complexe et avec plusieurs millions de lectures et écritures par seconde elle se doit d&amp;#39;avoir une infrastructure particulièrement tenace.  &lt;/p&gt;
&lt;!-- end paragraph bcc10bff-efa4-4f25-8af6-45f26c7aa746--&gt;
&lt;!-- begin paragraph 1850ec0e-75d7-4120-85a7-4b85298f83b4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1850ec0e-75d7-4120-85a7-4b85298f83b4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1850ec0e-75d7-4120-85a7-4b85298f83b4--&gt;
&lt;!-- begin paragraph b6bf1499-9553-4b9e-a84f-2c74c0d809da--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b6bf1499-9553-4b9e-a84f-2c74c0d809da&quot;&gt;Ainsi à titre d&amp;#39;exemple, Instagram shutdown très régulièrement des datacenters entiers pour tester l’adaptabilité de l&amp;#39;infrastructure en cas de crises majeures (spoil alert: tout se passe bien!). De plus chaque datacenter est configuré pour gérer un certain nombre de requêtes et pour trouver ce nombre, l’infrastructure surcharge un des datacenters afin de voir sa limite et ainsi de pouvoir quantifier la quantité d&amp;#39;informations qu&amp;#39;il peut traiter. Un autre fait intéressant, vos données Instagram vous suivent dans vos déplacements ainsi si vous décidez de partir en voyage aux États-Unis alors vos données actuellement concentrées dans le cluster Europe vont être déplacées dans le cluster Amérique du Nord pour réduire la latence.&lt;/p&gt;
&lt;!-- end paragraph b6bf1499-9553-4b9e-a84f-2c74c0d809da--&gt;
&lt;!-- begin paragraph d3037286-1332-4e1f-91bf-160fc06c8f23--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3037286-1332-4e1f-91bf-160fc06c8f23&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d3037286-1332-4e1f-91bf-160fc06c8f23--&gt;
&lt;!-- begin heading_1 5b08a372-3c2e-435c-8319-5e0d2563207a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5b08a372-3c2e-435c-8319-5e0d2563207a&quot;&gt;Kafka Streams, profiling, framegraphs, Oh My!&lt;/h2&gt;
&lt;!-- end heading_1 5b08a372-3c2e-435c-8319-5e0d2563207a--&gt;
&lt;!-- begin paragraph 6012a749-2ce6-402a-84f0-4c584c57ae21--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6012a749-2ce6-402a-84f0-4c584c57ae21&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6012a749-2ce6-402a-84f0-4c584c57ae21--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/6703398e-59f6-437b-b331-3f497e417673/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6a067852-1804-4d89-b050-313a2ad3ae7f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a067852-1804-4d89-b050-313a2ad3ae7f&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=DBfmKE42PiY&quot;&gt;https://www.youtube.com/watch?v=DBfmKE42PiY&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 6a067852-1804-4d89-b050-313a2ad3ae7f--&gt;
&lt;!-- begin paragraph 6bef9ec1-3998-4464-8d80-7bf46526fc9c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6bef9ec1-3998-4464-8d80-7bf46526fc9c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6bef9ec1-3998-4464-8d80-7bf46526fc9c--&gt;
&lt;!-- begin paragraph 69673deb-ba56-44c5-a396-74e853dbf16a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69673deb-ba56-44c5-a396-74e853dbf16a&quot;&gt;Nous avons continué notre matinée avec un talk de &lt;a href=&quot;https://www.linkedin.com/in/xavierleaute&quot;&gt;Xavier Léauté&lt;/a&gt; qui est actuellement software engineer chez &lt;a href=&quot;https://www.confluent.io/&quot;&gt;Confluent&lt;/a&gt;. Le but de ce talk, partager certaines astuces pour optimiser et profiler un kafka streams. Ce talk bien qu&amp;#39;étant focus sur kafka est aussi très intéressant pour l&amp;#39;optimisation de n&amp;#39;importe qu&amp;#39;elle programme s’exécutant via la JVM.&lt;/p&gt;
&lt;!-- end paragraph 69673deb-ba56-44c5-a396-74e853dbf16a--&gt;
&lt;!-- begin paragraph 2071ae39-c567-4eb8-8be2-d2a973ee11c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2071ae39-c567-4eb8-8be2-d2a973ee11c1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2071ae39-c567-4eb8-8be2-d2a973ee11c1--&gt;
&lt;!-- begin paragraph 017692e7-0e6b-4679-af30-b393deab0fe9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-017692e7-0e6b-4679-af30-b393deab0fe9&quot;&gt;Xavier rappelle que le profiling est important pour éviter de rencontrer des problèmes par la suite. Ce monitoring peut se faire via les metrics (Je vous recommande d&amp;#39;ailleurs cette trilogie d&amp;#39;articles sur &lt;a href=&quot;https://www.datadoghq.com/blog/monitoring-kafka-performance-metrics/&quot;&gt;le monitoring de kafka&lt;/a&gt; écrit par &lt;a href=&quot;https://www.linkedin.com/in/vagelim/&quot;&gt;Evan Mouzakitis&lt;/a&gt; sur le site de &lt;a href=&quot;https://www.datadoghq.com/&quot;&gt;DataDog&lt;/a&gt;). Cependant, Xavier nous met en garde. Les metrics sont adressés à des problèmes déjà rencontrés auparavant, mais il se peut que vos problèmes seront uniques à votre cas, de plus certains problèmes n’apparaîtrons qu&amp;#39;en production et pas en phase de test et dans ce cas la, il faut creuser plus bas pour détecter les bottlenecks.&lt;/p&gt;
&lt;!-- end paragraph 017692e7-0e6b-4679-af30-b393deab0fe9--&gt;
&lt;!-- begin paragraph b64c073f-9eee-4b72-9d26-f410a21cc579--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b64c073f-9eee-4b72-9d26-f410a21cc579&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b64c073f-9eee-4b72-9d26-f410a21cc579--&gt;
&lt;!-- begin paragraph e1cf17b9-071a-45a8-82a1-939ceb1d8dea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e1cf17b9-071a-45a8-82a1-939ceb1d8dea&quot;&gt;L&amp;#39;outil de prédilection pour faire du profiling selon ses préférences est &lt;a href=&quot;https://github.com/jvm-profiling-tools/async-profiler&quot;&gt;Async Profiler&lt;/a&gt;. Il le décrit comme étant l&amp;#39;outil le plus simple et le plus puissant pour faire faire du profilage d&amp;#39;application basé sur la JVM. Ses points positifs ? Il est très léger, s&amp;#39;attache directement à la JVM sans avoir besoin d&amp;#39;argument ou de restart cette dernière et est compatible avec les anciennes versions du JDK.&lt;/p&gt;
&lt;!-- end paragraph e1cf17b9-071a-45a8-82a1-939ceb1d8dea--&gt;
&lt;!-- begin heading_1 898cf05e-5da9-4fbb-83a9-d30b390aeffb--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-898cf05e-5da9-4fbb-83a9-d30b390aeffb&quot;&gt;Deep learning en production vu par un Data Engineer&lt;/h2&gt;
&lt;!-- end heading_1 898cf05e-5da9-4fbb-83a9-d30b390aeffb--&gt;
&lt;!-- begin paragraph f38e26b3-19df-4cbb-970e-7e1f23fab929--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f38e26b3-19df-4cbb-970e-7e1f23fab929&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=aLowsPZjVew&quot;&gt;https://www.youtube.com/watch?v=aLowsPZjVew&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph f38e26b3-19df-4cbb-970e-7e1f23fab929--&gt;
&lt;!-- begin paragraph b846c6ea-7429-49fd-b6d1-cb0f8a954100--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b846c6ea-7429-49fd-b6d1-cb0f8a954100&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b846c6ea-7429-49fd-b6d1-cb0f8a954100--&gt;
&lt;!-- begin paragraph 6490cfcf-e158-4a61-b664-6316e94ffc92--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6490cfcf-e158-4a61-b664-6316e94ffc92&quot;&gt;Rien de mieux qu&amp;#39;un talk sur le deep learning en production pour satisfaire à la fois les Data Scientists et à la fois les Data Engineers présents à l&amp;#39;événement. Cette idée, elle vient de &lt;a href=&quot;https://fr.linkedin.com/in/romain-sagean-38187a5a&quot;&gt;Romain Sagean&lt;/a&gt;, un consultant chez &lt;a href=&quot;https://xebia.fr/&quot;&gt;Xebia&lt;/a&gt;. &lt;/p&gt;
&lt;!-- end paragraph 6490cfcf-e158-4a61-b664-6316e94ffc92--&gt;
&lt;!-- begin paragraph e634034d-4ad3-4bb2-b4d3-d45cc39ba2f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e634034d-4ad3-4bb2-b4d3-d45cc39ba2f6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e634034d-4ad3-4bb2-b4d3-d45cc39ba2f6--&gt;
&lt;!-- begin paragraph 14b69a5d-4b05-48ae-98e1-e5860c121f21--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-14b69a5d-4b05-48ae-98e1-e5860c121f21&quot;&gt;Son objectif est très simple, faire part de son expérience personnelle de la mise en place d&amp;#39;un modèle de deep learning dans une chaîne de production et le tout vu par un data ingénieur. L&amp;#39;objectif du projet étant d&amp;#39;acheter des liens sponsorisés Google à moindre de coup pour un total de 10 millions de prédictions par jour et les faire en moins d&amp;#39;une heure. &lt;/p&gt;
&lt;!-- end paragraph 14b69a5d-4b05-48ae-98e1-e5860c121f21--&gt;
&lt;!-- begin paragraph 9c826811-3681-44f2-9f53-daefd46ee997--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c826811-3681-44f2-9f53-daefd46ee997&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9c826811-3681-44f2-9f53-daefd46ee997--&gt;
&lt;!-- begin paragraph 027d2ef3-c01c-4674-b14a-bb5cf6eb4866--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-027d2ef3-c01c-4674-b14a-bb5cf6eb4866&quot;&gt;Dans un tel contexte, il faut faire des choix. Quel modèle utilisé pour notre prédiction? quel framework utilisé pour créer le modèle et prédire?&lt;/p&gt;
&lt;!-- end paragraph 027d2ef3-c01c-4674-b14a-bb5cf6eb4866--&gt;
&lt;!-- begin paragraph 4f502140-3352-46d6-b862-0ce31554a511--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4f502140-3352-46d6-b862-0ce31554a511&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4f502140-3352-46d6-b862-0ce31554a511--&gt;
&lt;!-- begin paragraph 8e088309-4630-42a4-9685-398988c0c3aa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e088309-4630-42a4-9685-398988c0c3aa&quot;&gt;Dans le cas de Romain et son équipe, ils ont d&amp;#39;abord testé différents modèles de machine learning et sont venu à la conclusion que XGBoost était un choix intéressant car donnant les meilleures prédictions. Mais finalement, ils vont se diriger vers du deep learning car plus de possibilités et de potentiels là où XGBoost atteignait déjà ses limites. Une fois le modèle prêt, il fallait choisir le framework à utiliser: &lt;a href=&quot;https://www.tensorflow.org/&quot;&gt;TensorFlow&lt;/a&gt; et &lt;a href=&quot;https://keras.io/&quot;&gt;Keras&lt;/a&gt; ce sont immédiatement présenter à eux pour leurs communautés et leurs maturités respectives mais ils ne se sont pas arrêté à eux et ont aussi inclus dans leur choix &lt;a href=&quot;https://deeplearning4j.org/&quot;&gt;DL4J&lt;/a&gt; et &lt;a href=&quot;https://mxnet.apache.org/&quot;&gt;mxnet&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 8e088309-4630-42a4-9685-398988c0c3aa--&gt;
&lt;!-- begin paragraph 8e48bf5c-0dff-46f4-9dfd-d06ba147e809--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e48bf5c-0dff-46f4-9dfd-d06ba147e809&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8e48bf5c-0dff-46f4-9dfd-d06ba147e809--&gt;
&lt;!-- begin paragraph 3c44e971-f772-4401-8a62-c23998392878--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3c44e971-f772-4401-8a62-c23998392878&quot;&gt;Basé sur des critères propres à leur architecture, TensorFlow l&amp;#39;a emporté, mais impossible de le faire fonctionner en production à cause d&amp;#39;une version de glibc trop vieille au niveau de la production. Plutôt que d&amp;#39;update toute la chaîne de production, le choix le plus raisonnable en matière de temps et de moyens était de sélectionner le deuxième sur la liste DL4J, un framework codé en Java. S’intégrant parfaitement en production, il ne manquait plus qu&amp;#39;à l&amp;#39;optimiser. L&amp;#39;optimisation a été faite de bien des manières et voici quelques-unes des pistes traitées lors du talk: preprocessing, mapper les values par partitions, créer un seul modèle lazy et non un modèle par ligne, set spark.yarn.executor.memoryOverhead, gérer les workspaces DL4J.&lt;/p&gt;
&lt;!-- end paragraph 3c44e971-f772-4401-8a62-c23998392878--&gt;
&lt;!-- begin heading_1 047bb5af-ce36-485f-aa09-43605de1a0e4--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-047bb5af-ce36-485f-aa09-43605de1a0e4&quot;&gt;How to leverage the Apache Kafka Ecosystem to productionize Machine Learning&lt;/h2&gt;
&lt;!-- end heading_1 047bb5af-ce36-485f-aa09-43605de1a0e4--&gt;
&lt;!-- begin paragraph 601ee5b1-fab8-4379-8d60-02bf09d36d55--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-601ee5b1-fab8-4379-8d60-02bf09d36d55&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=I4eDowjTZLk&quot;&gt;https://www.youtube.com/watch?v=I4eDowjTZLk&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 601ee5b1-fab8-4379-8d60-02bf09d36d55--&gt;
&lt;!-- begin paragraph 59d805ca-3f92-4f9d-85d0-d757e85c64cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-59d805ca-3f92-4f9d-85d0-d757e85c64cf&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 59d805ca-3f92-4f9d-85d0-d757e85c64cf--&gt;
&lt;!-- begin paragraph 4d4f67d8-b871-437f-aa16-5a4ad634fdfc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4d4f67d8-b871-437f-aa16-5a4ad634fdfc&quot;&gt;Un talk imaginé par &lt;a href=&quot;http://www.kai-waehner.de/&quot;&gt;Kai Waehner&lt;/a&gt; qui est Technology Evangelist à &lt;a href=&quot;https://www.confluent.io/&quot;&gt;Confluent&lt;/a&gt; qui, dans la même veine que le dernier talk, s&amp;#39;adresse à la fois aux Data Scientist et au Data Engineer. Cependant celui-ci s’intéresse beaucoup plus à la place du machine learning autour d&amp;#39;une architecture Big Data plutôt qu&amp;#39;à la sélection d&amp;#39;un modèle et de son implémentation en production.&lt;/p&gt;
&lt;!-- end paragraph 4d4f67d8-b871-437f-aa16-5a4ad634fdfc--&gt;
&lt;!-- begin paragraph 38fbeffc-37ba-4a67-86d5-055dd8093ad6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38fbeffc-37ba-4a67-86d5-055dd8093ad6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 38fbeffc-37ba-4a67-86d5-055dd8093ad6--&gt;
&lt;!-- begin paragraph 6b5ff748-deea-4f15-8962-b0af9001e185--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6b5ff748-deea-4f15-8962-b0af9001e185&quot;&gt;Avec la présence de très bon framework de machine learning tels que Keras ou TensorFlow, il est très facile aujourd&amp;#39;hui de faire du machine learning. Mais une tâche beaucoup plus complexe est d&amp;#39;adapter ces frameworks à des cas où des millions d&amp;#39;informations arrivent en temps réel. Dans un cas comme celui-ci, on peut se demander comment entraîner le modèle en continue tout en appliquant le modèle en cours pour prédire des informations. Fortes heureusement pour nous, de grandes entreprises sont déjà passé par là et proposent des solutions pour nous aider dans notre tâche. Kai prend l&amp;#39;exemple de &lt;a href=&quot;https://eng.uber.com/michelangelo/&quot;&gt;Michelangelo&lt;/a&gt;, un framework made in Uber pour mettre en place du Machine Learning &amp;quot;at uber&amp;#39;s scale&amp;quot;.&lt;/p&gt;
&lt;!-- end paragraph 6b5ff748-deea-4f15-8962-b0af9001e185--&gt;
&lt;!-- begin paragraph 7aca22a5-b2e3-4256-a6a3-5c0a3f8c51ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7aca22a5-b2e3-4256-a6a3-5c0a3f8c51ac&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7aca22a5-b2e3-4256-a6a3-5c0a3f8c51ac--&gt;
&lt;!-- begin paragraph f26d37cd-0df4-4b76-8967-80594c7477e6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f26d37cd-0df4-4b76-8967-80594c7477e6&quot;&gt;La plupart des solutions fonctionnent avec Kafka et Kai nous donne les différentes raisons qui font que Kafka est la solution pour du machine learning en production. Il insiste d&amp;#39;ailleurs sur un point très important, Kafka n&amp;#39;est pas seulement un mécanisme de publication de message mais aussi un lieu de storage scalable et un lieu où on a le pouvoir de prétraiter notre data. De plus il tient aussi à nous rappeler que kafka ne se résume pas au broker que l&amp;#39;ont chéri tant mais fait partie d&amp;#39;un écosystème où l&amp;#39;on retrouve &lt;a href=&quot;https://kafka.apache.org/documentation/streams/&quot;&gt;Kafka Streams&lt;/a&gt;,  &lt;a href=&quot;https://kafka.apache.org/documentation/#connect&quot;&gt;Kafka Connect&lt;/a&gt; ou encore &lt;a href=&quot;https://www.confluent.io/product/ksql/&quot;&gt;KSQL&lt;/a&gt; (un outil développé pour prétraiter de la data en SQL en production permettant ainsi de réconcilier Data Scientists et Data Engineers). Le fait est qu&amp;#39;aujourd&amp;#39;hui il existe en plus une &lt;a href=&quot;https://github.com/tensorflow/io/tree/master/tensorflow_io/kafka&quot;&gt;solution&lt;/a&gt; pour faire ingérer un flux de données venant de Kafka directement à TensorFlow ce qui nous permet d’entraîner notre modèle en temps réel. Toutes ses caractéristiques pour ne citer qu&amp;#39;eux rendent Kafka très attrayant justifiant ainsi sa popularité.&lt;/p&gt;
&lt;!-- end paragraph f26d37cd-0df4-4b76-8967-80594c7477e6--&gt;
&lt;!-- begin heading_1 1d7356b8-fdaa-4777-9114-9c746d27e2a5--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1d7356b8-fdaa-4777-9114-9c746d27e2a5&quot;&gt;Traitement automatique du langage pour le routage d&amp;#39;emails&lt;/h2&gt;
&lt;!-- end heading_1 1d7356b8-fdaa-4777-9114-9c746d27e2a5--&gt;
&lt;!-- begin paragraph e9b22a3c-9531-4acc-b00d-bcec0ec46fc0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e9b22a3c-9531-4acc-b00d-bcec0ec46fc0&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=ANrdU6UgtZU&quot;&gt;https://www.youtube.com/watch?v=ANrdU6UgtZU&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph e9b22a3c-9531-4acc-b00d-bcec0ec46fc0--&gt;
&lt;!-- begin paragraph 5794152b-cece-476c-b90b-db63d032a725--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5794152b-cece-476c-b90b-db63d032a725&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5794152b-cece-476c-b90b-db63d032a725--&gt;
&lt;!-- begin paragraph 30a72ce3-b175-47f0-adcd-9428186e7d7a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-30a72ce3-b175-47f0-adcd-9428186e7d7a&quot;&gt;Il y a eu beaucoup de data engineering dans ces derniers talks, il était temps de prendre un peu l&amp;#39;air et rien de mieux que ce talk pour se détacher des technologies du big data afin de s&amp;#39;adonner à un peu de text mining avec l&amp;#39;incroyable librairie &lt;a href=&quot;https://github.com/MAIF/melusine&quot;&gt;Melusine&lt;/a&gt; codée en python.&lt;/p&gt;
&lt;!-- end paragraph 30a72ce3-b175-47f0-adcd-9428186e7d7a--&gt;
&lt;!-- begin paragraph 701c3adb-3346-4334-91a3-0e13f88eb170--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-701c3adb-3346-4334-91a3-0e13f88eb170&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 701c3adb-3346-4334-91a3-0e13f88eb170--&gt;
&lt;!-- begin paragraph 1d6c44a3-3b6b-482a-99ed-2150c7024cb7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d6c44a3-3b6b-482a-99ed-2150c7024cb7&quot;&gt;Ce talk nous a été fournis par deux Data Scientists travaillant à Quantmetry, &lt;a href=&quot;https://fr.linkedin.com/in/tom-stringer-a3a5b591&quot;&gt;Tom Stringer&lt;/a&gt; et &lt;a href=&quot;https://fr.linkedin.com/in/antoine-isnardy&quot;&gt;Antoine Isnardy&lt;/a&gt;. La librairie a pour but de fournir tout les outils nécessaires afin de faire du traitement automatique de mail de A à Z. Elle se décompose en plusieurs modules dont trois principaux :&lt;/p&gt;
&lt;!-- end paragraph 1d6c44a3-3b6b-482a-99ed-2150c7024cb7--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Un module de preprocessing et de cleaning de mail afin de diviser les différentes parties de ce dernier.&lt;/li&gt;&lt;li&gt;Un module qui résume un mail en extrayant les différents mots jugés importants.&lt;/li&gt;&lt;li&gt;Un module de classification qui utilise les réseaux de neurones récurrents et/ou les réseaux neuronales convolutifs pour classifier les mails vis-à-vis des catégories définies au préalable&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 0bf32bd8-2010-4ca1-a28d-b13821bab25c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0bf32bd8-2010-4ca1-a28d-b13821bab25c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0bf32bd8-2010-4ca1-a28d-b13821bab25c--&gt;
&lt;!-- begin paragraph 760ff8db-0258-47df-8085-cc0dde06dfcd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-760ff8db-0258-47df-8085-cc0dde06dfcd&quot;&gt;Ce talk a notamment été intéressant, car il a soulevé certaines problématiques telles que la transformation d&amp;#39;un produit (Melusine était destinée à être utilisé à la Maif) en un outil open source qui, de ce fait, requiert d&amp;#39;avoir une documentation claire et précise ainsi qu&amp;#39;un code modulable et adaptable pour tous. &lt;/p&gt;
&lt;!-- end paragraph 760ff8db-0258-47df-8085-cc0dde06dfcd--&gt;
&lt;!-- begin heading_1 2dfef1ab-2b12-47dc-9a12-3ef34fc2a6ad--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2dfef1ab-2b12-47dc-9a12-3ef34fc2a6ad&quot;&gt;L&amp;#39;incroyable efficacité de l&amp;#39;unification des logs !&lt;/h2&gt;
&lt;!-- end heading_1 2dfef1ab-2b12-47dc-9a12-3ef34fc2a6ad--&gt;
&lt;!-- begin paragraph c9dd9388-23d2-4d7f-a29a-d10bd4bcbbe5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c9dd9388-23d2-4d7f-a29a-d10bd4bcbbe5&quot;&gt;&lt;br /&gt;
&lt;/p&gt;
&lt;!-- end paragraph c9dd9388-23d2-4d7f-a29a-d10bd4bcbbe5--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/6703398e-59f6-437b-b331-3f497e417673/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 976f34f1-dbe8-487f-a353-ce4eca4cd541--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-976f34f1-dbe8-487f-a353-ce4eca4cd541&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=ryFAZZEZbyw&quot;&gt;https://www.youtube.com/watch?v=ryFAZZEZbyw&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 976f34f1-dbe8-487f-a353-ce4eca4cd541--&gt;
&lt;!-- begin paragraph 92ee522c-1185-47b5-9d51-c3afb27b92f7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-92ee522c-1185-47b5-9d51-c3afb27b92f7&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 92ee522c-1185-47b5-9d51-c3afb27b92f7--&gt;
&lt;!-- begin paragraph 138355c1-8d49-445c-8486-cd1252b68d42--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-138355c1-8d49-445c-8486-cd1252b68d42&quot;&gt;Le talk tant attendu par les univaliens puisque le talker n&amp;#39;est personne d&amp;#39;autre que &lt;a href=&quot;https://fr.linkedin.com/in/jwinandy/fr&quot;&gt;Jonathan Winandy&lt;/a&gt;, le fondateur d&amp;#39;&lt;a href=&quot;https://www.univalence.io/&quot;&gt;Univalence&lt;/a&gt; 😎. Dans ce talk, Jon a décidé de parler de log et de tracing à travers un talk assez déjanté et très bien structuré. D&amp;#39;ailleurs, Jon écrira très certainement un article qui rentrera plus en détail que cette petite introduction. &lt;/p&gt;
&lt;!-- end paragraph 138355c1-8d49-445c-8486-cd1252b68d42--&gt;
&lt;!-- begin paragraph 2b392d88-122f-4dcc-b5ac-8bba31736784--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2b392d88-122f-4dcc-b5ac-8bba31736784&quot;&gt;&lt;br /&gt;
Le talk part d&amp;#39;une tendance concrète, aujourd&amp;#39;hui nos applications sont de plus en plus complexes et il est de plus en plus dur de créer une histoire retraçant les actions de notre système lui-même composé d&amp;#39;une multitude de micro services. L&amp;#39;histoire est, concrètement, un ou plusieurs fichiers où sont stockés des événements émis par l&amp;#39;application. On peut par exemple, en Java, utiliser l&amp;#39;&lt;a href=&quot;https://logging.apache.org/log4j/2.x/&quot;&gt;Apache Logging Services&lt;/a&gt; (Log4j 2) pour créer ce système de log.&lt;/p&gt;
&lt;!-- end paragraph 2b392d88-122f-4dcc-b5ac-8bba31736784--&gt;
&lt;!-- begin paragraph cdf2b02f-6500-4fff-b6a0-a6adba3ca5d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cdf2b02f-6500-4fff-b6a0-a6adba3ca5d4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph cdf2b02f-6500-4fff-b6a0-a6adba3ca5d4--&gt;
&lt;!-- begin paragraph 4a316e53-6bc2-4d05-b8f8-15a551372fa5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a316e53-6bc2-4d05-b8f8-15a551372fa5&quot;&gt;Malheureusement, les applications sont devenues trop complexes pour que le logging soit efficace, une application a une multitude de micros services et chacun d&amp;#39;entre eux racontent sa propre histoire. Jon nous parle alors du descendant du logging, le tracing et d&amp;#39;une technologie qui met tout le monde d&amp;#39;accord : &lt;a href=&quot;https://opentelemetry.io/&quot;&gt;OpenTelemetry&lt;/a&gt;. Le tracing, comparer au logging, a un contexte causal créant une dépendance entre les logs. &lt;/p&gt;
&lt;!-- end paragraph 4a316e53-6bc2-4d05-b8f8-15a551372fa5--&gt;
&lt;!-- begin paragraph 851a8e63-3af1-4057-b5ce-ffc0ac6eca28--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-851a8e63-3af1-4057-b5ce-ffc0ac6eca28&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 851a8e63-3af1-4057-b5ce-ffc0ac6eca28--&gt;
&lt;!-- begin paragraph e92e0b5a-e9b5-4d52-ab17-64e14a76bbca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e92e0b5a-e9b5-4d52-ab17-64e14a76bbca&quot;&gt;Mais encore une fois, ce n&amp;#39;est pas assez. Jon propose d&amp;#39;aller encore plus loin dans le tracing avec un projet : Zoom. Son but étant d&amp;#39;utiliser les outils de monitoring distribué, d&amp;#39;unifier les logs avec la donnée sous forme d&amp;#39;événement, et d&amp;#39;améliorer la prise en compte des lieux de calculs en accentuant sur les lignes de codes utilisés, les commits effectués, la machine qui calcule, etc. Afin d&amp;#39;avoir l&amp;#39;histoire la plus claire possible.&lt;/p&gt;
&lt;!-- end paragraph e92e0b5a-e9b5-4d52-ab17-64e14a76bbca--&gt;
&lt;!-- begin heading_1 a9e8cfa2-e530-42b1-9f77-38108dd8c5c6--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a9e8cfa2-e530-42b1-9f77-38108dd8c5c6&quot;&gt;Incremental Data Architecture&lt;/h2&gt;
&lt;!-- end heading_1 a9e8cfa2-e530-42b1-9f77-38108dd8c5c6--&gt;
&lt;!-- begin paragraph 40c20803-cd5a-49fb-9877-0251c554fb6e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-40c20803-cd5a-49fb-9877-0251c554fb6e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 40c20803-cd5a-49fb-9877-0251c554fb6e--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/6703398e-59f6-437b-b331-3f497e417673/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph e0667f0a-fda2-425d-8ab2-b1f1c4686f3a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e0667f0a-fda2-425d-8ab2-b1f1c4686f3a&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=sew2i7G96a8&quot;&gt;https://www.youtube.com/watch?v=sew2i7G96a8&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph e0667f0a-fda2-425d-8ab2-b1f1c4686f3a--&gt;
&lt;!-- begin paragraph d9480de1-bd28-4107-b460-7dbb7b104a2e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d9480de1-bd28-4107-b460-7dbb7b104a2e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d9480de1-bd28-4107-b460-7dbb7b104a2e--&gt;
&lt;!-- begin paragraph 81d95af2-8941-4a09-a8b0-1d091c668cf2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81d95af2-8941-4a09-a8b0-1d091c668cf2&quot;&gt;On change de salle et on rejoint un talk sur l&amp;#39;incremental data architecture, un talk vraiment très intéressant et très dynamique donné par &lt;a href=&quot;https://fr.linkedin.com/in/walid-haouari-41b547ab&quot;&gt;Walid HAOUARI&lt;/a&gt;, Big Data Engineer à &lt;a href=&quot;https://xebia.fr/&quot;&gt;Xebia&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 81d95af2-8941-4a09-a8b0-1d091c668cf2--&gt;
&lt;!-- begin paragraph 86a0be3b-4b59-4f87-a2cd-addb344c3e9b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86a0be3b-4b59-4f87-a2cd-addb344c3e9b&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 86a0be3b-4b59-4f87-a2cd-addb344c3e9b--&gt;
&lt;!-- begin paragraph c41b1312-dbe8-4376-a4c4-12dcf4098adf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c41b1312-dbe8-4376-a4c4-12dcf4098adf&quot;&gt;Ce talk était bien, vraiment bien. Le sujet abordé était très universel et le choix de passer par un cas concret permet d&amp;#39;associer ce talk aux problématiques que l&amp;#39;on rencontre dans nos différents métiers. Walid a pris le cas d&amp;#39;une stratup fournissant un système d&amp;#39;analyse médicale à un petit village d&amp;#39;Afrique ayant pour but de s’accroître pour obtenir une architecture capable de gérer plusieurs villages entiers. Il explique comment et pourquoi il est préférable d&amp;#39;établir une architecture cible dès le commencement puis de la déstructurer afin d&amp;#39;obtenir des architectures réalisables d&amp;#39;un point de vue économique, temporelles et technologiques. Pour, par la suite, incrémenter cette architecture et tendre vers l&amp;#39;architecture cible.&lt;/p&gt;
&lt;!-- end paragraph c41b1312-dbe8-4376-a4c4-12dcf4098adf--&gt;
&lt;!-- begin paragraph 3076d224-0e94-4378-9a3b-b95dd1f42ce3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3076d224-0e94-4378-9a3b-b95dd1f42ce3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 3076d224-0e94-4378-9a3b-b95dd1f42ce3--&gt;
&lt;!-- begin paragraph 4a609a20-78ed-4acf-9110-412ae4df2be2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a609a20-78ed-4acf-9110-412ae4df2be2&quot;&gt;Il nous met en garde sur le choix que font certaines entreprises de changer complètement d&amp;#39;infrastructure, un choix qui se trouve être la plupart du temps inefficace et périlleux car vous risquer d&amp;#39;écœurer votre équipe qui ne retrouve plus ses marques et qui doit, si cela n&amp;#39;a pas été fait auparavant, monter en compétences pour maîtriser tout les nouveaux outils.&lt;/p&gt;
&lt;!-- end paragraph 4a609a20-78ed-4acf-9110-412ae4df2be2--&gt;
&lt;!-- begin heading_1 f624ad31-3f20-46a3-81a3-d368adb31fe0--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f624ad31-3f20-46a3-81a3-d368adb31fe0&quot;&gt;Harnessing the power of Generative Adversarial Networks (GANS) for supervised learning&lt;/h2&gt;
&lt;!-- end heading_1 f624ad31-3f20-46a3-81a3-d368adb31fe0--&gt;
&lt;!-- begin paragraph a6f92187-2a99-405c-b8f3-95381a6a16bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a6f92187-2a99-405c-b8f3-95381a6a16bc&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a6f92187-2a99-405c-b8f3-95381a6a16bc--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/6703398e-59f6-437b-b331-3f497e417673/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 89cc2816-edf9-47bb-b927-bd5b23964656--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89cc2816-edf9-47bb-b927-bd5b23964656&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=aL7rhJz8mAI&quot;&gt;https://www.youtube.com/watch?v=aL7rhJz8mAI&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 89cc2816-edf9-47bb-b927-bd5b23964656--&gt;
&lt;!-- begin paragraph 3d0bc0ab-2a1e-47b7-9716-b4ba3b8e9fc2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d0bc0ab-2a1e-47b7-9716-b4ba3b8e9fc2&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 3d0bc0ab-2a1e-47b7-9716-b4ba3b8e9fc2--&gt;
&lt;!-- begin paragraph c53d0353-7d60-4d9b-9f15-1df8476768f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c53d0353-7d60-4d9b-9f15-1df8476768f2&quot;&gt;S&amp;#39;en est suit un talk d&amp;#39;&lt;a href=&quot;https://www.linkedin.com/in/olga-p-petrova?originalSubdomain=fr&quot;&gt;Olga Petrova&lt;/a&gt;, une Machine Learning DevOps Engineer à &lt;a href=&quot;https://www.scaleway.com/en/&quot;&gt;Scaleway&lt;/a&gt;. Elle a fait le choix de nous parler des Generative Adversarial Networks plus communément appelé GAN notamment très utilisé pour générer des images très réaliste dans le cas d&amp;#39;un face swaping par exemple. &lt;/p&gt;
&lt;!-- end paragraph c53d0353-7d60-4d9b-9f15-1df8476768f2--&gt;
&lt;!-- begin paragraph 999795f4-a8e8-4140-bbfc-4c6fa7495436--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-999795f4-a8e8-4140-bbfc-4c6fa7495436&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 999795f4-a8e8-4140-bbfc-4c6fa7495436--&gt;
&lt;!-- begin paragraph 922ea876-31dc-41b4-9415-8e241bd529fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-922ea876-31dc-41b4-9415-8e241bd529fb&quot;&gt;Après une courte introduction sur la différence entre supervised et unsupervised learning. Olga nous a montré des exemples d&amp;#39;applications des GAN. GAN est notamment utilisé pour générer la tête de personne n&amp;#39;existant pas alors que tout porte à croire l&amp;#39;inverse tant le réalisme est au rendez-vous. C&amp;#39;est comme ça que &lt;a href=&quot;https://thispersondoesnotexist.com/&quot;&gt;https://thispersondoesnotexist.com/&lt;/a&gt; existe, un projet créé par Nvidia. D&amp;#39;autres projets tout autant ambitieux nous ont été présenté tels que la face frontalization qui consiste à prendre une photo d&amp;#39;une personne ayant un angle de tête quelconque en entrée et de renvoyer sa tête de face en sortie ou encore la super résolution (Un exemple de projet &lt;a href=&quot;https://github.com/idealo/image-super-resolution&quot;&gt;ici&lt;/a&gt;) visant à augmenter la résolution d&amp;#39;une image. &lt;/p&gt;
&lt;!-- end paragraph 922ea876-31dc-41b4-9415-8e241bd529fb--&gt;
&lt;!-- begin paragraph c54f14c6-fe13-4b91-bced-57ddef736f7b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c54f14c6-fe13-4b91-bced-57ddef736f7b&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c54f14c6-fe13-4b91-bced-57ddef736f7b--&gt;
&lt;!-- begin paragraph b56d2bfc-af25-4830-94a4-d88e4bcfc363--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b56d2bfc-af25-4830-94a4-d88e4bcfc363&quot;&gt;Elle nous a aussi fait part de sa libraire de machine learning en python préféré. Alors que tout le monde ne jure que par &lt;a href=&quot;https://www.tensorflow.org/&quot;&gt;Tensorflow&lt;/a&gt; ou &lt;a href=&quot;https://keras.io/&quot;&gt;Keras&lt;/a&gt;, Olga, quant à elle, ne jure que par &lt;a href=&quot;https://pytorch.org/&quot;&gt;PyTorch&lt;/a&gt; et était étonnée qu&amp;#39;il ne soit pas mentionner dans les autres talks ce qui à attiser ma curiosité. Fun fact intéressant, ces librairies en accord avec &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=3321441&quot;&gt;cet article&lt;/a&gt; atteignent les limites du langage, à voir ce que le futur nous réserve !&lt;/p&gt;
&lt;!-- end paragraph b56d2bfc-af25-4830-94a4-d88e4bcfc363--&gt;
&lt;!-- begin heading_1 5de4a413-1c3f-48bc-89bc-608e787d17ce--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5de4a413-1c3f-48bc-89bc-608e787d17ce&quot;&gt;Give meaning to 100 billion analytics events a day, analytics at Teads&lt;/h2&gt;
&lt;!-- end heading_1 5de4a413-1c3f-48bc-89bc-608e787d17ce--&gt;
&lt;!-- begin paragraph 888f5682-c910-4ae0-a068-28375b4b9711--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-888f5682-c910-4ae0-a068-28375b4b9711&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=Up3iaK5fdjw&quot;&gt;https://www.youtube.com/watch?v=Up3iaK5fdjw&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 888f5682-c910-4ae0-a068-28375b4b9711--&gt;
&lt;!-- begin paragraph a76ec060-aea2-49b1-8da9-ddf357700746--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a76ec060-aea2-49b1-8da9-ddf357700746&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a76ec060-aea2-49b1-8da9-ddf357700746--&gt;
&lt;!-- begin paragraph b6e7c45d-ddf2-4b42-8c21-bbe00b7bb8fd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b6e7c45d-ddf2-4b42-8c21-bbe00b7bb8fd&quot;&gt;La journée continue et touche presque à sa fin. Notre avant-dernier talk est produit par &lt;a href=&quot;https://fr.linkedin.com/in/albanperillatmerceroz&quot;&gt;Alban Perillat-Merceroz&lt;/a&gt;, Software Engineering Master pour &lt;a href=&quot;https://www.teads.com/&quot;&gt;Teads&lt;/a&gt;. Cette société ne vous dit peut-être rien au premier abord et pourtant, tout le monde a déjà vu leur travail à l&amp;#39;oeuvre. La pub qui s&amp;#39;affiche lorsque vous lisiez vos news le matin pendant le trajet jusqu&amp;#39;au travail, ce sont eux! Et pour pouvoir proposer les meilleures pubs aux différents utilisateurs, Teads est confrontée à plus de 100 milliards d&amp;#39;événements par jour.&lt;/p&gt;
&lt;!-- end paragraph b6e7c45d-ddf2-4b42-8c21-bbe00b7bb8fd--&gt;
&lt;!-- begin paragraph e2aead87-ffed-46ea-8de0-00004b1efebe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2aead87-ffed-46ea-8de0-00004b1efebe&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e2aead87-ffed-46ea-8de0-00004b1efebe--&gt;
&lt;!-- begin paragraph a653e2b9-3328-40dc-9c5f-c11a937b4ffd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a653e2b9-3328-40dc-9c5f-c11a937b4ffd&quot;&gt;Autant d&amp;#39;événements supposent une architecture Big Data solide pouvant engranger un aussi grand nombre de données. Pour résumer très succinctement, voici comment leur architecture se construit. Le browser envoi des événements à savoir : si la personne à cliquer sur la pub, quand la pub à commencer, quand la pub à stopper. Ces événements transitent dans Kafka, sont traités via &lt;a href=&quot;https://cloud.google.com/gartner-magic-quadrant-for-dmsa/&quot;&gt;DataFlow&lt;/a&gt; pour ensuite être écrits directement dans BigQuery. Il reste à récupérer les données qui sont encore à ce stade des raw data pour les fournir en tant qu&amp;#39;outils d&amp;#39;analyse et ainsi apporter de la valeur. À ce stade, l&amp;#39;entreprise est passée par plusieurs types d&amp;#39;architecture (Infobright Enterprise Edition) pour finalement entièrement se tourner sur &lt;a href=&quot;https://www.redshift3d.com/&quot;&gt;RedShift&lt;/a&gt; en tant que datamart.&lt;/p&gt;
&lt;!-- end paragraph a653e2b9-3328-40dc-9c5f-c11a937b4ffd--&gt;
&lt;!-- begin paragraph d7bcaaa0-871e-4d8f-8682-c6d793a6263d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d7bcaaa0-871e-4d8f-8682-c6d793a6263d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d7bcaaa0-871e-4d8f-8682-c6d793a6263d--&gt;
&lt;!-- begin paragraph 6f02ff68-3253-4455-a4e8-c87eb72913b4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6f02ff68-3253-4455-a4e8-c87eb72913b4&quot;&gt;Cette technologie est assez atypique car jouant sur deux types de services de cloud computing différents créant ainsi une latence lors des transitions entre deux types de technologies. Une question très intéressante a d&amp;#39;ailleurs été posée à la fin de ce talk ouvrant la porte à une solution permettant d&amp;#39;accumuler la totalité de la data, passé de Kafka à Cloud Pub/Sub pour garder une cohérence technologique et rendre le système beaucoup plus pratique. On a tendance à oublier que des &amp;quot;équivalents&amp;quot; Kafka existent tant ce dernier est chéri par tous et il est intéressant de songer aux alternatives qui dans des cas comme celui-ci ou on a une architecture très orienté gcp pourrait être une bonne idée.&lt;/p&gt;
&lt;!-- end paragraph 6f02ff68-3253-4455-a4e8-c87eb72913b4--&gt;
&lt;!-- begin heading_1 1e94a3c4-07ab-4d91-bb7b-52c95496236d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1e94a3c4-07ab-4d91-bb7b-52c95496236d&quot;&gt;The internals of stateful stream processing in Spark Structured Streaming&lt;/h2&gt;
&lt;!-- end heading_1 1e94a3c4-07ab-4d91-bb7b-52c95496236d--&gt;
&lt;!-- begin paragraph 1143244c-b9fb-4306-8450-64d268a86490--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1143244c-b9fb-4306-8450-64d268a86490&quot;&gt;Lien vers la vidéo: &lt;a href=&quot;https://www.youtube.com/watch?v=yhSwQ-GqAoA&quot;&gt;https://www.youtube.com/watch?v=yhSwQ-GqAoA&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 1143244c-b9fb-4306-8450-64d268a86490--&gt;
&lt;!-- begin paragraph 98bfd14e-953a-462e-b27e-d6b660c29822--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-98bfd14e-953a-462e-b27e-d6b660c29822&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 98bfd14e-953a-462e-b27e-d6b660c29822--&gt;
&lt;!-- begin paragraph 908279d2-f2b7-4bc1-ae25-392acd3b3997--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-908279d2-f2b7-4bc1-ae25-392acd3b3997&quot;&gt;De par son côté très technique, finir avec un talk de &lt;a href=&quot;https://about.me/jaceklaskowski&quot;&gt;Jacek Laskowski&lt;/a&gt;, un grand nom de la communauté Spark, n&amp;#39;est pas gagné. Cependant son côté très enjoué et plein de vie a complètement inversé la tendance et ce qui devait être un talk assez lourd c&amp;#39;est transformé en quelque chose de très plaisant à regarder et à écouter. Ce talk se base sur spark 2.4.3 et se base sur la partie &lt;a href=&quot;https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html&quot;&gt;structured streaming&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 908279d2-f2b7-4bc1-ae25-392acd3b3997--&gt;
&lt;!-- begin paragraph ed236f79-05bd-4412-817f-05ed7bef208d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ed236f79-05bd-4412-817f-05ed7bef208d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ed236f79-05bd-4412-817f-05ed7bef208d--&gt;
&lt;!-- begin paragraph 4c1566f5-e681-4b00-a9b7-b59b3ea6ee02--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4c1566f5-e681-4b00-a9b7-b59b3ea6ee02&quot;&gt;En réalité, spark ne fait pas exactement du temps réel. Comme l&amp;#39;explique Jacek, spark exécute des minis batchs très très rapidement à la place donnant cette impression de temps réel. Sachant cela, il est facile de comprendre que tout ce qui est faisable avec sparkSQL est faisable en streaming avec le structured streaming. Mais pas que, vous pouvez aussi très bien utiliser la DataSet API et vous ne serez donc pas perdu avec le côté streaming que fournis Spark.&lt;/p&gt;
&lt;!-- end paragraph 4c1566f5-e681-4b00-a9b7-b59b3ea6ee02--&gt;
&lt;!-- begin paragraph 309d2f5a-56e9-4535-921c-20b882753277--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-309d2f5a-56e9-4535-921c-20b882753277&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 309d2f5a-56e9-4535-921c-20b882753277--&gt;
&lt;!-- begin paragraph 38824b95-9a36-4412-981a-9c3ed45cebb6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38824b95-9a36-4412-981a-9c3ed45cebb6&quot;&gt;Spark streaming peut être vue comme un équivalent à &lt;a href=&quot;https://kafka.apache.org/documentation/streams/&quot;&gt;Kafka Streams&lt;/a&gt; ou encore &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Flink&lt;/a&gt; mais Jacek insiste quand même sur une différence majeure. Spark streaming a la possibilité de traiter une centaine de lignes en même temps quand les deux autres solutions ne peuvent en traiter qu&amp;#39;une à chaque fois.&lt;/p&gt;
&lt;!-- end paragraph 38824b95-9a36-4412-981a-9c3ed45cebb6--&gt;
&lt;!-- begin heading_1 6d111f5d-8957-43ff-bb47-6f8477f0a58d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6d111f5d-8957-43ff-bb47-6f8477f0a58d&quot;&gt;DataXDay pour l&amp;#39;équipe&lt;/h2&gt;
&lt;!-- end heading_1 6d111f5d-8957-43ff-bb47-6f8477f0a58d--&gt;
&lt;!-- begin paragraph a4bd382a-5e33-4878-beb5-dc72b9a3fa9c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a4bd382a-5e33-4878-beb5-dc72b9a3fa9c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph a4bd382a-5e33-4878-beb5-dc72b9a3fa9c--&gt;
&lt;!-- begin paragraph fa1d9288-19a0-4f44-b256-17baf181f42a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa1d9288-19a0-4f44-b256-17baf181f42a&quot;&gt;DataXDay ne se résume pas qu&amp;#39;à de très bons talks, mais aussi et surtout à une organisation faite aux petits oignons. En plus des talks, la qualité des différents repas (le saucisson et la fontaine de chocolat 😵), la bonne humeur de l&amp;#39;équipe de Xebia qui a organisé l&amp;#39;événement (&lt;a href=&quot;https://twitter.com/LoicMDivad&quot;&gt;Giulia Bianchi&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/souhaibxx&quot;&gt;Loïc Divad&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/souhaibxx&quot;&gt;Souhaib Guitoni&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/anne-beauchart-98554064/&quot;&gt;Anne Beauchart&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/sandra-pietrowska-721b09a8/?originalSubdomain=fr&quot;&gt;Sandra Pietrowska&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/samehbenf?lang=fr&quot;&gt;Sameh Ben Fredj&lt;/a&gt; et &lt;a href=&quot;https://twitter.com/ElhadiCherifi&quot;&gt;Elhadi Cherifi&lt;/a&gt;) et la qualité des animations (le data lake était génial encore merci pour les tableaux 😉) ont fait de cet événement un événement mémorable pour l&amp;#39;équipe Univalence. Cet événement nous a permis de tous nous retrouver dans la joie et la bonne humeur et il m&amp;#39;a aussi permis de découvrir et/ou me forger un avis sur énormément de différentes technologies du Big Data.&lt;/p&gt;
&lt;!-- end paragraph fa1d9288-19a0-4f44-b256-17baf181f42a--&gt;
&lt;!-- begin paragraph 755ac46d-26a8-48b8-906d-33884c948ecd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-755ac46d-26a8-48b8-906d-33884c948ecd&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 755ac46d-26a8-48b8-906d-33884c948ecd--&gt;
&lt;!-- begin paragraph 5256b59e-fe3d-48de-90ae-1f3210ebad71--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5256b59e-fe3d-48de-90ae-1f3210ebad71&quot;&gt;Hâte de vous retrouvez au DataXDay 2020 🚀&lt;/p&gt;
&lt;!-- end paragraph 5256b59e-fe3d-48de-90ae-1f3210ebad71--&gt;
&lt;!-- begin paragraph 616d9d24-3d8b-4f4a-9457-1766682caaa4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-616d9d24-3d8b-4f4a-9457-1766682caaa4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 616d9d24-3d8b-4f4a-9457-1766682caaa4--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:2a0a8e0961924e60a1a070abe55eb93a</id>
    <title>Tests with Spark: how to keep our heads above water</title>
    <updated>2019-12-08T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/tests-with-spark-how-to-keep-our-heads-above-water.html"/>
    <!--summary Data environment is complicated enough, no? Spark-Test is here to make your life easier during your tests with Spark ⚡-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Spark"></category>    <category term="SPARK-TEST"></category>    <category term="Spark-Tools"></category>    <category term="Introduction"></category>    <content type="html">
&lt;!-- begin paragraph d32d9356-01f7-48ca-8d38-dd4f82559043--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d32d9356-01f7-48ca-8d38-dd4f82559043&quot;&gt;Are you drowning in your Spark tests? Are you tired of declaring a Spark session every time? We have the solution for you! With Spark-Test™, write your tests without any difficulties!&lt;/p&gt;
&lt;!-- end paragraph d32d9356-01f7-48ca-8d38-dd4f82559043--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/2a0a8e09-6192-4e60-a1a0-70abe55eb93a/34ol2n.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 75114a1f-4cbb-4089-a35b-e887f3cb69c4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75114a1f-4cbb-4089-a35b-e887f3cb69c4&quot;&gt;Great question, Baby Beaver! Spark-Test is one of the numerous libraries in Spark-Tools, a set of tools designed to make life easier for Spark users. We invite you to discover the other tools over the next few articles we&amp;#39;ll be publishing. (Pardon our French! There are already some en français, &lt;a href=&quot;https://blog.univalence.io/spark-zio/&quot;&gt;on Spark-ZIO&lt;/a&gt;, a library that combines ZIO and Spark, and on &lt;a href=&quot;https://github.com/univalence/spark-plumbus&quot;&gt;Spark Plumbus&lt;/a&gt; proposing some creative solutions &lt;a href=&quot;https://blog.univalence.io/fonctions-dordre-superieur-dans-spark-2-pour-traiter-des-structures-imbriquees/&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://blog.univalence.io/en-finir-avec-les-problemes-de-case-class-dans-spark/&quot;&gt;there&lt;/a&gt;.) &lt;/p&gt;
&lt;!-- end paragraph 75114a1f-4cbb-4089-a35b-e887f3cb69c4--&gt;
&lt;!-- begin heading_1 fb607772-2c8f-478c-adf6-3338041bde79--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-fb607772-2c8f-478c-adf6-3338041bde79&quot;&gt;Let&amp;#39;s discover Spark-Test&lt;/h2&gt;
&lt;!-- end heading_1 fb607772-2c8f-478c-adf6-3338041bde79--&gt;
&lt;!-- begin paragraph be347dda-15e4-4e68-9967-ce6326672946--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be347dda-15e4-4e68-9967-ce6326672946&quot;&gt;Today, we start with the library that is the easiest to learn and to use from the set: Spark-Test. This library provides tools that makes it easier to create tests related to dataframes and/or datasets by making them simpler while providing clear and accurate information through metadata!&lt;/p&gt;
&lt;!-- end paragraph be347dda-15e4-4e68-9967-ce6326672946--&gt;
&lt;!-- begin paragraph 961e1eca-e01f-4ef5-9653-6067428fc943--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-961e1eca-e01f-4ef5-9653-6067428fc943&quot;&gt;Some of your might already be familiar with MrPowers&amp;#39; &lt;i&gt;spark-fast-tests&lt;/i&gt; (&lt;a href=&quot;https://github.com/MrPowers/spark-fast-tests&quot;&gt;https://github.com/MrPowers/spark-fast-tests&lt;/a&gt;). We decided to tackle testing problems from a different angle, which resulted in a very different conception. Apart from having a different architecture, Spark-Test offers more accurate error reporting. Currently, we&amp;#39;re working on improving performance for future updates, which will help greatly accelerate testing.&lt;/p&gt;
&lt;!-- end paragraph 961e1eca-e01f-4ef5-9653-6067428fc943--&gt;
&lt;!-- begin paragraph 8352aa75-f2dd-4872-b137-73e6f71402ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8352aa75-f2dd-4872-b137-73e6f71402ac&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8352aa75-f2dd-4872-b137-73e6f71402ac--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://media.giphy.com/media/f7tlW8dWK188o/giphy.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 31b36f80-aa35-42f1-b5fc-f12a676c15bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-31b36f80-aa35-42f1-b5fc-f12a676c15bb&quot;&gt;Caption: (For testing, I mean. For testing!) ou ou... (Just kidding. We all love testing, don&amp;#39;t we?)&lt;/p&gt;
&lt;!-- end paragraph 31b36f80-aa35-42f1-b5fc-f12a676c15bb--&gt;
&lt;!-- begin heading_1 189dcdd7-1dd2-4f5b-88e3-ff055bf0ecf0--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-189dcdd7-1dd2-4f5b-88e3-ff055bf0ecf0&quot;&gt;Setting up&lt;/h2&gt;
&lt;!-- end heading_1 189dcdd7-1dd2-4f5b-88e3-ff055bf0ecf0--&gt;
&lt;!-- begin paragraph 6446d9bf-6541-498a-b638-82237412445f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6446d9bf-6541-498a-b638-82237412445f&quot;&gt;It&amp;#39;s easy. You just need to add Spark-Test&amp;#39;s dependency in &lt;code&gt;build.sbt&lt;/code&gt;, then &lt;code&gt;extends SparkTest&lt;/code&gt; in your test file and &lt;i&gt;voilà!&lt;/i&gt; For those who are starting to dabble with Scala libraries, here&amp;#39;s a skeleton of Spark-Test you can build on.&lt;/p&gt;
&lt;!-- end paragraph 6446d9bf-6541-498a-b638-82237412445f--&gt;
&lt;!-- begin code 57efe8b1-f8c3-4844-a151-f50dbd51a768--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-57efe8b1-f8c3-4844-a151-f50dbd51a768&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies += &amp;quot;io.univalence&amp;quot; % &amp;quot;spark-test_2.12&amp;quot; % &amp;quot;0.3&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 57efe8b1-f8c3-4844-a151-f50dbd51a768--&gt;
&lt;!-- begin code 650d63b9-749e-41e2-92de-f911f2e18057--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-650d63b9-749e-41e2-92de-f911f2e18057&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;package io.univalence.sparktest

import org.scalatest.FunSuite

class GettingStartedTest extends FunSuite with SparkTest {

  test(&amp;quot;some test&amp;quot;) {
    // Start the test here...
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 650d63b9-749e-41e2-92de-f911f2e18057--&gt;
&lt;!-- begin paragraph a9444bee-2f56-4cec-aa42-e5e5eaaf6444--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a9444bee-2f56-4cec-aa42-e5e5eaaf6444&quot;&gt;Now that everything is ready, let&amp;#39;s start by creating a dataframe that will serve as an example throughout this article. Spark-Test provides tools that help you to create a dataframe or a dataset quickly for your tests. So we&amp;#39;re going to use one of them right now!&lt;/p&gt;
&lt;!-- end paragraph a9444bee-2f56-4cec-aa42-e5e5eaaf6444--&gt;
&lt;!-- begin paragraph 3be7a4fd-d9ca-43d6-9003-799adb581b2a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3be7a4fd-d9ca-43d6-9003-799adb581b2a&quot;&gt;You don&amp;#39;t need to instantiate a SparkSession. Everything is already set up for you.&lt;/p&gt;
&lt;!-- end paragraph 3be7a4fd-d9ca-43d6-9003-799adb581b2a--&gt;
&lt;!-- begin code 68676250-cf3b-46b6-9db1-20a30a1b5175--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-68676250-cf3b-46b6-9db1-20a30a1b5175&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val df = dataframe(&amp;quot;{a:1, b:true}&amp;quot;, &amp;quot;{a:2, b:false}&amp;quot;)
/*
| a |   b   |
+---+-------+
| 1 | true  |
| 2 | false |
*/&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 68676250-cf3b-46b6-9db1-20a30a1b5175--&gt;
&lt;!-- begin paragraph 29d48ba5-f63d-4e0b-8d24-a50f2a64aa83--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-29d48ba5-f63d-4e0b-8d24-a50f2a64aa83&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 29d48ba5-f63d-4e0b-8d24-a50f2a64aa83--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://media.giphy.com/media/3o7btNa0RUYa5E7iiQ/giphy.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 a351a89a-79c7-444c-a917-c963a552bb1a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a351a89a-79c7-444c-a917-c963a552bb1a&quot;&gt;Let&amp;#39;s compare dataframes&lt;/h2&gt;
&lt;!-- end heading_1 a351a89a-79c7-444c-a917-c963a552bb1a--&gt;
&lt;!-- begin paragraph a71fe9f1-7e45-4f09-9428-862c0f834ea1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a71fe9f1-7e45-4f09-9428-862c0f834ea1&quot;&gt;Now, let&amp;#39;s imagine that we want to compare two dataframes. You should know that we can obtain two types of differences: differences with both schemas and differences with the values.&lt;/p&gt;
&lt;!-- end paragraph a71fe9f1-7e45-4f09-9428-862c0f834ea1--&gt;
&lt;!-- begin paragraph ffb5c189-382c-48eb-876c-7b18bde73eed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ffb5c189-382c-48eb-876c-7b18bde73eed&quot;&gt;First, we will implement a solution &lt;b&gt;without using Spark-Test&lt;/b&gt;. To do this, we will need to retrieve schemas of the two dataframes, retrieve values, and compare them to see if our dataframes are different or not. This is one of the many possible solutions.&lt;/p&gt;
&lt;!-- end paragraph ffb5c189-382c-48eb-876c-7b18bde73eed--&gt;
&lt;!-- begin code 044bbd59-e96a-41b4-a150-2406be7f6bf7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-044bbd59-e96a-41b4-a150-2406be7f6bf7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//without using Spark-Test
def isEqual(df1: Dataframe, df2: Dataframe): Unit = {
  if (!df1.schema.equals(df2.schema))
    throw new Exception(&amp;quot;schemas are different&amp;quot;)
  if (!df1.collect().sameElements(df2.collect()))
    throw new Exception(&amp;quot;values are different&amp;quot;)
}

test(&amp;quot;some test without Spark-Test&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, c:false}&amp;quot;)

  isEqual(df1, df2)
  /*
   * java.lang.Exception:
   * Les schemas sont différents
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 044bbd59-e96a-41b4-a150-2406be7f6bf7--&gt;
&lt;!-- begin paragraph d021b116-d31a-46de-9753-64d287908bfe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d021b116-d31a-46de-9753-64d287908bfe&quot;&gt;This solution has several problems:&lt;/p&gt;
&lt;!-- end paragraph d021b116-d31a-46de-9753-64d287908bfe--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;The lack of information provided by our &lt;code&gt;Exception&lt;/code&gt;: we are able to know if the equality problem comes from the schema or if it comes from values. But we are unable to know exactly what the problem is, how schemas are different to each other, or which values are different.&lt;/li&gt;&lt;li&gt;The lack of flexibility: you may only want to compare the common columns between the two dataframes. As an example, we&amp;#39;re only looking at the column &amp;quot;a&amp;quot; in the dataframe above.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph b823ead4-b2f0-4462-a849-912f4bbebfc7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b823ead4-b2f0-4462-a849-912f4bbebfc7&quot;&gt;This may seem trivial, since our dataframes do not contain more than two columns and two rows. But imagine two dataframes with thousands of columns and millions of rows...&lt;/p&gt;
&lt;!-- end paragraph b823ead4-b2f0-4462-a849-912f4bbebfc7--&gt;
&lt;!-- begin paragraph c048e6eb-dbb6-48fc-9505-20a46983782d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c048e6eb-dbb6-48fc-9505-20a46983782d&quot;&gt;Now, let&amp;#39;s solve this problem using Spark-Test.&lt;/p&gt;
&lt;!-- end paragraph c048e6eb-dbb6-48fc-9505-20a46983782d--&gt;
&lt;!-- begin code 13b20f9b-589d-4a21-b2db-2a7128070a12--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-13b20f9b-589d-4a21-b2db-2a7128070a12&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;test(&amp;quot;some test with Spark-Test&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, c:false}&amp;quot;)

  df1.assertEquals(df2)
  /*
   * io.univalence.sparktest.SparkTest$SchemaError: 
   * Field c was not in the original DataFrame.
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 13b20f9b-589d-4a21-b2db-2a7128070a12--&gt;
&lt;!-- begin paragraph a768ad31-6cae-489d-b978-07cc35beadbc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a768ad31-6cae-489d-b978-07cc35beadbc&quot;&gt;We obtain a nice &lt;code&gt;SchemaError&lt;/code&gt;. And that&amp;#39;s not all! We also have the reason of this difference and that is what Spark-Test is all about. The error here comes from the column &amp;quot;c&amp;#39; which is not present in the original dataframe.&lt;/p&gt;
&lt;!-- end paragraph a768ad31-6cae-489d-b978-07cc35beadbc--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;But, but… I wanted to compare columns in common 😣&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph 33e166ce-673d-4db1-9805-7737306a7c86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-33e166ce-673d-4db1-9805-7737306a7c86&quot;&gt;Don&amp;#39;t panic! Don&amp;#39;t panic! Simply use the Spark-Test configuration and specify that you want to ignore the extra columns located in &lt;code&gt;df2&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 33e166ce-673d-4db1-9805-7737306a7c86--&gt;
&lt;!-- begin code 03f6c321-a89a-4b21-ba7e-0aa31aedb597--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-03f6c321-a89a-4b21-ba7e-0aa31aedb597&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;test(&amp;quot;some test with custom configuration&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, c:false}&amp;quot;)

  withConfiguration(failOnMissingExpectedCol = false)({ df1.assertEquals(df2) })
  /*
   * Success
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 03f6c321-a89a-4b21-ba7e-0aa31aedb597--&gt;
&lt;!-- begin paragraph 799fd0b7-6152-4818-8050-28d255550682--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-799fd0b7-6152-4818-8050-28d255550682&quot;&gt;Yeaaahh! The test goes off without a hitch. 🤗&lt;/p&gt;
&lt;!-- end paragraph 799fd0b7-6152-4818-8050-28d255550682--&gt;
&lt;!-- begin paragraph 1151c8a2-15fa-47d3-a452-c6b4e6f289bd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1151c8a2-15fa-47d3-a452-c6b4e6f289bd&quot;&gt;Now, let&amp;#39;s look at the case where two dataframes have the same schema but are still different. This is due to one or more value errors, i.e. dissonances within the columns themselves.&lt;/p&gt;
&lt;!-- end paragraph 1151c8a2-15fa-47d3-a452-c6b4e6f289bd--&gt;
&lt;!-- begin code b0b48980-b42a-4559-8625-13f9a2aa3ca8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b0b48980-b42a-4559-8625-13f9a2aa3ca8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;test(&amp;quot;some test with same schema&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1, b:true}&amp;quot;,  &amp;quot;{a:2, b:true}&amp;quot;, &amp;quot;{a:3, b:true}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, b:false}&amp;quot;, &amp;quot;{a:2, b:true}&amp;quot;, &amp;quot;{a:4, b:false}&amp;quot;)

  df1.assertEquals(df2)
  /*
   * io.univalence.sparktest.SparkTest$ValueError: 
   * The data set content is different :
   *
   * in value at b, false was not equal to true
   * dataframe({b: true, a: 1})
   * dataframe({b: false, a: 1})
   *
   * in value at b, false was not equal to true
   * in value at a, 4 was not equal to 3
   * dataframe({b: true, a: 3})
   * dataframe({b: false, a: 4})
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b0b48980-b42a-4559-8625-13f9a2aa3ca8--&gt;
&lt;!-- begin paragraph 81085b54-326f-4e05-95d3-2184420f3ad2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81085b54-326f-4e05-95d3-2184420f3ad2&quot;&gt;Finally, here it is, the second error: &lt;code&gt;ValueError&lt;/code&gt;. This error informs us about the differences between the two dataframes by specifying the error, as well as the lines where the said errors are found in eithers &lt;code&gt;df1&lt;/code&gt; or &lt;code&gt;df2&lt;/code&gt;. &lt;/p&gt;
&lt;!-- end paragraph 81085b54-326f-4e05-95d3-2184420f3ad2--&gt;
&lt;!-- begin paragraph 1eeb042e-3247-42cc-bf6b-4c504052f3b4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1eeb042e-3247-42cc-bf6b-4c504052f3b4&quot;&gt;The precision given by errors, the flexibility, and ease of use, here are the strengths of Spark-Test. This library provides other features (such as a function to check a predicate in a whole Dataset) that can make your life as a data engineer easier. The cherry on top? Spark-Test is fully open source. It is available here:&lt;/p&gt;
&lt;!-- end paragraph 1eeb042e-3247-42cc-bf6b-4c504052f3b4--&gt;
&lt;!-- begin paragraph b08aa9a8-c5d0-4849-9b1c-0fae39d0f0ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b08aa9a8-c5d0-4849-9b1c-0fae39d0f0ad&quot;&gt;&lt;a href=&quot;https://github.com/univalence/spark-tools/tree/master/spark-test&quot;&gt;https://github.com/univalence/spark-tools/tree/master/spark-test&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph b08aa9a8-c5d0-4849-9b1c-0fae39d0f0ad--&gt;
&lt;!-- begin paragraph b564d772-77a8-4bbb-ba8e-c090eec9016d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b564d772-77a8-4bbb-ba8e-c090eec9016d&quot;&gt;We welcome any feedback and of course, we&amp;#39;ll be delighted to hear about your use cases! 😁&lt;/p&gt;
&lt;!-- end paragraph b564d772-77a8-4bbb-ba8e-c090eec9016d--&gt;
&lt;!-- begin paragraph 2eeddc82-4cb6-4d1d-8ea1-d2ddf08e4de3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2eeddc82-4cb6-4d1d-8ea1-d2ddf08e4de3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2eeddc82-4cb6-4d1d-8ea1-d2ddf08e4de3--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:1d751140221f43cea0d91912b007e920</id>
    <title>Les monoïdes ne sont pas une maladie</title>
    <updated>2019-10-07T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/les-monoides-ne-sont-pas-une-maladie.html"/>
    <!--summary -->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Programmation fonctionnelle"></category>    <category term="Monoïde"></category>    <category term="MapReduce"></category>    <category term="Spark"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin paragraph f3909200-526d-4d9d-ac6a-429947d4080a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f3909200-526d-4d9d-ac6a-429947d4080a&quot;&gt;Provenant de lieux lointains dans l&amp;#39;univers (ce qui est pléonastique, puisque quoi que nous regardions dans l&amp;#39;univers, c&amp;#39;est forcément loin) et traversant l&amp;#39;espace à une vitesse inimaginable, des rayonnements viennent frapper les capteurs d&amp;#39;un satellite artificiel, délogeant sans gêne des électrons suivant leur course probabiliste tranquille autour d&amp;#39;atomes. Ce qui ne va pas sans électriser l&amp;#39;environnement. Par cette excitation locale et des mécanismes plus ou moins similaires, nos rayonnements initiaux sont transformés en quelque chose qu&amp;#39;on peut interpréter sous forme de nombres et transmis par radiocommunication vers une planète bleue (quoique pas trop), tellement petite qu&amp;#39;on la distingue à peine depuis Europe, le satellite de glace de Jupiter. Là sur cette planète, une partie certainement infime de nos émissions radios rebondissent sur un dôme inversé, atteignant un espace réduit, pour à nouveau être converties en signaux électriques et grâce à des procédés complexes terminer leur course écrasés contre un écran d&amp;#39;ordinateur sans pouvoir se plaindre. Tout ça pour vérifier si, par additions ou par multiplications successives lancées séquentiellement ou parallèlement, un rayonnement lointain permet de justifier une découverte astronomique... ou pas.&lt;/p&gt;
&lt;!-- end paragraph f3909200-526d-4d9d-ac6a-429947d4080a--&gt;
&lt;!-- begin paragraph 63d57fb0-fdfd-4e2d-ac77-10bf3791149c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-63d57fb0-fdfd-4e2d-ac77-10bf3791149c&quot;&gt;Cette histoire se voit attribuer de nombreux héros permettant de faire avancer la connaissance humaine. Si nous nous permettons quelques personnifications, il y a certes tous les dispositifs matériels et électroniques qui ont un rôle prépondérant. Mais il faut aussi compter sur les outils mathématiques. Et dans ce cadre extraordinaire, les seuls outils dont j&amp;#39;ai fait référence ici sont pourtant parmi ceux les plus utilisés au quotidien par nous simples développeurs sur des applis de gestions, du tracking d&amp;#39;utilisateurs ou des jeux sans fin qui consistent à aligner des friandises pour pouvoir avancer.&lt;/p&gt;
&lt;!-- end paragraph 63d57fb0-fdfd-4e2d-ac77-10bf3791149c--&gt;
&lt;!-- begin heading_1 83736c6b-4483-42c5-9b1b-0f5768c58751--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-83736c6b-4483-42c5-9b1b-0f5768c58751&quot;&gt;Invasion monoïde&lt;/h2&gt;
&lt;!-- end heading_1 83736c6b-4483-42c5-9b1b-0f5768c58751--&gt;
&lt;!-- begin paragraph 3aaf8cd8-5383-41fe-bdb9-7ee8ce8b5219--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3aaf8cd8-5383-41fe-bdb9-7ee8ce8b5219&quot;&gt;Faire une succession d&amp;#39;additions est une opération courante :&lt;/p&gt;
&lt;!-- end paragraph 3aaf8cd8-5383-41fe-bdb9-7ee8ce8b5219--&gt;
&lt;!-- begin code a3645d77-7996-4cfe-97ec-8b47c26ac9e7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a3645d77-7996-4cfe-97ec-8b47c26ac9e7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def masseSalariale(remunerationBrutes: List[Double]): Double =
  remunerationBrutes.foldLeft(0.0) {
    case (sousTotal, remunerationBrute) =&amp;gt; sousTotal + remunerationBrute }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a3645d77-7996-4cfe-97ec-8b47c26ac9e7--&gt;
&lt;!-- begin paragraph e0b234a0-8b25-42e7-a485-5e94d2c75cba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e0b234a0-8b25-42e7-a485-5e94d2c75cba&quot;&gt;Faire une succession de multiplications aussi :&lt;/p&gt;
&lt;!-- end paragraph e0b234a0-8b25-42e7-a485-5e94d2c75cba--&gt;
&lt;!-- begin code 9ca2234d-dd44-4184-8f88-a6b51c1b9fce--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9ca2234d-dd44-4184-8f88-a6b51c1b9fce&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def prixFinal(prixInitial: Double, tauxInflation: List[Double]): Double =
  tauxInflation.foldLeft(prixInitial) {
    case (prixActuel, inflation) =&amp;gt; prixActuel * (1.0 + inflation) }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9ca2234d-dd44-4184-8f88-a6b51c1b9fce--&gt;
&lt;!-- begin paragraph 1ae7ee2f-3af9-4096-9b67-6005d7811f26--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1ae7ee2f-3af9-4096-9b67-6005d7811f26&quot;&gt;Faire autre chose aussi :&lt;/p&gt;
&lt;!-- end paragraph 1ae7ee2f-3af9-4096-9b67-6005d7811f26--&gt;
&lt;!-- begin code e9e1f5a1-83fc-428f-97ae-530b6cf9feff--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e9e1f5a1-83fc-428f-97ae-530b6cf9feff&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val data = Map(
  &amp;quot;Paris&amp;quot; -&amp;gt; 19.0,
  &amp;quot;Marseille&amp;quot; -&amp;gt; 12.0,
  &amp;quot;Paris&amp;quot; -&amp;gt; 8.0,
  &amp;quot;Paris&amp;quot; -&amp;gt; 15.0,
  &amp;quot;Marseille&amp;quot; -&amp;gt; 20.0,
  &amp;quot;Lyon&amp;quot; -&amp;gt; 10.0
)

def priceByCities(data: Map[String, Double]): Map[String, Double] =
  data.foldLeft(Map[String, Double]()) { case (acc, (city, price)) =&amp;gt;
    if (acc.contains(city)) acc.updated(city, acc(city) + price)
    else acc.updated(city, price)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e9e1f5a1-83fc-428f-97ae-530b6cf9feff--&gt;
&lt;!-- begin paragraph 85f929ef-7930-4f90-985a-fa51baf8dceb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-85f929ef-7930-4f90-985a-fa51baf8dceb&quot;&gt;Dans ces exemples, nous avons utilisé à chaque fois, l&amp;#39;opération de &lt;code&gt;foldLeft&lt;/code&gt; qui permet d&amp;#39;agréger &lt;b&gt;des valeurs&lt;/b&gt; en fonction d&amp;#39;une &lt;b&gt;valeur initiale&lt;/b&gt; et d&amp;#39;une &lt;b&gt;opération binaire&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 85f929ef-7930-4f90-985a-fa51baf8dceb--&gt;
&lt;!-- begin paragraph 6d458b3b-a40c-491e-95dc-95d4204daad3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d458b3b-a40c-491e-95dc-95d4204daad3&quot;&gt;Nous allons justement nous concentrer sur, à la fois :&lt;/p&gt;
&lt;!-- end paragraph 6d458b3b-a40c-491e-95dc-95d4204daad3--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;l&amp;#39;ensemble des valeurs acceptées,&lt;/li&gt;&lt;li&gt;l&amp;#39;opération binaire,&lt;/li&gt;&lt;li&gt;la valeur initiale.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph df1103ea-3212-422a-80db-937667744e67--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-df1103ea-3212-422a-80db-937667744e67&quot;&gt;Ces trois éléments forment un tout indissociable. Ce tout est ce que les mathématiciens appellent un &lt;b&gt;monoïde&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph df1103ea-3212-422a-80db-937667744e67--&gt;
&lt;!-- begin paragraph 2897993e-e207-43dc-91c8-77d17b4d2ef8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2897993e-e207-43dc-91c8-77d17b4d2ef8&quot;&gt;Sur le plan physique, les monoïds sont de la même taille que les humains, mais leur peau est sombre et composée d&amp;#39;écailles. Il n&amp;#39;ont qu&amp;#39;un seul œil sur la tête, au niveau de la bouche. Au XXVIIe siècle, ils savent toujours parler. Mais depuis le 57e Segment du Temps, ils n&amp;#39;ont pas de bouche visible et s&amp;#39;en tiennent à un langage de signes pour communiquer... &lt;a href=&quot;https://tardis.fandom.com/wiki/Monoid&quot;&gt;Dr Who&lt;/a&gt;... Désolé.&lt;/p&gt;
&lt;!-- end paragraph 2897993e-e207-43dc-91c8-77d17b4d2ef8--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;https://vignette.wikia.nocookie.net/tardis/images/8/86/One.jpg/revision/latest/scale-to-width-down/700?cb=20110223223210&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Dans Dr Who, les monoïds sont des êtres qui peuvent être sympas avec les humains. Mais ça n&amp;#39;a pas toujours été le cas.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 78a7b44a-a21e-48ff-82d4-889f87cfa6c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78a7b44a-a21e-48ff-82d4-889f87cfa6c3&quot;&gt;Sinon, un monoïde est une structure algébrique correspondant à un ensemble (les valeurs) avec un élément neutre (la valeur initiale) et une loi de composition interne associative (l&amp;#39;opération binaire). C&amp;#39;est souvent noté (&lt;i&gt;E&lt;/i&gt;, ✻, &lt;i&gt;e&lt;/i&gt;), avec &lt;i&gt;E&lt;/i&gt; l&amp;#39;ensemble de valeurs, ✻ la loi de composition interne et &lt;i&gt;e&lt;/i&gt; l&amp;#39;élément neutre.&lt;/p&gt;
&lt;!-- end paragraph 78a7b44a-a21e-48ff-82d4-889f87cfa6c3--&gt;
&lt;!-- begin paragraph 9426df4f-c8c2-45e2-8d4c-be7fb2db3424--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9426df4f-c8c2-45e2-8d4c-be7fb2db3424&quot;&gt;Alors, on dit &lt;i&gt;loi de composition&lt;/i&gt;, car l&amp;#39;idée de notre opération est de combiner deux éléments de notre ensemble de valeurs (ie. de les composer, d&amp;#39;ailleurs on devrait pouvoir parler d&amp;#39;agréger des valeurs). On dit &lt;i&gt;interne&lt;/i&gt; car en combinant ces deux valeurs, notre opération retourne une nouvelle valeur qui fait partie de notre ensemble initial de valeurs (ie. on ne sort pas de cet ensemble).&lt;/p&gt;
&lt;!-- end paragraph 9426df4f-c8c2-45e2-8d4c-be7fb2db3424--&gt;
&lt;!-- begin paragraph 6789ea50-764c-4052-b2b4-9629478a3a9a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6789ea50-764c-4052-b2b4-9629478a3a9a&quot;&gt;Pour &lt;i&gt;associative&lt;/i&gt;, hé bien... Si nous utilisons plusieurs fois notre opération dans une expression, il est alors possible d&amp;#39;évaluer l&amp;#39;expression quelque soit l&amp;#39;endroit où on commence à l&amp;#39;évaluer. C&amp;#39;est-à-dire qu&amp;#39;avec l&amp;#39;expression &lt;code&gt;a + b + c&lt;/code&gt;, je peux très bien commencer par &lt;code&gt;x = a + b&lt;/code&gt; et ensuite faire &lt;code&gt;x + c&lt;/code&gt; ou commencer par &lt;code&gt;x = b + c&lt;/code&gt; et faire &lt;code&gt;a + x&lt;/code&gt; après. Au final, on note ça &lt;code&gt;(a + b) + c = a + (b + c)&lt;/code&gt;. L&amp;#39;ordre d&amp;#39;évaluation des sous-expressions n&amp;#39;a pas d&amp;#39;importance pour un monoïde.&lt;/p&gt;
&lt;!-- end paragraph 6789ea50-764c-4052-b2b4-9629478a3a9a--&gt;
&lt;!-- begin paragraph c5317e5e-5b7f-4f13-8f58-eebf42b30484--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c5317e5e-5b7f-4f13-8f58-eebf42b30484&quot;&gt;C&amp;#39;est une propriété intéressante, car ça permet de découper une expression en sous-expression et d&amp;#39;évaluer ces sous-expressions en parallèle, puis de récupérer et combiner les résultats de ces évaluations pour obtenir le résultat final... Et comme ça, on vient de réinventer MapReduce !&lt;/p&gt;
&lt;!-- end paragraph c5317e5e-5b7f-4f13-8f58-eebf42b30484--&gt;
&lt;!-- begin paragraph 452b2c16-f337-4382-806b-7993a1b9be7b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-452b2c16-f337-4382-806b-7993a1b9be7b&quot;&gt;Voyons ce que ça donne...&lt;/p&gt;
&lt;!-- end paragraph 452b2c16-f337-4382-806b-7993a1b9be7b--&gt;
&lt;!-- begin heading_1 f6bc15ca-1c5f-4d1b-ac6e-76a3cc4c83e6--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f6bc15ca-1c5f-4d1b-ac6e-76a3cc4c83e6&quot;&gt;Tes monoïdes font du MapReduce en ski !&lt;/h2&gt;
&lt;!-- end heading_1 f6bc15ca-1c5f-4d1b-ac6e-76a3cc4c83e6--&gt;
&lt;!-- begin paragraph d3eea6ea-66b5-4978-a579-8d3d7e8bb2ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3eea6ea-66b5-4978-a579-8d3d7e8bb2ac&quot;&gt;Voici une liste de stations de ski avec notamment leur localisation et une évaluation sur 5.&lt;/p&gt;
&lt;!-- end paragraph d3eea6ea-66b5-4978-a579-8d3d7e8bb2ac--&gt;
&lt;!-- begin paragraph 532880f2-ff26-47d0-a320-2ff45df3170a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-532880f2-ff26-47d0-a320-2ff45df3170a&quot;&gt;Nous avons ci-dessous un extrait d&amp;#39;une liste des stations de ski en France. Bon... Ce n&amp;#39;est pas du big data, hein ! Mais on va faire comme si 😬.&lt;/p&gt;
&lt;!-- end paragraph 532880f2-ff26-47d0-a320-2ff45df3170a--&gt;
&lt;!-- begin code 80100a06-f25f-4ad4-aee8-fbe65ec84d3b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-80100a06-f25f-4ad4-aee8-fbe65ec84d3b&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;station_name,station_rating,city,postal_code,county,departement,region,latitude,longitudeLus la Jarjatte,5.0,1,Lus-la-Croix-Haute,26620,Die,Drôme,Auvergne-Rhône-Alpes,44.679971,5.7661523
Panticosa,5.0,,,,,,,
Formigal,4.5,,,,,,,
Cerler,4.5,,,,,,,
Baqueira / Beret,4.5,,,,,,,
Val d&amp;#39;Isère,4.4,Val-d&amp;#39;Isère,73150,Albertville,Savoie,Auvergne-Rhône-Alpes,45.4498666,6.9804421
La Grave,4.4,La Grave,05320,Briançon,Hautes-Alpes,Provence-Alpes-Côte d&amp;#39;Azur,45.0455379,6.3068184
La Sambuy,4.4,,,,,,,
Le granier,4.4,Aime-la-Plagne,73210,Albertville,Savoie,Auvergne-Rhône-Alpes,45.60089445,6.62550908591405
Mont-Saxonnex,4.4,Mont-Saxonnex,74130,Bonneville,Haute-Savoie,Auvergne-Rhône-Alpes,46.0508713,6.4792764
Les Karellis,4.3,Montricher-Albanne,73870,Saint-Jean-de-Maurienne,Savoie,Auvergne-Rhône-Alpes,45.215851,6.399584218507
Peisey-Vallandry,4.3,,,,,,,
Le Mourtis,4.3,,,,,,,
Lans en Vercors,4.3,Lans-en-Vercors,38250,Grenoble,Isère,Auvergne-Rhône-Alpes,45.1277768,5.5891386
La Giettaz en Aravis,4.3,La Giettaz,73590,Albertville,Savoie,Auvergne-Rhône-Alpes,45.8827863,6.5334974
Sainte-Anne,4.3,Sainte-Anne,97180,Pointe-à-Pitre,Pointe-à-Pitre,Guadeloupe,16.225685,-61.3859128
Pic du Midi de Bigorre,4.3,Bagnères-de-Bigorre,,Bagnères-de-Bigorre,Hautes-Pyrénées,Occitanie,42.9367764,0.1416107&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 80100a06-f25f-4ad4-aee8-fbe65ec84d3b--&gt;
&lt;!-- begin paragraph 0a278815-616e-4af6-8220-7138415dcbdd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0a278815-616e-4af6-8220-7138415dcbdd&quot;&gt;Cette liste n&amp;#39;est pas complète en terme d&amp;#39;information. Malgré ça, nous allons donner la moyenne des évaluations... tel que le ferait MapReduce ou Spark.&lt;/p&gt;
&lt;!-- end paragraph 0a278815-616e-4af6-8220-7138415dcbdd--&gt;
&lt;!-- begin paragraph 2c681973-d3d3-4995-89a6-58d020e85853--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c681973-d3d3-4995-89a6-58d020e85853&quot;&gt;Dans MapReduce, il y a une étape de préparation des données où on part d&amp;#39;un fichier pour le convertir en un ensemble clé/valeur. Notez que l&amp;#39;évaluation est en position 1 dans chaque ligne (en commençant par 0) et que le département est en position 6.&lt;/p&gt;
&lt;!-- end paragraph 2c681973-d3d3-4995-89a6-58d020e85853--&gt;
&lt;!-- begin code 38682a4d-c9cf-4c45-ab0f-ee5e24b979c6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-38682a4d-c9cf-4c45-ab0f-ee5e24b979c6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import scala.collection.MapView
import scala.util.Try

def getRatingsByDepartement(lines: Iterable[String]): MapView[String, Iterable[Double]] = {
  // drop CSV header
  val data: Iterable[String] = lines.drop(1)

  val rows: Iterable[Array[String]] =
    data.map { line =&amp;gt;
      // cleaning line and separate fields
      val row: Array[String] = line.trim.split(&amp;quot;,&amp;quot;)

      // cleansing: if fields are missing, we pad row with empty strings
      row.padTo(7, &amp;quot;&amp;quot;)
    }
  
  val deptRatings: Iterable[(String, Double)] =
    // we remove lines with no departement
    rows.filterNot(_(6).isEmpty)
      .map(fields =&amp;gt;
        (fields(6), Try { fields(1).toDouble }.getOrElse(0.0))
      )

  deptRatings
    .groupBy { case (departement, rating) =&amp;gt; departement }
    .view.mapValues(row =&amp;gt; row.map { case (departement, rating) =&amp;gt; rating })
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 38682a4d-c9cf-4c45-ab0f-ee5e24b979c6--&gt;
&lt;!-- begin paragraph 04b1e796-ea83-4fbc-9254-a5d360d59d5d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-04b1e796-ea83-4fbc-9254-a5d360d59d5d&quot;&gt;La fonction &lt;code&gt;getRatingsByDepartement&lt;/code&gt; exécute en fait un &lt;i&gt;shuffle&lt;/i&gt; (ie. une redistribution des données) en réalisant un partitionnement utilisant le département comme clé (ce qui n&amp;#39;est pas la meilleure des clés, dans la mesure où la répartition des données dans les différentes partitions sera ici déséquilibrée, puisque par exemple dans les Vosges il n&amp;#39;y a pas beaucoup de stations contrairement à la Haute-Savoie... Mais, bon. Ce n&amp;#39;est comme si on pouvait faire du big data avec l&amp;#39;énumération des stations de ski en France). Le résultat de &lt;code&gt;getRatingsByDepartement&lt;/code&gt; est de type &lt;code&gt;MapView[String, Iterable[Double]]&lt;/code&gt; — MapView est une &amp;quot;vue&amp;quot; sur une collection de type Map. Ici, à chaque clé correspond une partition des évaluations. Dans le cadre de MapReduce, chaque partition serait déposée dans des nœuds différents du cluster.&lt;/p&gt;
&lt;!-- end paragraph 04b1e796-ea83-4fbc-9254-a5d360d59d5d--&gt;
&lt;!-- begin paragraph 2010adae-28e4-4f55-9287-af4702f4ac59--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2010adae-28e4-4f55-9287-af4702f4ac59&quot;&gt;Il va maintenant falloir calculer la moyenne. En supposant, qu&amp;#39;on ait à faire à une liste immense, il est plus intéressant de parcourir cette liste en une seule passe qu&amp;#39;en deux. Car pour calculer une moyenne, il faut d&amp;#39;un côté une somme de valeurs et de l&amp;#39;autre leur quantité, avant de diviser ces deux résultats. Ce qui normalement implique deux passes sur notre dataset. Pour le faire en une seule passe, nous allons calculer la somme et la quantité en même temps, en stockant les résultats intermédiaires dans un couple de valeurs &lt;i&gt;(somme, quantité)&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 2010adae-28e4-4f55-9287-af4702f4ac59--&gt;
&lt;!-- begin paragraph e75d6f01-1e38-4a7b-a097-339bf1b2fc2f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e75d6f01-1e38-4a7b-a097-339bf1b2fc2f&quot;&gt;Alors, il existe différentes approches pour implémenter ce calcul de moyenne. Pour l&amp;#39;exercice ici, nous allons étudier une solution mettant en avant la notion de monoïde, en se basant sur une &lt;a href=&quot;https://blog.univalence.io/pourquoi-les-typeclasses-cest-top/&quot;&gt;typeclasse&lt;/a&gt; (un peu à la manière de la bibliothèque &lt;a href=&quot;https://typelevel.org/cats/typeclasses/monoid.html&quot;&gt;Scala Cats&lt;/a&gt;).&lt;/p&gt;
&lt;!-- end paragraph e75d6f01-1e38-4a7b-a097-339bf1b2fc2f--&gt;
&lt;!-- begin paragraph 61b8f83f-c52c-48f9-815f-aecf7e5215c8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-61b8f83f-c52c-48f9-815f-aecf7e5215c8&quot;&gt;En Scala, pour déclarer une typeclasse Monoid, il faut déclarer un trait générique, où le paramètre A représente le type qui sera qualifié de monoïde. Ce trait contient deux méthodes &lt;code&gt;empty&lt;/code&gt; qui retourne l&amp;#39;élément neutre et &lt;code&gt;combine&lt;/code&gt; qui permet de combiner deux éléments de A.&lt;/p&gt;
&lt;!-- end paragraph 61b8f83f-c52c-48f9-815f-aecf7e5215c8--&gt;
&lt;!-- begin code f4e94f73-8fa4-4f8e-9499-627c865c25da--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f4e94f73-8fa4-4f8e-9499-627c865c25da&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Monoid[A] {
  def empty: A
  def combine(a: A, b: A): A
}

object Monoid {
  @inline def apply[A](implicit ev: Monoid[A]): Monoid[A] = ev
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f4e94f73-8fa4-4f8e-9499-627c865c25da--&gt;
&lt;!-- begin paragraph 6998aa6d-dbe3-482c-98a6-bb3c053b5c85--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6998aa6d-dbe3-482c-98a6-bb3c053b5c85&quot;&gt;La méthode &lt;code&gt;apply&lt;/code&gt; de l&amp;#39;object Monoid ci-dessus apporte une facilité d&amp;#39;utilisation du trait, qui permet par exemple d&amp;#39;écrire &lt;code&gt;Monoid[Int].empty&lt;/code&gt; à la place de &lt;code&gt;implicitly[Monoid[Int]].empty&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6998aa6d-dbe3-482c-98a6-bb3c053b5c85--&gt;
&lt;!-- begin paragraph 33f15005-c00a-47e7-863b-69af13070985--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-33f15005-c00a-47e7-863b-69af13070985&quot;&gt;Déclarons quelques instances de notre typeclasse Monoid, qui nous servirons pour résoudre notre problème.&lt;/p&gt;
&lt;!-- end paragraph 33f15005-c00a-47e7-863b-69af13070985--&gt;
&lt;!-- begin code bf4f190a-ded9-4937-a0a6-64ee119dc044--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bf4f190a-ded9-4937-a0a6-64ee119dc044&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// Monoid (Int, +, 0)
implicit val intMonoid: Monoid[Int] = new Monoid[Int] {
  override def empty: Int = 0
  override def combine(a: Int, b: Int): Int = a + b
}

// Monoid (Double, +, 0.0)
implicit val doubleMonoid: Monoid[Double] = new Monoid[Double] {
  override def empty: Double = 0.0
  override def combine(a: Double, b: Double): Double = a + b
}

// turn any tuple (A, B) into Monoid, providing A and B both are Monoid
implicit def tupleMonoid[A: Monoid, B: Monoid]: Monoid[(A, B)] =
  new Monoid[(A, B)] {
    override def empty: (A, B) = (Monoid[A].empty, Monoid[B].empty)

    override def combine(left: (A, B), right: (A, B)): (A, B) =
      (Monoid[A].combine(left._1, right._1),
       Monoid[B].combine(left._2, right._2))
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bf4f190a-ded9-4937-a0a6-64ee119dc044--&gt;
&lt;!-- begin paragraph 0d5b9e2e-1db3-4a95-8e6c-d7c2fbceb449--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0d5b9e2e-1db3-4a95-8e6c-d7c2fbceb449&quot;&gt;Rajoutons à certaines collections une opération &lt;code&gt;combineAll&lt;/code&gt; qui, dans la mesure où les valeurs contenues font parties d&amp;#39;un monoïde, combine toutes les valeurs contenues pour obtenir un unique résultat.&lt;/p&gt;
&lt;!-- end paragraph 0d5b9e2e-1db3-4a95-8e6c-d7c2fbceb449--&gt;
&lt;!-- begin code 7147bb03-6801-4faf-ac67-f3216bbc3233--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7147bb03-6801-4faf-ac67-f3216bbc3233&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit class iterableWithCombineAll[A: Monoid](l: Iterable[A]) {
  def combineAll: A = l.fold(Monoid[A].empty)(Monoid[A].combine)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7147bb03-6801-4faf-ac67-f3216bbc3233--&gt;
&lt;!-- begin paragraph cca337f5-acdb-4f57-858c-57134e4ef2af--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cca337f5-acdb-4f57-858c-57134e4ef2af&quot;&gt;Utilisons notre fonction &lt;code&gt;getRatingsByDepartement&lt;/code&gt; avec un fichier pour obtenir un partitionnement des données.&lt;/p&gt;
&lt;!-- end paragraph cca337f5-acdb-4f57-858c-57134e4ef2af--&gt;
&lt;!-- begin code 48d67ae7-6e3f-4f91-91b4-b299d5a8b411--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-48d67ae7-6e3f-4f91-91b4-b299d5a8b411&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import scala.io.Source

val file = Source.fromFile(&amp;quot;/data/geo/stations.csv&amp;quot;)
val partitions = getRatingsByDepartement(file.getLines().to(Iterable))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 48d67ae7-6e3f-4f91-91b4-b299d5a8b411--&gt;
&lt;!-- begin paragraph 044a2efa-624c-485b-9650-2f9dffdb11be--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-044a2efa-624c-485b-9650-2f9dffdb11be&quot;&gt;Et maintenant, voici notre code MapReduce :&lt;/p&gt;
&lt;!-- end paragraph 044a2efa-624c-485b-9650-2f9dffdb11be--&gt;
&lt;!-- begin code 499395b5-11e2-48d6-b61d-2d109a8e7c73--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-499395b5-11e2-48d6-b61d-2d109a8e7c73&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// phase 1 (Map): get ratings only and associate the value 1 to then
val partitionedRatingWithOne =
  partitions.mapValues(ratings =&amp;gt; ratings.map(rating =&amp;gt; (rating, 1)))

// phase 2 (Combine): locally sum ratings and 1s for each partition
val partitionedSumRatingsAndCount =
  partitionedRatingWithOne.mapValues(data =&amp;gt; data.combineAll)

// phase 3 (Reduce): combine for all partitions the sum of ratings and counts
val (rating, count) =
    partitionedSumRatingsAndCount.values.combineAll

println(rating / count)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 499395b5-11e2-48d6-b61d-2d109a8e7c73--&gt;
&lt;!-- begin paragraph b04b653f-9df1-41c2-925b-43d1bfed176f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b04b653f-9df1-41c2-925b-43d1bfed176f&quot;&gt;Ce qui donne environ 3.45 sur 5 sur notre dataset.&lt;/p&gt;
&lt;!-- end paragraph b04b653f-9df1-41c2-925b-43d1bfed176f--&gt;
&lt;!-- begin paragraph b18b3d75-3f8b-409c-b306-284b5cc0a1b6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b18b3d75-3f8b-409c-b306-284b5cc0a1b6&quot;&gt;Et c&amp;#39;est ainsi que MapReduce fonctionne, si on inclut la phase &lt;i&gt;Combine&lt;/i&gt; : 1/ &lt;i&gt;Map&lt;/i&gt; : on transforme chaque élément dans une forme qui facilite les phases suivantes, 2/ &lt;i&gt;Combine&lt;/i&gt; : on effectue un &lt;i&gt;Reduce&lt;/i&gt; local pour réduire la quantité de données à transférer entre les nœuds du cluster à la phase suivante (c&amp;#39;est une optimisation), 3/ &lt;i&gt;Reduce&lt;/i&gt; : on rassemble les résultats intermédiaires pour calculer et retourner le résultat final.&lt;/p&gt;
&lt;!-- end paragraph b18b3d75-3f8b-409c-b306-284b5cc0a1b6--&gt;
&lt;!-- begin paragraph 3e092359-364a-4fea-941d-b046a4beca7c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3e092359-364a-4fea-941d-b046a4beca7c&quot;&gt;L&amp;#39;optimisation par la phase &lt;i&gt;Combine&lt;/i&gt; est rendu possible par le fait que (Int, +, 0) est un monoïde, que (Double, +, 0.0) est un monoïde et qu&amp;#39;à partir de ((Double, Int), (+, +), (0.0, 0)) on a aussi un monoïde. Et comme l&amp;#39;opération &lt;code&gt;combine&lt;/code&gt; sur un monoïde est associative, nous savons alors qu&amp;#39;il est possible de diviser notre calcul de la phase &lt;i&gt;Reduce&lt;/i&gt; en plein de calculs intermédiaires de la manière qui arrange le plus.&lt;/p&gt;
&lt;!-- end paragraph 3e092359-364a-4fea-941d-b046a4beca7c--&gt;
&lt;!-- begin paragraph 618bf7de-c64d-4b53-a2b2-2340a9b360ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-618bf7de-c64d-4b53-a2b2-2340a9b360ac&quot;&gt;Bien entendu, le code ci-dessus est très simple, alors que ce qui se passe dans une véritable implémentation de MapReduce est bien plus complexe. Mais pour arriver à un véritable MapReduce, ici il faudrait changer l&amp;#39;implémentation du type Iterable et s&amp;#39;intéresser aux méthodes drop, map, filter, groupBy, mapValues et fold. Il faudrait aussi changer la façon de lire un fichier surtout s&amp;#39;il est réparti sur un cluster. Tout ceci est disponible avec les RDD de Spark. Et avec les RDD de Spark, on aurait d&amp;#39;ailleurs une version bien plus simple.&lt;/p&gt;
&lt;!-- end paragraph 618bf7de-c64d-4b53-a2b2-2340a9b360ac--&gt;
&lt;!-- begin code 221247ee-5489-4f29-be51-11c2146582ec--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-221247ee-5489-4f29-be51-11c2146582ec&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// read and partition a file
val data: RDD[(String, Double)] =
  getRatingsByDepartement(sparkContext.textFile(&amp;quot;/data/geo/stations.csv&amp;quot;))

// phase 1 (Map): get ratings only and associate the value 1 to then
val ratingWithOne: RDD[(Double, Int)] =
  data.map { case (departement, rating) =&amp;gt; (rating, 1) }

// phase 2 (Reduce): combine for all partitions the sum of ratings and 1s
val (rating, count) =
  ratingWithOne.combineAll

println(rating / count)

// ----
// add combineAll to RDD
implicit class rddWithCombineAll[A: Monoid](l: RDD[A]) {
  def combineAll: A = l.fold(Monoid[A].empty)(Monoid[A].combine)
}

// ----
// data preparation
def getRatingsByDepartement(lines: RDD[String]): RDD[(String, Double)] = {
  val header: String = lines.first()
  val data: RDD[String] = lines.filter(row =&amp;gt; row != header)
  val rows: RDD[Array[String]] =
    data.map { line =&amp;gt;
      // cleaning line and separate fields
      val row: Array[String] = line.trim.split(&amp;quot;,&amp;quot;)
      // cleansing: if fields are missing, we pad row with empty strings
      row.padTo(7, &amp;quot;&amp;quot;)
    }
    // we remove lines with no departement
      .filter(row =&amp;gt; !row(6).isEmpty)

  rows.map(fields =&amp;gt; (
    fields(6), // departement
     Try { fields(1).toDouble }.getOrElse(0.0)) // rating
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 221247ee-5489-4f29-be51-11c2146582ec--&gt;
&lt;!-- begin heading_1 2a2a6fee-af6f-4766-b605-1911f9e61d8a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2a2a6fee-af6f-4766-b605-1911f9e61d8a&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 2a2a6fee-af6f-4766-b605-1911f9e61d8a--&gt;
&lt;!-- begin paragraph 257174df-aa62-41c2-a1bd-160865e8f062--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-257174df-aa62-41c2-a1bd-160865e8f062&quot;&gt;Notre histoire de rayonnements cosmiques transformés en nombres écrasés sur un écran fait apparaître une forme de vie mathématique à part entière : les monoïdes. Ils sont partout et leur prolifération est incontrôlable. Car nous les retrouvons partout : dans les calculs scientifiques, dans les clusters, dans la gestion des règles métiers et même dans les additions de nos enfants. Et le plus saisissant est que nous les utilisons parfois sans nous en rendre compte.&lt;/p&gt;
&lt;!-- end paragraph 257174df-aa62-41c2-a1bd-160865e8f062--&gt;
&lt;!-- begin paragraph ec4acbe7-58f0-431e-84a6-f58ce1c1dc0a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec4acbe7-58f0-431e-84a6-f58ce1c1dc0a&quot;&gt;Bien ou mal ? C&amp;#39;est à vous de décider. Mais une chose est sûr, c&amp;#39;est que les monoïdes apportent une aide indéniable.&lt;/p&gt;
&lt;!-- end paragraph ec4acbe7-58f0-431e-84a6-f58ce1c1dc0a--&gt;
&lt;!-- begin paragraph 87e8baa9-e5a1-4ab9-b034-490129f516a8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-87e8baa9-e5a1-4ab9-b034-490129f516a8&quot;&gt;Le code et les données : &lt;a href=&quot;https://github.com/univalence/blog-code/tree/master/monoid&quot;&gt;https://github.com/univalence/blog-code/tree/master/monoid&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 87e8baa9-e5a1-4ab9-b034-490129f516a8--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:de31a1df21504fd799a1a5988f99246d</id>
    <title>La data quality, notre amie pour la vie</title>
    <updated>2019-09-09T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/la-data-quality-notre-amie-pour-la-vie.html"/>
    <!--summary La data quality est peu utilisée, et à tort! Aujourd&#039;hui la data est devenue plus importante que le pétrole alors dites non à une donnée sale !-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Data Quality"></category>    <category term="Scala"></category>    <category term="Spark"></category>    <category term="Introduction"></category>    <content type="html">
&lt;!-- begin heading_3 7ae852c8-9472-4dcc-a3fe-8dc502ef2055--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-7ae852c8-9472-4dcc-a3fe-8dc502ef2055&quot;&gt;Structure&lt;/h4&gt;
&lt;!-- end heading_3 7ae852c8-9472-4dcc-a3fe-8dc502ef2055--&gt;
&lt;!-- begin paragraph 5fcc1635-9a4b-41cb-a135-e9ed9063f3c6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5fcc1635-9a4b-41cb-a135-e9ed9063f3c6&quot;&gt;problem → solution → problem of this solution → solution of the problem of this solution&lt;/p&gt;
&lt;!-- end paragraph 5fcc1635-9a4b-41cb-a135-e9ed9063f3c6--&gt;
&lt;!-- begin paragraph 1a608fb6-158c-42ae-a389-cbafd9df0f87--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1a608fb6-158c-42ae-a389-cbafd9df0f87&quot;&gt;(best struture ever)&lt;/p&gt;
&lt;!-- end paragraph 1a608fb6-158c-42ae-a389-cbafd9df0f87--&gt;
&lt;!-- begin divider 0aec6f4f-f830-4581-b4ac-443c9cf91a5e--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-0aec6f4f-f830-4581-b4ac-443c9cf91a5e&quot;&gt;
&lt;!-- end divider 0aec6f4f-f830-4581-b4ac-443c9cf91a5e--&gt;
&lt;!-- begin paragraph 722cfec0-b48b-407c-9455-ee3ec70ea448--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-722cfec0-b48b-407c-9455-ee3ec70ea448&quot;&gt;La &lt;i&gt;data quality&lt;/i&gt; est un sujet qui ne fait pas beaucoup de bruit et à tort ! Si vous demandez à un data scientist ce qui lui prend le plus de temps, vous aimeriez qu&amp;#39;il vous dise que la majorité de son temps est consacrée à comparer des modèles de machine learning pour trouver le plus approprié ou qu&amp;#39;il est en train d&amp;#39;effectuer une tout autre analyse permettant, à l&amp;#39;aide de la data, de comprendre le problème donné. La vérité est tout autre, et ce, très souvent dû à une mauvaise qualité de données. &lt;/p&gt;
&lt;!-- end paragraph 722cfec0-b48b-407c-9455-ee3ec70ea448--&gt;
&lt;!-- begin paragraph cc32758d-7b64-4341-9f45-70a0c5477511--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cc32758d-7b64-4341-9f45-70a0c5477511&quot;&gt;Dans la réalité qui est la nôtre, ces mêmes personnes vous diront qu&amp;#39;une large partie de leur temps est consacrée au data cleaning et à la data préparation. Selon Amelia Arbisser dans un &lt;a href=&quot;https://www.youtube.com/watch?v=A2XQhrvGsyw&quot;&gt;talk au Spark Summit de 2016,&lt;/a&gt; ces deux phases occuperaient plus de 80% du temps d&amp;#39;un ingénieur travaillant avec des données.&lt;/p&gt;
&lt;!-- end paragraph cc32758d-7b64-4341-9f45-70a0c5477511--&gt;
&lt;!-- begin heading_2 6911d502-7328-4d72-a6cd-5eea1e1fb9f1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6911d502-7328-4d72-a6cd-5eea1e1fb9f1&quot;&gt;L&amp;#39;ennemi public numéro 1 du data scientist&lt;/h3&gt;
&lt;!-- end heading_2 6911d502-7328-4d72-a6cd-5eea1e1fb9f1--&gt;
&lt;!-- begin paragraph 728b2a46-871e-4fa8-b74b-852a381b86e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-728b2a46-871e-4fa8-b74b-852a381b86e0&quot;&gt;L&amp;#39;un des nombreux problèmes que l&amp;#39;on rencontre aujourd&amp;#39;hui, c&amp;#39;est qu&amp;#39;il y a trop de données inconsistantes et sales : du bruit dans le signal. Cela oblige les ingénieurs à consacrer une très (trop) grande partie de leur temps à la recherche de solutions contournant le problème ou à nettoyer la donnée. Dans certains cas, la donnée est complètement inutilisable et il suffit de se rendre sur un site comme Kaggle pour y trouver des dizaines de datasets incomplets, mais qui semblent pourtant très intéressants au premier abord.&lt;/p&gt;
&lt;!-- end paragraph 728b2a46-871e-4fa8-b74b-852a381b86e0--&gt;
&lt;!-- begin paragraph 074af3a9-fb3c-4da5-9471-e2de9ea4ed42--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-074af3a9-fb3c-4da5-9471-e2de9ea4ed42&quot;&gt;Rien ne vaut un exemple pour appuyer mes propos. Voici un dataset provenant de Kaggle &lt;a href=&quot;https://www.kaggle.com/datafiniti/womens-shoes-prices&quot;&gt;https://www.kaggle.com/datafiniti/womens-shoes-prices&lt;/a&gt;. Ce dataset peut être une véritable mine d&amp;#39;or d&amp;#39;information. Imaginons que cette data provienne d&amp;#39;un magasin de chaussures. Cette dernière pourrait servir à savoir la couleur à la mode actuellement, ou même le type de chaussure, ou même la forme et ainsi engranger de meilleures ventes. Cette information est très intéressante pour le commerçant. Alors on décide de s’adonner à la tâche et là... Le drame... 41% des articles n&amp;#39;ont pas la couleur renseignée. Cela fait plus de 13000 articles sur les 33800, que contient le dataset inutilisables. Cela n&amp;#39;est pas une fin en soi, mais on perd beaucoup d&amp;#39;informations. Alors on s&amp;#39;applique, on remarque un champ &amp;quot;feature&amp;quot; avec un JSON à l’intérieur et on essaye de l&amp;#39;exploiter pour y obtenir la couleur. Et mince ! Ici encore, il manque 20% de la donnée, la couleur n&amp;#39;est pas toujours présente on se retrouve avec un code immense, et ce, pour combler un manque de data quality qui peut être évité.&lt;/p&gt;
&lt;!-- end paragraph 074af3a9-fb3c-4da5-9471-e2de9ea4ed42--&gt;
&lt;!-- begin heading_2 3ef8f46f-0ee5-45f1-8a4d-6dc33855ad98--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3ef8f46f-0ee5-45f1-8a4d-6dc33855ad98&quot;&gt;La data quality à travers le Constraint Checking&lt;/h3&gt;
&lt;!-- end heading_2 3ef8f46f-0ee5-45f1-8a4d-6dc33855ad98--&gt;
&lt;!-- begin paragraph 15e4fa10-67ca-412a-9f88-ac93f006849e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-15e4fa10-67ca-412a-9f88-ac93f006849e&quot;&gt;L’exemple précédent est très basique, le but étant qu&amp;#39;il parle au plus grand nombre. Généralement, la data est absorbée par d&amp;#39;autres systèmes accompagnés d&amp;#39;hypothèses implicites. Par exemple, une telle application va supposer qu&amp;#39;une des colonnes d&amp;#39;un quelconque dataset est d&amp;#39;un type X et qu&amp;#39;elle ne contient pas de valeur nulle. Si ces conditions ne sont pas respectées, l&amp;#39;application risque fortement de planter ou de proposer des résultats erronés. Ce fût le cas pour la NASA avec ses capteurs de radiation installés sur la station spatiale internationale (ISS). Ces capteurs devaient renvoyer 0 lorsqu&amp;#39;il n&amp;#39;y avait pas de radiations. Mais en la réalité, ils renvoyaient des nombres négatifs. Si ces nombres sont directement incorporés dans un réseau de neurones, cela peut provoquer des erreurs dans les prédictions pouvant mener à des conséquences plus ou moins catastrophiques. Heureusement pour nous, &lt;a href=&quot;https://www.bbc.com/news/uk-39351833&quot;&gt;Miles Soloman&lt;/a&gt;, un étudiant de 17 ans a mis en lumière ce problème de fonctionnement et la NASA a ainsi pu corriger le tir.&lt;/p&gt;
&lt;!-- end paragraph 15e4fa10-67ca-412a-9f88-ac93f006849e--&gt;
&lt;!-- begin paragraph 023e6f3e-c94d-47e4-bbd3-807988f863bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-023e6f3e-c94d-47e4-bbd3-807988f863bc&quot;&gt;Pour éviter ce genre de problème, il faut l&amp;#39;attaquer à la racine avant que la donnée nourrisse les algorithmes de machine learning et autres systèmes. Heureusement pour nous, il existe des bibliothèques et autres outils appliquant la data quality, permettant de rajouter un point de contrôle avant de nourrir ces futurs systèmes. &lt;a href=&quot;https://github.com/awslabs/deequ&quot;&gt;Deequ&lt;/a&gt; est l&amp;#39;une d&amp;#39;entre elles et propose beaucoup de fonctionnalités en rapport avec la data quality. Car oui, il n&amp;#39;y a pas qu&amp;#39;une seule façon de faire de la data quality, mais des dizaines et des dizaines de pratiques toutes essayant de répondre à des besoins spécifiques selon le domaine métier envisagé.&lt;/p&gt;
&lt;!-- end paragraph 023e6f3e-c94d-47e4-bbd3-807988f863bc--&gt;
&lt;!-- begin paragraph 087adfa3-8c8b-42c2-9e26-71c900e540d0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-087adfa3-8c8b-42c2-9e26-71c900e540d0&quot;&gt;Parmi elles se trouvent le &lt;code&gt;constraint checking&lt;/code&gt;. Cette pratique consiste à appliquer des matrices aux différentes colonnes pour vérifier qu&amp;#39;elles respectent certaines contraintes. Prenons un dataframe contenant des informations utilisateur et utilisons le &lt;code&gt;constraint checking&lt;/code&gt; proposé par Deequ, afin de contrôler la qualité de notre donnée.&lt;/p&gt;
&lt;!-- end paragraph 087adfa3-8c8b-42c2-9e26-71c900e540d0--&gt;
&lt;!-- begin code 19930ad9-a078-4aef-b6d9-d4273be75dce--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-19930ad9-a078-4aef-b6d9-d4273be75dce&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Profile(id: Long, name: String, age: Int, sexe: String, photo: String)

val rdd = sparkSession.sparkContext.parallelize(Seq(
  Profile(1, &amp;quot;Georgie Hester&amp;quot;, 25, &amp;quot;male&amp;quot;, &amp;quot;https://randomuser.me/api/portraits/men/11.jpg&amp;quot;),
  Profile(2, &amp;quot;Daisie Farrington&amp;quot;, 17,  &amp;quot;other&amp;quot;, &amp;quot;https://randomuser.me/api/portraits/women/11.jpg&amp;quot;),
  Profile(3, &amp;quot;Raveena Espinoza&amp;quot;, 37, &amp;quot;female&amp;quot;, &amp;quot;https://randomuser.me/api/portraits/women/12.jpg&amp;quot;),
  Profile(4, &amp;quot;Donovan Salt&amp;quot;, 21, &amp;quot;male&amp;quot;, null),
  Profile(5, &amp;quot;Ella-Mai Landry&amp;quot;, 49, &amp;quot;female&amp;quot;, &amp;quot;https://randomuser.me/api/portraits/women/13.jpg&amp;quot;)))

val data = sparkSession.createDataFrame(rdd)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 19930ad9-a078-4aef-b6d9-d4273be75dce--&gt;
&lt;!-- begin paragraph 1799e767-1d3f-4715-ae29-f6c9639d3ca2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1799e767-1d3f-4715-ae29-f6c9639d3ca2&quot;&gt;e a self-hosted &amp;amp; open soCe genre de data suppose certaines propriétés. Il faut, par exemple, absolument que l&amp;#39;id soit unique et non null. Mais on peut rajouter énormément de règles pour essayer d&amp;#39;avoir le jeu de données le plus exploitable possible, comme ce qui suit :&lt;/p&gt;
&lt;!-- end paragraph 1799e767-1d3f-4715-ae29-f6c9639d3ca2--&gt;
&lt;!-- begin code 4a8fa06a-c336-426b-95ec-a86ead348358--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4a8fa06a-c336-426b-95ec-a86ead348358&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val verification = VerificationSuite()
	.onData(data)
	.addCheck(
	  Check(CheckLevel.Error, &amp;quot;unit testing&amp;quot;)
	    .hasSize(_ == 5) // 5 éléments dans notre Dataframe
	
	    .isComplete(&amp;quot;id&amp;quot;) // La colonne &amp;quot;id&amp;quot; n&amp;#39;a pas de Null
	    .isUnique(&amp;quot;id&amp;quot;) // La colonne &amp;quot;id&amp;quot; n&amp;#39;a pas de doublon
	
	    .isComplete(&amp;quot;name&amp;quot;) // La colonne &amp;quot;name&amp;quot; n&amp;#39;a pas de Null
	
	    .isPositive(&amp;quot;age&amp;quot;) // La colonne &amp;quot;age&amp;quot; est définit dans l&amp;#39;ensemble Z+
	    .hasMin(&amp;quot;age&amp;quot;, _ == 18) // La colonne &amp;quot;age&amp;quot; 

	    // La colonne &amp;quot;sexe&amp;quot; contient que des valeurs présentes dans l&amp;#39;array
	    .isContainedIn(&amp;quot;sexe&amp;quot;, Array(&amp;quot;male&amp;quot;, &amp;quot;female&amp;quot;, &amp;quot;other&amp;quot;))
	
	    // La colonne &amp;quot;photo&amp;quot; contient au minimum 50% d&amp;#39;URL
	    .containsURL(&amp;quot;photo&amp;quot;, _ &amp;gt;= 0.5))
	.run()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4a8fa06a-c336-426b-95ec-a86ead348358--&gt;
&lt;!-- begin paragraph cbd12775-d5c1-41a3-9b0e-291dfe975ea8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cbd12775-d5c1-41a3-9b0e-291dfe975ea8&quot;&gt;Une fois la vérification faite, on obtient un objet VerificationResult contenant les résultats de l&amp;#39;analyse. On peut ainsi prendre en considération le cas où au moins une contrainte n&amp;#39;est pas respectée. Dans notre cas, on va juste les afficher en console. Mais l&amp;#39;objectif serait ici de traiter ces cas, soit en n&amp;#39;acceptant pas la source et en annulant alors l&amp;#39;action, soit en les envoyant dans un dataset à part, soit en traitant le dataframe en question à l&amp;#39;aide des informations données par le résultat.&lt;/p&gt;
&lt;!-- end paragraph cbd12775-d5c1-41a3-9b0e-291dfe975ea8--&gt;
&lt;!-- begin code d20f3963-f54a-4a1e-beb8-27dd3d9d336f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d20f3963-f54a-4a1e-beb8-27dd3d9d336f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;if (verification.status == CheckStatus.Success) {
    // On envoie la data aux services d&amp;#39;après ou en route pour du machine learning 🧠
  } else {
    // On rejete la data, on la clean, on la fixe, on fait globalement ce qu&amp;#39;on veut, hein !

    // On peut récupérer les contraintes qui ont échouer comme suit
    val contraintes = verification.checkResults
      .flatMap { case (_, checkResult) =&amp;gt; checkResult.constraintResults }

    val contraintes_ratées = contraintes
      .filter { _.status != ConstraintStatus.Success }
      
    // On affiche les contraintes qui ont empéché une bonne exécution du programme
    contraintes_ratées 
	.foreach { result =&amp;gt; println(s&amp;quot;${result.constraint}: ${result.message.get}&amp;quot;) }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d20f3963-f54a-4a1e-beb8-27dd3d9d336f--&gt;
&lt;!-- begin paragraph a6adfbc6-02e3-46cf-b9fa-24c352cd9421--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a6adfbc6-02e3-46cf-b9fa-24c352cd9421&quot;&gt;Dans notre cas, la data étant plutôt propre. On obtient juste l&amp;#39;erreur suivante :&lt;/p&gt;
&lt;!-- end paragraph a6adfbc6-02e3-46cf-b9fa-24c352cd9421--&gt;
&lt;!-- begin code 67e22e71-7f4b-4934-b521-fa62bd4fb097--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-67e22e71-7f4b-4934-b521-fa62bd4fb097&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;We found errors in the data:

MinimumConstraint(Minimum(age,None)): Value: 17.0 does not meet the constraint requirement!&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 67e22e71-7f4b-4934-b521-fa62bd4fb097--&gt;
&lt;!-- begin paragraph a67f4a70-1e78-4e30-90ea-5c89c0f240a8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a67f4a70-1e78-4e30-90ea-5c89c0f240a8&quot;&gt;Et en effet, Daisie n&amp;#39;a pas encore 18 ans !&lt;/p&gt;
&lt;!-- end paragraph a67f4a70-1e78-4e30-90ea-5c89c0f240a8--&gt;
&lt;!-- begin paragraph 65169107-a0bd-474e-b581-3d4190c3dc51--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-65169107-a0bd-474e-b581-3d4190c3dc51&quot;&gt;Comme dit précédemment, le &lt;code&gt;constraint checking&lt;/code&gt; est l&amp;#39;une des nombreuses façons de faire de la data quality sur nos jeux de données. Une autre manière, par exemple, serait d&amp;#39;utiliser les &lt;code&gt;annotations&lt;/code&gt; fournies par une de nos librairies: &lt;a href=&quot;https://github.com/univalence/spark-tools/tree/master/centrifuge&quot;&gt;Centrifuge&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 65169107-a0bd-474e-b581-3d4190c3dc51--&gt;
&lt;!-- begin paragraph f5b748bd-e034-437b-8ae9-313543bf32a4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f5b748bd-e034-437b-8ae9-313543bf32a4&quot;&gt;Centrifuge est très similaire au &lt;code&gt;constraint checking&lt;/code&gt; à une différence près : ici, chaque ligne est associée à son lot de contraintes permettant une analyse au cas par cas. Tout dépend du problème qui vous fait face et de ce que vous recherchez. J&amp;#39;ai choisi ici de me concentrer sur Deequ, mais j&amp;#39;aurais bien pu faire écho à d&amp;#39;autres librairies proposant des solutions afin de répondre aux problèmes de qualité de la donnée. &lt;a href=&quot;https://github.com/FRosner/drunken-data-quality&quot;&gt;Drunken data quality&lt;/a&gt;, propose, à titre d’exemple, un service similaire à Deequ. &lt;/p&gt;
&lt;!-- end paragraph f5b748bd-e034-437b-8ae9-313543bf32a4--&gt;
&lt;!-- begin heading_2 4a2aad16-37e8-4766-af74-7d9787f92529--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4a2aad16-37e8-4766-af74-7d9787f92529&quot;&gt;Un problème de taille&lt;/h3&gt;
&lt;!-- end heading_2 4a2aad16-37e8-4766-af74-7d9787f92529--&gt;
&lt;!-- begin paragraph 9665e505-b29e-4032-adfc-28c28009732d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9665e505-b29e-4032-adfc-28c28009732d&quot;&gt;Qu&amp;#39;importe la solution envisagée, la data quality a un problème majeur : il alourdit le processus. Créer un profil résumant votre data ne peut pas être gratuit en matière de calcul et donc en termes de temps. Nombreux sont les outils qui, aujourd&amp;#39;hui, ne peuvent pas être utilisés, car rallongeant le processus d&amp;#39;un temps incommensurablement long à moins d&amp;#39;augmenter notre puissance de calcul et demandant une capacité de stockage parfois bien trop grande en vue de nos jeux de données immenses. Cependant, il existe des moyens de réduire drastiquement son impact. Cela n&amp;#39;est pas pour autant gratuit et généralement, un gain de temps et d&amp;#39;espace signifie obligatoirement une perte d&amp;#39;information. &lt;/p&gt;
&lt;!-- end paragraph 9665e505-b29e-4032-adfc-28c28009732d--&gt;
&lt;!-- begin paragraph 177c5e42-7cd9-4878-9994-c209a76ab7e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-177c5e42-7cd9-4878-9994-c209a76ab7e7&quot;&gt;Nous sommes, par exemple en ce moment même, en train de construire une librairie nommée &lt;a href=&quot;https://github.com/univalence/spark-tools/tree/master/parka&quot;&gt;Parka&lt;/a&gt;. Cette dernière applique une technique de data quality nommée &lt;code&gt;delta QA&lt;/code&gt;. &lt;code&gt;delta QA&lt;/code&gt; compare deux dataframes entre eux et nous informe de leurs différences. L&amp;#39;objectif premier de cette librairie est de faciliter les &lt;a href=&quot;https://fr.wikipedia.org/wiki/Test_de_r%C3%A9gression&quot;&gt;tests de non-régression&lt;/a&gt; en vérifiant que les changements effectués n&amp;#39;affectent pas de manière négative la data. Vous pouvez retrouver une présentation complète de ce qu&amp;#39;est &lt;code&gt;delta QA&lt;/code&gt; dans ce &lt;a href=&quot;https://www.youtube.com/watch?v=t24sUF2zWLY&amp;t=14m25s&quot;&gt;talk de Jonathan Winandy à Curry On! 2017&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 177c5e42-7cd9-4878-9994-c209a76ab7e7--&gt;
&lt;!-- begin paragraph b4fb55ff-7e3c-46d3-a03c-803506ebdec4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b4fb55ff-7e3c-46d3-a03c-803506ebdec4&quot;&gt;Pour cette bibliothèque, un de nos objectifs était de rendre le processus léger. Comme dit précédemment, cela implique de devoir faire des compromis. Le compromis de Parka est l&amp;#39;approximation des mesures lorsqu&amp;#39;il s&amp;#39;agit d&amp;#39;engranger une large quantité de données en implémentant des structures approximatives qui sont aujourd&amp;#39;hui, au sein de Parka, au nombre de deux : 1/ l&amp;#39;utilisation de &lt;a href=&quot;https://twitter.github.io/algebird/datatypes/approx/q_tree.html&quot;&gt;QTree&lt;/a&gt; comme structure approximative d&amp;#39;un histogramme et 2/ l&amp;#39;utilisation du &lt;a href=&quot;https://www.youtube.com/watch?v=ibxXO-b14j4&quot;&gt;count min sketch&lt;/a&gt; pour l&amp;#39;énumération des éléments au sein d&amp;#39;une même colonne.&lt;/p&gt;
&lt;!-- end paragraph b4fb55ff-7e3c-46d3-a03c-803506ebdec4--&gt;
&lt;!-- begin paragraph 16081990-e937-453b-acca-c1a262a851f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-16081990-e937-453b-acca-c1a262a851f2&quot;&gt;Un autre moyen d&amp;#39;alléger le processus a été de donner à notre profil une structure &lt;a href=&quot;https://fr.wikipedia.org/wiki/Mono%C3%AFde&quot;&gt;monoïdale&lt;/a&gt;. Sans rentrer dans les détails (je vous invite cependant à regarder du côté des structures algébriques et un article sur le sujet apparaîtra sans doute dans le futur), cela permet de diviser pour mieux régner, c&amp;#39;est-à-dire fragmenter notre donnée, lancer ces fragments en parallèle pour ensuite les rassembler en les combinant et tout ceci grâce à sa &lt;a href=&quot;https://fr.wikipedia.org/wiki/Associativit%C3%A9&quot;&gt;loi associative&lt;/a&gt; interne.&lt;/p&gt;
&lt;!-- end paragraph 16081990-e937-453b-acca-c1a262a851f2--&gt;
&lt;!-- begin paragraph 8cb76143-cd5c-4a46-af11-0b14e769ffc1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8cb76143-cd5c-4a46-af11-0b14e769ffc1&quot;&gt;Cela n&amp;#39;était qu&amp;#39;un avant-goût de cette librairie. Parka aura, tout comme les autres librairies de &lt;a href=&quot;https://github.com/univalence/spark-tools&quot;&gt;spark-tools&lt;/a&gt;, le droit à son propre article 😋.&lt;/p&gt;
&lt;!-- end paragraph 8cb76143-cd5c-4a46-af11-0b14e769ffc1--&gt;
&lt;!-- begin heading_2 18abf3de-1562-45bf-8d8a-3594305a5a79--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-18abf3de-1562-45bf-8d8a-3594305a5a79&quot;&gt;Ce qu&amp;#39;il faut retenir&lt;/h3&gt;
&lt;!-- end heading_2 18abf3de-1562-45bf-8d8a-3594305a5a79--&gt;
&lt;!-- begin paragraph a8b0649e-eeb5-4142-b1df-162eb81ab2c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a8b0649e-eeb5-4142-b1df-162eb81ab2c3&quot;&gt;Qu&amp;#39;importe le coût de la data quality, cette dernière ne devrait pas être mise en second plan comme elle est aujourd&amp;#39;hui. Une mauvaise qualité de données coûte énormément aux entreprises et peut-être dans certains cas une perte d&amp;#39;information incorrigible par la suite.&lt;/p&gt;
&lt;!-- end paragraph a8b0649e-eeb5-4142-b1df-162eb81ab2c3--&gt;
&lt;!-- begin paragraph 48ccdceb-ab10-489f-9fbb-c72d8f970f1e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-48ccdceb-ab10-489f-9fbb-c72d8f970f1e&quot;&gt;Aujourd&amp;#39;hui, toutes les entreprises ont des problèmes de data quality, et nombreuses sont corrigibles par le biais de techniques déjà existantes. Nous en avons vu certaines aujourd&amp;#39;hui telles que le &lt;code&gt;constraint checking&lt;/code&gt;, les &lt;code&gt;annotations&lt;/code&gt; ou encore &lt;code&gt;delta QA&lt;/code&gt;. Mais il en existe plein d&amp;#39;autres et je vous invite fortement à vous y intéresser de plus près.&lt;/p&gt;
&lt;!-- end paragraph 48ccdceb-ab10-489f-9fbb-c72d8f970f1e--&gt;
&lt;!-- begin paragraph 974735a6-2372-4144-b565-e4337d18bb9e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-974735a6-2372-4144-b565-e4337d18bb9e&quot;&gt;Une des façons les plus ludiques de s&amp;#39;y intéresser, c&amp;#39;est de regarder des talks à ce sujet. Voici une liste de talks non exhaustive parlant de data quality:&lt;/p&gt;
&lt;!-- end paragraph 974735a6-2372-4144-b565-e4337d18bb9e--&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/C3sbxtRe2Po?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Anomaly Detection for Data Quality and Metric Shifts at Netflix présenté par Laura Pruitt&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/t24sUF2zWLY?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Data quality in Spark without the costs présenté par Jonathan Winandy&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/JDzs3RHQ1Sg?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Data quality as a key factor in Big Data Government strategies présenté par David Bordas &amp;amp; Nacho Alvaro&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 0ebe4027-6e9d-4e42-91c8-792c75fbf090--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0ebe4027-6e9d-4e42-91c8-792c75fbf090&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0ebe4027-6e9d-4e42-91c8-792c75fbf090--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:167412d5403c436db1e1609fc8b779b9</id>
    <title>Transparence référentielle - II : The rise of non-strict evaluation</title>
    <updated>2019-08-01T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/transparence-referentielle-ii-the-rise-of-non-strict-evaluation.html"/>
    <!--summary -->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Programmation fonctionnelle"></category>    <category term="Effet"></category>    <category term="Évaluation non-stricte"></category>    <category term="Transparence référentielle"></category>    <content type="html">
&lt;!-- begin paragraph c6376d5b-6c1c-4718-82e7-9120d56e5d36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c6376d5b-6c1c-4718-82e7-9120d56e5d36&quot;&gt;&lt;a href=&quot;https://blog.univalence.io/transparence-referentielle-i-la-perte-de-la-predictibilite/&quot;&gt;Ayant précédemment abordé la transparence référentielle&lt;/a&gt;, nous nous posions précédemment des questions sur la prédictabilité du code dans un cadre contenant des effets (appel de service, accès aux données du système, variable globale...). Bien évidemment, les effets créent des dépendances fortes avec des éléments externes à l&amp;#39;application, ils ne facilitent pas cette prédictabilité et par conséquent, ils ne facilitent ni la testabilité et ni la maintenabilité d&amp;#39;une application et de ses composantes. Mais les effets sont un mal nécessaire et il faut composer notre code avec ! Alors comment faire ?&lt;/p&gt;
&lt;!-- end paragraph c6376d5b-6c1c-4718-82e7-9120d56e5d36--&gt;
&lt;!-- begin paragraph af4bcfee-1476-4eb9-84e8-22b803af4856--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-af4bcfee-1476-4eb9-84e8-22b803af4856&quot;&gt;En programmation fonctionnelle, dès que quelque chose ne semble a priori pas faisable, on passe par des fonctions et des paramètres. C&amp;#39;est exactement ce qu&amp;#39;on va faire avec les effets afin de retrouver la transparence référentielle.&lt;/p&gt;
&lt;!-- end paragraph af4bcfee-1476-4eb9-84e8-22b803af4856--&gt;
&lt;!-- begin paragraph fb9b959f-d8df-4cf4-a625-dd391679186d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fb9b959f-d8df-4cf4-a625-dd391679186d&quot;&gt;En gros, nous allons représenter les effets par des fonctions, composer des expressions à partir des ces &lt;i&gt;fonctions-effets&lt;/i&gt; et une fois que l&amp;#39;ensemble de l&amp;#39;expression est bâtie, représentant ainsi l&amp;#39;orchestration des effets, on exécute le tout, lançant ainsi les effets que s&amp;#39;ils sont nécessaires.&lt;/p&gt;
&lt;!-- end paragraph fb9b959f-d8df-4cf4-a625-dd391679186d--&gt;
&lt;!-- begin heading_1 c4cc22ab-426e-476b-8939-e94395110042--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c4cc22ab-426e-476b-8939-e94395110042&quot;&gt;Exemple&lt;/h2&gt;
&lt;!-- end heading_1 c4cc22ab-426e-476b-8939-e94395110042--&gt;
&lt;!-- begin paragraph 3f97d456-8630-460f-ad3a-9927b0d72349--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3f97d456-8630-460f-ad3a-9927b0d72349&quot;&gt;Prenons cet exemple, où on dépose plusieurs fois la même instruction d&amp;#39;affichage dans une liste.&lt;/p&gt;
&lt;!-- end paragraph 3f97d456-8630-460f-ad3a-9927b0d72349--&gt;
&lt;!-- begin code 9366732a-22b2-4600-9d25-f01c0a53616e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9366732a-22b2-4600-9d25-f01c0a53616e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;List(println(&amp;quot;hello&amp;quot;), println(&amp;quot;hello&amp;quot;), println(&amp;quot;hello&amp;quot;))
// hello
// hello
// hello
// List[Unit] = List((), (), ())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9366732a-22b2-4600-9d25-f01c0a53616e--&gt;
&lt;!-- begin paragraph 26b6bfcb-05b6-4e77-826c-d133b911e7af--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26b6bfcb-05b6-4e77-826c-d133b911e7af&quot;&gt;Chaque &lt;code&gt;println&lt;/code&gt; s&amp;#39;affiche immédiatement et retourne la valeur &lt;code&gt;()&lt;/code&gt;, qui est la seule valeur possible pour le type &lt;code&gt;Unit&lt;/code&gt;, type de sortie de &lt;code&gt;println&lt;/code&gt;. Selon le principe de transparence référentielle, il devrait être possible de remplacer les trois &lt;code&gt;println(&amp;quot;hello&amp;quot;)&lt;/code&gt; précédents par une variable représentant cet appel. Sauf que...&lt;/p&gt;
&lt;!-- end paragraph 26b6bfcb-05b6-4e77-826c-d133b911e7af--&gt;
&lt;!-- begin code 5c41a259-f9e8-43ed-b601-1f0c7e4af7e8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5c41a259-f9e8-43ed-b601-1f0c7e4af7e8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val printHello: Unit = println(&amp;quot;hello&amp;quot;)
// hello
// printHello: Unit = ()

List(printHello, printHello, printHello)
// List[Unit] = List((), (), ())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5c41a259-f9e8-43ed-b601-1f0c7e4af7e8--&gt;
&lt;!-- begin paragraph 2b9b0f68-17b1-4894-8b29-c6d22330df24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2b9b0f68-17b1-4894-8b29-c6d22330df24&quot;&gt;Contrairement au code précédent, nous n&amp;#39;avons ici qu&amp;#39;un seul &lt;code&gt;hello&lt;/code&gt; d&amp;#39;affiché. En effet, à la déclaration de la variable &lt;code&gt;printHello&lt;/code&gt;, l&amp;#39;instruction &lt;code&gt;println&lt;/code&gt; va d&amp;#39;abord afficher son paramètre avant de retourner &lt;code&gt;()&lt;/code&gt;, qui est en fait la véritable valeur contenue dans &lt;code&gt;printHello&lt;/code&gt;. Par la suite, l&amp;#39;utilisation de &lt;code&gt;printHello&lt;/code&gt; retournera donc &lt;code&gt;()&lt;/code&gt; sans chercher à appeler &lt;code&gt;println(&amp;quot;hello&amp;quot;)&lt;/code&gt;. J&amp;#39;ai ici un comportement différent de l&amp;#39;exemple précédent : je ne suis donc pas référentiellement transparent, mais référentiellement opaque.&lt;/p&gt;
&lt;!-- end paragraph 2b9b0f68-17b1-4894-8b29-c6d22330df24--&gt;
&lt;!-- begin paragraph 256fce91-6f9e-46ff-bfe4-42c7dc17b507--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-256fce91-6f9e-46ff-bfe4-42c7dc17b507&quot;&gt;Pour pallier cette particularité, nous allons &amp;quot;différer&amp;quot; l&amp;#39;évaluation du &lt;code&gt;println&lt;/code&gt; en l&amp;#39;encadrant dans un contexte réplicable : c&amp;#39;est-à-dire une fonction.&lt;/p&gt;
&lt;!-- end paragraph 256fce91-6f9e-46ff-bfe4-42c7dc17b507--&gt;
&lt;!-- begin code 3cf297c0-2e79-457b-84bb-ea7f9ebbf113--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3cf297c0-2e79-457b-84bb-ea7f9ebbf113&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val printHello: () =&amp;gt; Unit = () =&amp;gt; println(&amp;quot;hello&amp;quot;)
// printHello: () =&amp;gt; Unit = &amp;lt;function0&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3cf297c0-2e79-457b-84bb-ea7f9ebbf113--&gt;
&lt;!-- begin paragraph ddfa8519-8380-4306-9134-273aa8e8b773--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddfa8519-8380-4306-9134-273aa8e8b773&quot;&gt;En effet, une fonction va &amp;quot;différer&amp;quot; l&amp;#39;évaluation de son contenu dans la mesure où rien ne se passe dans la fonction jusqu&amp;#39;à ce qu&amp;#39;elle soit appelée. De plus, appeler plusieurs fois la même fonction nous garanti d&amp;#39;exécuter et de réexécuter autant de fois que demandé le même code.&lt;/p&gt;
&lt;!-- end paragraph ddfa8519-8380-4306-9134-273aa8e8b773--&gt;
&lt;!-- begin paragraph ef7a7f18-5c1e-4b26-9636-903c5b392e78--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ef7a7f18-5c1e-4b26-9636-903c5b392e78&quot;&gt;Notre liste précédente devient une liste de fonctions.&lt;/p&gt;
&lt;!-- end paragraph ef7a7f18-5c1e-4b26-9636-903c5b392e78--&gt;
&lt;!-- begin code 51daf00a-f322-4ba3-b89c-687d6b99dbfd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-51daf00a-f322-4ba3-b89c-687d6b99dbfd&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val task: List[() =&amp;gt; Unit] =
  List(printHello, printHello, printHello)
// task: List[() =&amp;gt; Unit] = List(&amp;lt;function0&amp;gt;, &amp;lt;function0&amp;gt;, &amp;lt;function0&amp;gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 51daf00a-f322-4ba3-b89c-687d6b99dbfd--&gt;
&lt;!-- begin paragraph b1e0ba21-70e1-486a-ad98-e5504ae87ca8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b1e0ba21-70e1-486a-ad98-e5504ae87ca8&quot;&gt;Et pour retrouver le même comportement qu&amp;#39;avant, il faut appeler les fonctions contenues une à une. Ce qui se fait simplement en utilisant &lt;code&gt;map&lt;/code&gt;, ou mieux dans ce cadre : &lt;code&gt;foreach&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph b1e0ba21-70e1-486a-ad98-e5504ae87ca8--&gt;
&lt;!-- begin code 16ec1791-32c0-44df-90a8-56435fa5262d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-16ec1791-32c0-44df-90a8-56435fa5262d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;task.foreach(t =&amp;gt; t()) // or task.foreach(_())
// hello
// hello
// hello&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 16ec1791-32c0-44df-90a8-56435fa5262d--&gt;
&lt;!-- begin paragraph 19775bf4-cb1a-46ad-bd04-7ab296f17a54--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-19775bf4-cb1a-46ad-bd04-7ab296f17a54&quot;&gt;Le fait de pouvoir &amp;quot;différer&amp;quot; une évaluation, qui sera exécutée par la suite au besoin, rentre dans ce qui est appelé l&amp;#39;&lt;b&gt;évaluation non-stricte&lt;/b&gt;. Autrement dit, dans ce cadre, l&amp;#39;évaluation d&amp;#39;une expression se fait en respectant les dépendances, mais pas exactement dans l&amp;#39;ordre exprimé dans le code. C&amp;#39;est dans ce sens qu&amp;#39;elle est non-stricte.&lt;/p&gt;
&lt;!-- end paragraph 19775bf4-cb1a-46ad-bd04-7ab296f17a54--&gt;
&lt;!-- begin paragraph a75eed6f-2332-436c-a79a-aa8f09b8b783--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a75eed6f-2332-436c-a79a-aa8f09b8b783&quot;&gt;Un autre point que nous voyons apparaître ci-dessus : c&amp;#39;est la dichotomie qu&amp;#39;il y a entre une zone de code sans effet (lors de la déclaration de &lt;code&gt;printHello&lt;/code&gt; et &lt;code&gt;task&lt;/code&gt;) et une zone de code avec effet (lors de l&amp;#39;appel à &lt;code&gt;foreach&lt;/code&gt;). Cette dichotomie est une approche classique en programmation fonctionnelle. Mais pas que... Vous la voyez apparaître aussi dans &lt;a href=&quot;https://blog.octo.com/architecture-hexagonale-trois-principes-et-un-exemple-dimplementation/&quot;&gt;l&amp;#39;architecture hexagonale&lt;/a&gt;, séparant des services déterministes et sans états (assimilables à des fonctions pures) du monde extérieur (requête utilisateur, base de données, service tiers, sonde... assimilables à des composants à effet).&lt;/p&gt;
&lt;!-- end paragraph a75eed6f-2332-436c-a79a-aa8f09b8b783--&gt;
&lt;!-- begin paragraph 8c40a61e-775f-4b8c-ace7-1c6bb5b2cfb4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c40a61e-775f-4b8c-ace7-1c6bb5b2cfb4&quot;&gt;Ici la transparence référentielle, nous a permis d&amp;#39;élaborer un moyen de sortir partiellement du dictat de l&amp;#39;approche impérative en différant les effets grâce à des fonctions. Ainsi nous avons pu séparer une zone fonctionnellement pure dans un coin du code et une zone impérative dans un autre, tout en les rendant plus visibles. Cette approche facilite d&amp;#39;autant la lisibilité du code et sa capacité à être refactorable.&lt;/p&gt;
&lt;!-- end paragraph 8c40a61e-775f-4b8c-ace7-1c6bb5b2cfb4--&gt;
&lt;!-- begin paragraph 8f1084b4-e18c-4436-9957-da4767aceda7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8f1084b4-e18c-4436-9957-da4767aceda7&quot;&gt;Dans un prochain article nous verrons comment améliorer cette première conception avec des fonctions en introduisant un type représentant des effets.&lt;/p&gt;
&lt;!-- end paragraph 8f1084b4-e18c-4436-9957-da4767aceda7--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:ee4b8c842d1a41b6ba67e0e0a6580266</id>
    <title>Historisation de données avec Parquet</title>
    <updated>2019-07-18T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/historisation-de-donnees-avec-parquet.html"/>
    <!--summary Profiter du format parquet pour historiser ses données.-->    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="Spark"></category>    <category term="Compression"></category>    <category term="Parquet"></category>    <content type="html">
&lt;!-- begin paragraph 00ee1eb8-a65c-46a7-9270-b48a484aa9bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-00ee1eb8-a65c-46a7-9270-b48a484aa9bc&quot;&gt;Nous allons voir aujourd&amp;#39;hui comment il est possible de compresser fortement des données historisées, par exemple une même table pour des dates différentes, donc en principe n&amp;#39;ayant pas un changement conséquent de données au quotidien en utilisant simplement le format de fichier Parquet, assez connu aujourd&amp;#39;hui.&lt;/p&gt;
&lt;!-- end paragraph 00ee1eb8-a65c-46a7-9270-b48a484aa9bc--&gt;
&lt;!-- begin paragraph ccbd1852-54cf-4c99-bf95-3a199612ec01--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ccbd1852-54cf-4c99-bf95-3a199612ec01&quot;&gt;Introduisons tout d&amp;#39;abord le &lt;a href=&quot;https://fr.wikipedia.org/wiki/Run-length_encoding&quot;&gt;RLE&lt;/a&gt; pour &lt;b&gt;Run-Length Encoding&lt;/b&gt; (codage par plages si l&amp;#39;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Office_qu%C3%A9b%C3%A9cois_de_la_langue_fran%C3%A7aise&quot;&gt;OQLF&lt;/a&gt; est dans le coin), qui est un algorithme de compression de données sans pertes. Youhou !&lt;/p&gt;
&lt;!-- end paragraph ccbd1852-54cf-4c99-bf95-3a199612ec01--&gt;
&lt;!-- begin paragraph acc177f2-af49-4e6c-998c-56fcc5276aed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-acc177f2-af49-4e6c-998c-56fcc5276aed&quot;&gt;Cet algorithme est principalement utilisé pour les documents noirs et blancs comme le fax 👴📠 et pour certains formats de stockage d&amp;#39;images comme le &lt;a href=&quot;https://fr.wikipedia.org/wiki/JPEG&quot;&gt;JPEG&lt;/a&gt; et &lt;a href=&quot;https://fr.wikipedia.org/wiki/Windows_bitmap&quot;&gt;BMP&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph acc177f2-af49-4e6c-998c-56fcc5276aed--&gt;
&lt;!-- begin heading_2 6dacef51-b9ec-4ac8-9a25-e500e0a56124--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-6dacef51-b9ec-4ac8-9a25-e500e0a56124&quot;&gt;Run-Length Encoding&lt;/h3&gt;
&lt;!-- end heading_2 6dacef51-b9ec-4ac8-9a25-e500e0a56124--&gt;
&lt;!-- begin paragraph d12850f9-8f6c-490e-971e-bd8d25df0d02--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d12850f9-8f6c-490e-971e-bd8d25df0d02&quot;&gt;Prenons le cas d&amp;#39;une image qui représente une lettre de l&amp;#39;alphabet en noir sur un fond blanc. Nous savons que cette image est en fait composé de pixels, soit noirs, soit blancs. Dans cette image, nous avons beaucoup de pixels blanc successifs suivis d&amp;#39;un nombre plus ou moins important de pixels noirs puis à nouveau de pixels blancs.&lt;/p&gt;
&lt;!-- end paragraph d12850f9-8f6c-490e-971e-bd8d25df0d02--&gt;
&lt;!-- begin paragraph acc5a6a9-668b-46e0-96cb-7b7a7ef8f83a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-acc5a6a9-668b-46e0-96cb-7b7a7ef8f83a&quot;&gt;Le principe du RLE consiste à dire qu&amp;#39;au lieu de stocker toute l&amp;#39;information qui indique la couleur du pixel, il suffit de stocker seulement le nombre de pixels puis la couleur du pixel.&lt;/p&gt;
&lt;!-- end paragraph acc5a6a9-668b-46e0-96cb-7b7a7ef8f83a--&gt;
&lt;!-- begin paragraph 8ce9c8a8-9bae-40ee-9e45-12a79d250195--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8ce9c8a8-9bae-40ee-9e45-12a79d250195&quot;&gt;Supposons que B = pixel blanc et N = pixel noir. Pour pouvoir stocker 10 pixels blancs sans compression, nous devons stocker BBBBBBBBBB. Mais avec RLE, nous pouvons dire 10B (avec &amp;quot;10&amp;quot; étant écrit au format binaire &lt;code&gt;0x000a&lt;/code&gt;), ce qui prend beaucoup moins de place.&lt;/p&gt;
&lt;!-- end paragraph 8ce9c8a8-9bae-40ee-9e45-12a79d250195--&gt;
&lt;!-- begin heading_2 08d3a145-864f-4fb4-8923-89c10d1f7b63--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-08d3a145-864f-4fb4-8923-89c10d1f7b63&quot;&gt;Bit-packing&lt;/h3&gt;
&lt;!-- end heading_2 08d3a145-864f-4fb4-8923-89c10d1f7b63--&gt;
&lt;!-- begin paragraph cbcaa058-0a6e-4b21-80f1-ffe48910fae2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cbcaa058-0a6e-4b21-80f1-ffe48910fae2&quot;&gt;Parquet utilise aussi du bit-packing, une technique pour optimiser le rangement de données. En quelque mots : lorsqu&amp;#39;on déclare une variable, une taille en bits est réservée. Par exemple un &lt;code&gt;int&lt;/code&gt; réserve 32 bits en général. Mais si l&amp;#39;entier est déclaré avec une valeur de 1 alors nous n&amp;#39;utilisons que 1 bit sur ces 32 bits et il est possible de ranger d&amp;#39;autres données dans l&amp;#39;espace restant.&lt;/p&gt;
&lt;!-- end paragraph cbcaa058-0a6e-4b21-80f1-ffe48910fae2--&gt;
&lt;!-- begin heading_1 6f5623d0-054a-4100-840e-5e03d53f4bc2--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6f5623d0-054a-4100-840e-5e03d53f4bc2&quot;&gt;What now?&lt;/h2&gt;
&lt;!-- end heading_1 6f5623d0-054a-4100-840e-5e03d53f4bc2--&gt;
&lt;!-- begin paragraph b79dd178-66bd-4fe3-a47f-c70280ce903c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b79dd178-66bd-4fe3-a47f-c70280ce903c&quot;&gt;Imaginez maintenant que vous pouvez utiliser ces deux techniques en même temps : cela ferait de vous un &lt;b&gt;grand maître absolu&lt;/b&gt; de la compression. N&amp;#39;est-ce pas ?&lt;/p&gt;
&lt;!-- end paragraph b79dd178-66bd-4fe3-a47f-c70280ce903c--&gt;
&lt;!-- begin paragraph c4901f52-4d10-4485-bc10-4f13fbacf8fc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c4901f52-4d10-4485-bc10-4f13fbacf8fc&quot;&gt;Dans les faits, cette alliance de techniques a déjà été mis en place par le biais d&amp;#39;un format de fichier assez familier en data engineering : le Parquet. &lt;code&gt;&amp;lt;EOD&amp;gt;&lt;/code&gt; (&lt;i&gt;End Of Dream&lt;/i&gt;).&lt;/p&gt;
&lt;!-- end paragraph c4901f52-4d10-4485-bc10-4f13fbacf8fc--&gt;
&lt;!-- begin paragraph 79716c05-b928-4fe0-9aec-1b60b62293e3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79716c05-b928-4fe0-9aec-1b60b62293e3&quot;&gt;Alors que vos rêves de création d&amp;#39;un nouvel algorithme de compression s&amp;#39;évaporent, soudainement, une terrible idée se propage dans votre esprit.&lt;/p&gt;
&lt;!-- end paragraph 79716c05-b928-4fe0-9aec-1b60b62293e3--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/ee4b8c84-2d1a-41b6-ba67-e0e0a6580266/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph b9d16065-42f0-41ee-9281-0543d0bd7547--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b9d16065-42f0-41ee-9281-0543d0bd7547&quot;&gt;Parquet utilise donc un mélange de RLE et de bit-packing. Nous avons vu que le RLE marche très bien dans le cas où la donnée est similaire sur un nombre assez long de bits. De plus, il faut savoir que Parquet stocke la donnée en colonne ce qui signifie que dans un bloc de données d&amp;#39;un disque dur, on essaie de stocker toute la donnée d&amp;#39;une colonne et non pas d&amp;#39;une ligne.&lt;/p&gt;
&lt;!-- end paragraph b9d16065-42f0-41ee-9281-0543d0bd7547--&gt;
&lt;!-- begin paragraph f5ded5f0-65c4-4912-a809-9a9a1286a460--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f5ded5f0-65c4-4912-a809-9a9a1286a460&quot;&gt;Imaginons maintenant que nous travaillons au sein d&amp;#39;une entreprise qui souhaite stocker les données d&amp;#39;une table chaque jour — ce qui arrive assez couramment. Et imaginons aussi que nous l&amp;#39;avons fait pendant une semaine pour commencer. &lt;/p&gt;
&lt;!-- end paragraph f5ded5f0-65c4-4912-a809-9a9a1286a460--&gt;
&lt;!-- begin paragraph 8b15e186-9dcb-4ea8-b6cc-772407200322--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8b15e186-9dcb-4ea8-b6cc-772407200322&quot;&gt;Pour simplifier, supposons que la table ressemble à ça pour le 5 avril 2018 :&lt;/p&gt;
&lt;!-- end paragraph 8b15e186-9dcb-4ea8-b6cc-772407200322--&gt;
&lt;!-- begin table 8e3b909b-159a-47d8-8af7-beb9aeb199d0--&gt;
&lt;table class=&quot;block-table level-&quot; id=&quot;t-8e3b909b-159a-47d8-8af7-beb9aeb199d0&quot;&gt;  &lt;thead&gt;
    &lt;tr&gt;      &lt;th scope=&quot;col&quot;&gt;id&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;data&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;dump_date&lt;/th&gt;    &lt;/tr&gt;
  &lt;/thead&gt;  &lt;tbody&gt;
                    &lt;tr&gt;
                  &lt;td&gt;idTigrou1&lt;/td&gt;                  &lt;td&gt;some data&lt;/td&gt;                  &lt;td&gt;20180405&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;idTigrou2&lt;/td&gt;                  &lt;td&gt;some more data&lt;/td&gt;                  &lt;td&gt;20180405&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;idTigrou2&lt;/td&gt;                  &lt;td&gt;data&lt;/td&gt;                  &lt;td&gt;20180405&lt;/td&gt;          &lt;/tr&gt;      &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- end table 8e3b909b-159a-47d8-8af7-beb9aeb199d0--&gt;
&lt;!-- begin paragraph 0bf74615-0f43-4c6a-afd4-6cdf582d6f65--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0bf74615-0f43-4c6a-afd4-6cdf582d6f65&quot;&gt;Ici, nous avons id qui nous servira de clé primaire de la table Tigrou (si nous ne connaissons pas la clé primaire, c&amp;#39;est beaucoup plus compliqué mais cela ne sera pas traité ici). Nous avons également la date à laquelle nous avons stocké cette table et bien sûr de la donnée.&lt;/p&gt;
&lt;!-- end paragraph 0bf74615-0f43-4c6a-afd4-6cdf582d6f65--&gt;
&lt;!-- begin paragraph 54a2a159-4366-4e0f-8dab-a5736cd080de--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-54a2a159-4366-4e0f-8dab-a5736cd080de&quot;&gt;L&amp;#39;idée que nous avons eu précédemment se dessine de plus en plus clairement : &lt;/p&gt;
&lt;!-- end paragraph 54a2a159-4366-4e0f-8dab-a5736cd080de--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;Pouvons-nous &amp;quot;ranger&amp;quot; la donnée de l&amp;#39;ensemble des tables Tigrou de manière à prendre avantage du format Parquet qui utilise un stockage colonne et des optimisations de compression comme le RLE et le bit-packing pour pouvoir ainsi fortement réduire la taille qu&amp;#39;occupe ces tables tout en pouvant requêter simplement la nouvelle table super compressée résultant de ce rangement ?&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph 816d596e-bde2-48a1-970d-27781b017fdb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-816d596e-bde2-48a1-970d-27781b017fdb&quot;&gt;Et bien la réponse* est :&lt;/p&gt;
&lt;!-- end paragraph 816d596e-bde2-48a1-970d-27781b017fdb--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://media.giphy.com/media/10Jpr9KSaXLchW/source.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 991d4a99-25ce-44d7-8df7-cde20ed6e84c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-991d4a99-25ce-44d7-8df7-cde20ed6e84c&quot;&gt;(* : Nous aurons besoin d&amp;#39;Impala pour requêter la donnée sans s&amp;#39;arracher les cheveux)&lt;/p&gt;
&lt;!-- end paragraph 991d4a99-25ce-44d7-8df7-cde20ed6e84c--&gt;
&lt;!-- begin paragraph a5a03b09-34dd-4f14-9c9b-4a1e75c7f0d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a5a03b09-34dd-4f14-9c9b-4a1e75c7f0d4&quot;&gt;Voyons alors comment faire ça ! Et voyons aussi à la fin des résultats de ce rangement sur des données bien réelles 😇&lt;/p&gt;
&lt;!-- end paragraph a5a03b09-34dd-4f14-9c9b-4a1e75c7f0d4--&gt;
&lt;!-- begin paragraph 8b3c0b95-e1ae-4d30-855f-6cc5ff3507f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8b3c0b95-e1ae-4d30-855f-6cc5ff3507f2&quot;&gt;Reprenons les données des tables Tigrou (seulement sur trois jours pour ne pas avoir à allonger l&amp;#39;exemple).&lt;/p&gt;
&lt;!-- end paragraph 8b3c0b95-e1ae-4d30-855f-6cc5ff3507f2--&gt;
&lt;!-- begin heading_3 f22e486a-1565-48aa-a295-0fe2cf0f9478--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-f22e486a-1565-48aa-a295-0fe2cf0f9478&quot;&gt;Tigrou pour le 20180405&lt;/h4&gt;
&lt;!-- end heading_3 f22e486a-1565-48aa-a295-0fe2cf0f9478--&gt;
&lt;!-- begin code 5d1c8ab9-6cb9-4180-9d49-f7599d4139e6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5d1c8ab9-6cb9-4180-9d49-f7599d4139e6&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+----+--------+
|id |data|date    |
+---+----+--------+
|1  |toto|20180405|
|2  |titi|20180405|
|3  |tata|20180405|
+---+----+--------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5d1c8ab9-6cb9-4180-9d49-f7599d4139e6--&gt;
&lt;!-- begin heading_3 44fedf1e-8624-456c-a9b9-1ca96373c925--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-44fedf1e-8624-456c-a9b9-1ca96373c925&quot;&gt;Tigrou pour le 20180406&lt;/h4&gt;
&lt;!-- end heading_3 44fedf1e-8624-456c-a9b9-1ca96373c925--&gt;
&lt;!-- begin code 75421505-a45e-4dcd-9753-c34632864e57--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-75421505-a45e-4dcd-9753-c34632864e57&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;
+---+------+--------+
|id |data  |date    |
+---+------+--------+
|1  |tototo|20180406|
|2  |titi  |20180406|
|3  |tatata|20180406|
+---+------+--------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 75421505-a45e-4dcd-9753-c34632864e57--&gt;
&lt;!-- begin heading_3 7ccb577b-0d53-43dd-ae50-545d9837bf53--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-7ccb577b-0d53-43dd-ae50-545d9837bf53&quot;&gt;Tigrou pour le 20180407&lt;/h4&gt;
&lt;!-- end heading_3 7ccb577b-0d53-43dd-ae50-545d9837bf53--&gt;
&lt;!-- begin code 46f6d1c8-25b3-499f-a73e-c228269fb99a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-46f6d1c8-25b3-499f-a73e-c228269fb99a&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;
+---+--------+--------+
|id |data    |date    |
+---+--------+--------+
|1  |tototo  |20180407|
|2  |titi    |20180407|
|3  |tatatata|20180407|
+---+--------+--------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 46f6d1c8-25b3-499f-a73e-c228269fb99a--&gt;
&lt;!-- begin paragraph 2676c2f9-5e32-4841-a553-9d1e3a34de9f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2676c2f9-5e32-4841-a553-9d1e3a34de9f&quot;&gt;Notre but est d&amp;#39;arriver à une table super compressée qui contient la donnée des trois tables.&lt;/p&gt;
&lt;!-- end paragraph 2676c2f9-5e32-4841-a553-9d1e3a34de9f--&gt;
&lt;!-- begin paragraph 9dffe32c-575d-43be-be99-f09003731a55--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9dffe32c-575d-43be-be99-f09003731a55&quot;&gt;Nous pouvons commencer par faire l&amp;#39;union de ces trois tables (en utilisant la fonction unionByName expliqué dans &lt;a href=&quot;https://blog.univalence.io/alignement-de-schema-union/&quot;&gt;cet article&lt;/a&gt;)&lt;/p&gt;
&lt;!-- end paragraph 9dffe32c-575d-43be-be99-f09003731a55--&gt;
&lt;!-- begin code aef4c5b0-b46c-493e-b44e-b42427fd13c8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-aef4c5b0-b46c-493e-b44e-b42427fd13c8&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val dfs: Seq[(String, DataFrame)] = Seq(
      &amp;quot;20180405&amp;quot; -&amp;gt; Seq((&amp;quot;1&amp;quot;, &amp;quot;toto&amp;quot;), (&amp;quot;2&amp;quot;, &amp;quot;titi&amp;quot;), (&amp;quot;3&amp;quot;, &amp;quot;tata&amp;quot;)).toDF(&amp;quot;id&amp;quot;, &amp;quot;data&amp;quot;),
      &amp;quot;20190406&amp;quot; -&amp;gt; Seq((&amp;quot;1&amp;quot;, &amp;quot;tototo&amp;quot;), (&amp;quot;2&amp;quot;, &amp;quot;titi&amp;quot;), (&amp;quot;3&amp;quot;, &amp;quot;tatata&amp;quot;)).toDF(&amp;quot;id&amp;quot;, &amp;quot;data&amp;quot;),
      &amp;quot;20180407&amp;quot; -&amp;gt; Seq((&amp;quot;1&amp;quot;, &amp;quot;tototo&amp;quot;), (&amp;quot;2&amp;quot;, &amp;quot;titi&amp;quot;), (&amp;quot;3&amp;quot;, &amp;quot;tatatata&amp;quot;)).toDF(&amp;quot;id&amp;quot;, &amp;quot;data&amp;quot;)
    )

val unionDF: DataFrame = dfs.map(df =&amp;gt; df._2.withColumn(&amp;quot;date&amp;quot;, lit(df._1))).reduce(_ unionByName _)

unionDF.show(false)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code aef4c5b0-b46c-493e-b44e-b42427fd13c8--&gt;
&lt;!-- begin paragraph 639801cf-c0aa-438a-81f7-c7b89e369a26--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-639801cf-c0aa-438a-81f7-c7b89e369a26&quot;&gt;Cela donne : &lt;/p&gt;
&lt;!-- end paragraph 639801cf-c0aa-438a-81f7-c7b89e369a26--&gt;
&lt;!-- begin code 98b17a07-0433-4ff2-99a5-40e4477e9fd8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-98b17a07-0433-4ff2-99a5-40e4477e9fd8&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+--------+--------+
|id |data    |date    |
+---+--------+--------+
|1  |toto    |20180405|
|2  |titi    |20180405|
|3  |tata    |20180405|
|1  |tototo  |20180406|
|2  |titi    |20180406|
|3  |tatata  |20180406|
|1  |tototo  |20180407|
|2  |titi    |20180407|
|3  |tatatata|20180407|
+---+--------+--------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 98b17a07-0433-4ff2-99a5-40e4477e9fd8--&gt;
&lt;!-- begin paragraph f3c8b274-9a5c-46dc-9afe-a8a6d4c3c3f8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f3c8b274-9a5c-46dc-9afe-a8a6d4c3c3f8&quot;&gt;Et ensuite nous pouvons grouper la donnée similaire par date :&lt;/p&gt;
&lt;!-- end paragraph f3c8b274-9a5c-46dc-9afe-a8a6d4c3c3f8--&gt;
&lt;!-- begin code 2ac99145-9b64-490a-9da5-42687778a570--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2ac99145-9b64-490a-9da5-42687778a570&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val dateGroupDF: DataFrame = unionDF.groupBy(cols.map(expr): _*).agg(collect_list(&amp;quot;date&amp;quot;).as(&amp;quot;date&amp;quot;))

dateGroupDF.show(false)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2ac99145-9b64-490a-9da5-42687778a570--&gt;
&lt;!-- begin paragraph 3b6e8b71-b572-416c-b333-4c6a34781770--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b6e8b71-b572-416c-b333-4c6a34781770&quot;&gt;En résultat :&lt;/p&gt;
&lt;!-- end paragraph 3b6e8b71-b572-416c-b333-4c6a34781770--&gt;
&lt;!-- begin code 6130528d-b5d1-40fa-8b5c-ec5e9fa074e7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6130528d-b5d1-40fa-8b5c-ec5e9fa074e7&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+--------+------------------------------+
|id |data    |date                          |
+---+--------+------------------------------+
|3  |tatata  |[20180406]                    |
|3  |tatatata|[20180407]                    |
|3  |tata    |[20180405]                    |
|2  |titi    |[20180405, 20180406, 20180407]|
|1  |tototo  |[20180406, 20180407]          |
|1  |toto    |[20180405]                    |
+---+--------+------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6130528d-b5d1-40fa-8b5c-ec5e9fa074e7--&gt;
&lt;!-- begin paragraph 18ca509f-ac47-4678-b9e2-58d9f761c8c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-18ca509f-ac47-4678-b9e2-58d9f761c8c0&quot;&gt;Finalement on groupe par id :&lt;/p&gt;
&lt;!-- end paragraph 18ca509f-ac47-4678-b9e2-58d9f761c8c0--&gt;
&lt;!-- begin code 779a23f5-fa83-4f4c-afa9-5604142e5bfd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-779a23f5-fa83-4f4c-afa9-5604142e5bfd&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val finalDF: DataFrame = dateGroupDF
.groupBy(groupBy.map(expr): _*)
.agg(collect_list(struct(c.columns.filter(x =&amp;gt; !groupBy.contains(x)).map(expr): _*)).as(&amp;quot;rest&amp;quot;))
.sort(sortBy.map(expr): _*)

finalDF.show(false)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 779a23f5-fa83-4f4c-afa9-5604142e5bfd--&gt;
&lt;!-- begin paragraph 26f15cd2-86a0-415e-8095-fa49e0783e0e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26f15cd2-86a0-415e-8095-fa49e0783e0e&quot;&gt;Et on a enfin :&lt;/p&gt;
&lt;!-- end paragraph 26f15cd2-86a0-415e-8095-fa49e0783e0e--&gt;
&lt;!-- begin code b17d0683-520f-43cc-960c-fd292669ec28--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b17d0683-520f-43cc-960c-fd292669ec28&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+------------------------------------------------------------------+
|id |rest                                                              |
+---+------------------------------------------------------------------+
|1  |[                                                                 |
|   |  [tototo, [20180406, 20180407]],                                 |
|   |  [toto,   [20180405]]                                            |
|   |]                                                                 |
|2  |[                                                                 |
|   |  [titi, [20180405, 20180406, 20180407]]                          |
|   |]                                                                 |                       
|3  |[                                                                 |
|   |  [tatata,   [20180406]],                                         |
|   |  [tatatata, [20180407]],                                         |
|   |  [tata,     [20180405]]                                          |
|   |]                                                                 |
+---+------------------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b17d0683-520f-43cc-960c-fd292669ec28--&gt;
&lt;!-- begin paragraph 1d12ea8f-bd2a-464e-a86a-85a283ca3680--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d12ea8f-bd2a-464e-a86a-85a283ca3680&quot;&gt;Le RLE ici se fait entre tototo / toto et tatata, tatatata et tata !&lt;/p&gt;
&lt;!-- end paragraph 1d12ea8f-bd2a-464e-a86a-85a283ca3680--&gt;
&lt;!-- begin paragraph d68add5b-4100-49bf-a76d-6b119a37c097--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d68add5b-4100-49bf-a76d-6b119a37c097&quot;&gt;Tout le code est disponible dans &lt;a href=&quot;https://github.com/univalence/spark-tools/tree/master/plumbus&quot;&gt;spark-tools/plumbus&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph d68add5b-4100-49bf-a76d-6b119a37c097--&gt;
&lt;!-- begin paragraph 6be0f355-ed02-4315-930e-e96bf19a1635--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6be0f355-ed02-4315-930e-e96bf19a1635&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6be0f355-ed02-4315-930e-e96bf19a1635--&gt;
&lt;!-- begin paragraph b3ea8f93-5317-4c28-b741-597c0bf56ad2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3ea8f93-5317-4c28-b741-597c0bf56ad2&quot;&gt;Nous verrons dans un prochain article comment créer des vues sur cette table qui permettent d&amp;#39;exploiter les données comme si nous avions les tables de départ.&lt;/p&gt;
&lt;!-- end paragraph b3ea8f93-5317-4c28-b741-597c0bf56ad2--&gt;
&lt;!-- begin paragraph f5500f67-7ce9-4fa3-8463-e8c1f465be64--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f5500f67-7ce9-4fa3-8463-e8c1f465be64&quot;&gt;J&amp;#39;aimerais pour finir vous partager quelques chiffres sur un test rapide fait sur des tables de production faisant ~350Mo en parquet pour un jour. En appliquant ce processus à ces tables pour cinq jours nous passons de 1.8Go à 629Mo ce qui représente un taux de compression de 65% 🥳&lt;/p&gt;
&lt;!-- end paragraph f5500f67-7ce9-4fa3-8463-e8c1f465be64--&gt;
&lt;!-- begin heading_3 eea59c35-580f-4295-adab-d05d13f89d4e--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-eea59c35-580f-4295-adab-d05d13f89d4e&quot;&gt;Les fichiers en entrée&lt;/h4&gt;
&lt;!-- end heading_3 eea59c35-580f-4295-adab-d05d13f89d4e--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/ee4b8c84-2d1a-41b6-ba67-e0e0a6580266/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 bdc44e51-b378-416b-944b-42c5f37ede1e--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-bdc44e51-b378-416b-944b-42c5f37ede1e&quot;&gt;Le fichier parquet en sortie&lt;/h4&gt;
&lt;!-- end heading_3 bdc44e51-b378-416b-944b-42c5f37ede1e--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/ee4b8c84-2d1a-41b6-ba67-e0e0a6580266/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_3 d0a3003a-2000-4873-a0b4-633df1b4f444--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-d0a3003a-2000-4873-a0b4-633df1b4f444&quot;&gt;Avec du snappy en bonus&lt;/h4&gt;
&lt;!-- end heading_3 d0a3003a-2000-4873-a0b4-633df1b4f444--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/ee4b8c84-2d1a-41b6-ba67-e0e0a6580266/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 47fef672-2b89-4286-b42c-d4e3b5457e33--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-47fef672-2b89-4286-b42c-d4e3b5457e33&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 47fef672-2b89-4286-b42c-d4e3b5457e33--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:c2c01f7c3ac046afa04de2fd67fb4dce</id>
    <title>Les tests avec Spark : sortir la tête de l&#039;eau</title>
    <updated>2019-07-08T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/les-tests-avec-spark-sortir-la-tete-de-l-eau.html"/>
    <!--summary Les métiers de la data sont assez compliqués comme ça vous ne trouvez pas? Spark-Test est là pour vous faciliter la vie lors de vos tests avec Spark ⚡-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Spark"></category>    <category term="SPARK-TEST"></category>    <category term="Introduction"></category>    <category term="Spark-Tools"></category>    <content type="html">
&lt;!-- begin paragraph ccff9543-8f47-4480-b7c8-cfb87362d87e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ccff9543-8f47-4480-b7c8-cfb87362d87e&quot;&gt;Vous galérez à faire des tests avec Spark ? Vous en avez marre de déclarer une Spark session à chaque fois ? Nous avons la solution pour vous ! Avec Spark-Test™, &lt;del&gt;dites adieu à la saleté&lt;/del&gt; faites vos tests sans difficultés ! &lt;/p&gt;
&lt;!-- end paragraph ccff9543-8f47-4480-b7c8-cfb87362d87e--&gt;
&lt;!-- begin paragraph 0c713fad-8ca4-45a2-9ae9-250df6852bc5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0c713fad-8ca4-45a2-9ae9-250df6852bc5&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0c713fad-8ca4-45a2-9ae9-250df6852bc5--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/c2c01f7c-3ac0-46af-a04d-e2fd67fb4dce/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6cfed581-6656-4db1-982f-be331105abd5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6cfed581-6656-4db1-982f-be331105abd5&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6cfed581-6656-4db1-982f-be331105abd5--&gt;
&lt;!-- begin paragraph 9573a1c0-6abb-4f01-a2e6-0ad3eced2e5f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9573a1c0-6abb-4f01-a2e6-0ad3eced2e5f&quot;&gt;Eh bien, en voila une question intéressante ! Spark-Test est l&amp;#39;une des nombreuses bibliothèques que contient &lt;a href=&quot;https://github.com/univalence/spark-tools&quot;&gt;Spark-Tools&lt;/a&gt;, un ensemble d&amp;#39;outils visant à simplifier la vie des utilisateurs de Spark. Ne vous inquiétez pas, vous aurez l&amp;#39;occasion de découvrir les autres outils au fil de nos articles sur ce blog (avec déjà un article sur &lt;a href=&quot;https://blog.univalence.io/spark-zio/&quot;&gt;Spark-ZIO&lt;/a&gt; et des articles prémisses à Plumbus &lt;a href=&quot;https://blog.univalence.io/fonctions-dordre-superieur-dans-spark-2-pour-traiter-des-structures-imbriquees/&quot;&gt;ici&lt;/a&gt; et &lt;a href=&quot;https://blog.univalence.io/en-finir-avec-les-problemes-de-case-class-dans-spark/&quot;&gt;là&lt;/a&gt;).&lt;/p&gt;
&lt;!-- end paragraph 9573a1c0-6abb-4f01-a2e6-0ad3eced2e5f--&gt;
&lt;!-- begin heading_1 7630f767-3cdc-420a-b729-50291da6059b--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-7630f767-3cdc-420a-b729-50291da6059b&quot;&gt;À la découverte de Spark-Test&lt;/h2&gt;
&lt;!-- end heading_1 7630f767-3cdc-420a-b729-50291da6059b--&gt;
&lt;!-- begin paragraph 10b2e4f5-9ba7-462f-a9d3-ad1130736929--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-10b2e4f5-9ba7-462f-a9d3-ad1130736929&quot;&gt;Aujourd&amp;#39;hui, on commence par la bibliothèque qui est la plus facile à prendre en main. Et oui, vous l&amp;#39;aurez compris on va enfin se concentrer sur Spark-Test. Cette bibliothèque fournit des outils qui facilitent l&amp;#39;édition de tests touchant aux dataframes et/ou datasets en les rendant plus simples tout en fournissant des informations claires et précises grâce aux métadonnées!&lt;/p&gt;
&lt;!-- end paragraph 10b2e4f5-9ba7-462f-a9d3-ad1130736929--&gt;
&lt;!-- begin paragraph 55f812a5-e22a-46b6-a5fc-02c73a4162d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-55f812a5-e22a-46b6-a5fc-02c73a4162d7&quot;&gt;Spark-Test est un descendant de &lt;a href=&quot;https://github.com/MrPowers/spark-fast-tests&quot;&gt;Spark-Fast-Tests&lt;/a&gt; à travers lequel nous avons voulu présenter notre propre vision du problème et proposons ainsi une conception totalement différente. Cette nouvelle conception nous permet, par exemple, d&amp;#39;améliorer le rapport d&amp;#39;erreur. De plus, un chantier est en cours pour améliorer les performances et accélérer fortement les tests. &lt;/p&gt;
&lt;!-- end paragraph 55f812a5-e22a-46b6-a5fc-02c73a4162d7--&gt;
&lt;!-- begin paragraph 741d6393-8247-407f-a146-04f6b1eeca0d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-741d6393-8247-407f-a146-04f6b1eeca0d&quot;&gt;Le setup de cet outil ? Rien de compliqué. Rajouter la dépendance de Spark-Test dans votre &lt;code&gt;build.sbt&lt;/code&gt;, puis un zeste de &lt;code&gt;extends SparkTest&lt;/code&gt; et voilà on est parti ! Pour ceux du fond qui viennent d&amp;#39;arriver ne vous inquiétez pas. Voici un squelette potentiel pour débuter avec Spark-Test.&lt;/p&gt;
&lt;!-- end paragraph 741d6393-8247-407f-a146-04f6b1eeca0d--&gt;
&lt;!-- begin code c73aaf35-c69a-463a-baa9-18050c7517dd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c73aaf35-c69a-463a-baa9-18050c7517dd&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;
// pour scala 2.11 et 2.12
libraryDependencies += &amp;quot;io.univalence&amp;quot; %% &amp;quot;spark-test&amp;quot; % &amp;quot;0.3&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c73aaf35-c69a-463a-baa9-18050c7517dd--&gt;
&lt;!-- begin code d9799786-bbc4-4a09-bfcc-9573e8775823--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d9799786-bbc4-4a09-bfcc-9573e8775823&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;package io.univalence.sparktest

import org.scalatest.FunSuite

class GettingStartedTest extends FunSuite with SparkTest {

  test(&amp;quot;some test&amp;quot;) {
    // Commençons nos tests ici...
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d9799786-bbc4-4a09-bfcc-9573e8775823--&gt;
&lt;!-- begin paragraph 30002c7a-d1c5-4bf6-89c7-b8c263fe88f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-30002c7a-d1c5-4bf6-89c7-b8c263fe88f9&quot;&gt;Maintenant que tout est prêt, commençons par créer un dataframe qui va nous servir d&amp;#39;exemple tout au long de cet article. Spark-Test donne des outils qui aident à créer un dataframe ou un dataset rapidement pour nos tests. Alors, on va pas se gêner pour les utiliser !&lt;/p&gt;
&lt;!-- end paragraph 30002c7a-d1c5-4bf6-89c7-b8c263fe88f9--&gt;
&lt;!-- begin paragraph 7d515b01-33b7-4154-9c5d-f6bba45aa61a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7d515b01-33b7-4154-9c5d-f6bba45aa61a&quot;&gt;Pas besoin de SparkSession. Tout est déjà mis en place pour vous.&lt;/p&gt;
&lt;!-- end paragraph 7d515b01-33b7-4154-9c5d-f6bba45aa61a--&gt;
&lt;!-- begin code ea172000-c8c6-49a0-a3b7-5c90c17acd0b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ea172000-c8c6-49a0-a3b7-5c90c17acd0b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val df = dataframe(&amp;quot;{a:1, b:true}&amp;quot;, &amp;quot;{a:2, b:false}&amp;quot;)
/*
| a |   b   |
+---+-------+
| 1 | true  |
| 2 | false |
*/&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ea172000-c8c6-49a0-a3b7-5c90c17acd0b--&gt;
&lt;!-- begin paragraph 31ca1ad0-9bb5-47f4-a55e-9ff22bc0b860--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-31ca1ad0-9bb5-47f4-a55e-9ff22bc0b860&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 31ca1ad0-9bb5-47f4-a55e-9ff22bc0b860--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://media.giphy.com/media/3o7btNa0RUYa5E7iiQ/giphy.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 b6126e3b-1c43-4594-a5f4-330d33d45887--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-b6126e3b-1c43-4594-a5f4-330d33d45887&quot;&gt;Comparons deux dataframes pour le fun&lt;/h2&gt;
&lt;!-- end heading_1 b6126e3b-1c43-4594-a5f4-330d33d45887--&gt;
&lt;!-- begin paragraph 86c5e462-fee2-490b-a758-5127579e4677--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86c5e462-fee2-490b-a758-5127579e4677&quot;&gt;Maintenant, imaginons que nous voulons comparer deux dataframes. Il faut savoir que deux types de différences peuvent apparaître : une différence dans le schéma et des valeurs différentes.&lt;/p&gt;
&lt;!-- end paragraph 86c5e462-fee2-490b-a758-5127579e4677--&gt;
&lt;!-- begin paragraph 731b122b-6818-44ec-a955-fd0fccc0b37d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-731b122b-6818-44ec-a955-fd0fccc0b37d&quot;&gt;Dans un premier temps, nous allons implémenter une solution &lt;b&gt;sans utiliser Spark-Test&lt;/b&gt;. Pour se faire, nous allons avoir besoin de récupérer le schéma des deux dataframes, récupérer les valeurs, puis les comparer tout ce petit monde pour savoir si oui ou non nos dataframes sont différents. Voici l&amp;#39;une des nombreuses solutions possibles.&lt;/p&gt;
&lt;!-- end paragraph 731b122b-6818-44ec-a955-fd0fccc0b37d--&gt;
&lt;!-- begin code 31d4eb9f-52a6-45e9-a983-560e9602b938--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-31d4eb9f-52a6-45e9-a983-560e9602b938&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//sans utiliser Spark-Test
def isEqual(df1: Dataframe, df2: Dataframe): Unit = {
  if (!df1.schema.equals(df2.schema))
    throw new Exception(&amp;quot;Les schemas sont différents&amp;quot;)
  if (!df1.collect().sameElements(df2.collect()))
    throw new Exception(&amp;quot;Les valeurs sont différentes&amp;quot;)
}

test(&amp;quot;some test without Spark-Test&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, c:false}&amp;quot;)

  isEqual(df1, df2)
  /*
   * java.lang.Exception:
   * Les schemas sont différents
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 31d4eb9f-52a6-45e9-a983-560e9602b938--&gt;
&lt;!-- begin paragraph eafaae78-e228-4396-a575-474b671b0ffd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eafaae78-e228-4396-a575-474b671b0ffd&quot;&gt;Cette solution comporte plusieurs problèmes :&lt;/p&gt;
&lt;!-- end paragraph eafaae78-e228-4396-a575-474b671b0ffd--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le manque d&amp;#39;informations fournies par notre Exception : on peut savoir si le problème d&amp;#39;égalité vient du schéma ou s&amp;#39;il vient des valeurs. Mais on ne peut pas savoir quel est exactement le problème, à savoir en quoi le schéma est différent ou quelles valeurs sont différentes.&lt;/li&gt;&lt;li&gt;Le manque de flexibilité : on peut par exemple ne vouloir comparer que les colonnes communes entre les deux dataframes, par exemple ici uniquement la colonne &amp;quot;a&amp;quot;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 414e2402-92e8-420b-a582-16863144a8bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-414e2402-92e8-420b-a582-16863144a8bb&quot;&gt;Cela peut sembler anodin dans le cas présent, puisque nos dataframes ne contiennent pas plus de deux colonnes et deux lignes. Mais imaginez deux dataframes avec des milliers de colonnes et des millions de lignes... &lt;/p&gt;
&lt;!-- end paragraph 414e2402-92e8-420b-a582-16863144a8bb--&gt;
&lt;!-- begin paragraph d93e254e-7384-466c-90b2-7dd88779c708--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d93e254e-7384-466c-90b2-7dd88779c708&quot;&gt;Maintenant, résolvons ce problème à l&amp;#39;aide de Spark-Test.&lt;/p&gt;
&lt;!-- end paragraph d93e254e-7384-466c-90b2-7dd88779c708--&gt;
&lt;!-- begin code f9befb82-1a64-43dd-99ec-c87eb40e210b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f9befb82-1a64-43dd-99ec-c87eb40e210b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;test(&amp;quot;some test with Spark-Test&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, c:false}&amp;quot;)

  df1.assertEquals(df2)
  /*
   * io.univalence.sparktest.SparkTest$SchemaError: 
   * Field c was not in the original DataFrame.
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f9befb82-1a64-43dd-99ec-c87eb40e210b--&gt;
&lt;!-- begin paragraph 74c38add-fecc-46b0-8871-6ab912bb3464--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-74c38add-fecc-46b0-8871-6ab912bb3464&quot;&gt;On obtient une superbe erreur de type &lt;code&gt;SchemaError&lt;/code&gt;. Mais ce n&amp;#39;est pas tout ! On a aussi le pourquoi du comment et c&amp;#39;est là tout l’intérêt de Spark-Test. L&amp;#39;erreur vient ici de la colonne &amp;#39;c&amp;#39; qui n&amp;#39;est pas présente dans le dataframe d&amp;#39;origine.&lt;/p&gt;
&lt;!-- end paragraph 74c38add-fecc-46b0-8871-6ab912bb3464--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;Mais, mais... Je voulais comparer les colonnes en commun moiiii T_T&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph 4d80d4cc-b8f8-43f6-8341-44802328ba48--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4d80d4cc-b8f8-43f6-8341-44802328ba48&quot;&gt;Pas de panique ! Il suffit d&amp;#39;utiliser la configuration de Spark-Test et de préciser que l&amp;#39;on veut ignorer les colonnes en trop situées dans &lt;code&gt;df2&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 4d80d4cc-b8f8-43f6-8341-44802328ba48--&gt;
&lt;!-- begin code dd6418f7-19f8-4107-8f5c-2d7c37d2c23b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dd6418f7-19f8-4107-8f5c-2d7c37d2c23b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;test(&amp;quot;some test with custom configuration&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, c:false}&amp;quot;)

  withConfiguration(failOnMissingExpectedCol = false)({ df1.assertEquals(df2) })
  /*
   * Success
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dd6418f7-19f8-4107-8f5c-2d7c37d2c23b--&gt;
&lt;!-- begin paragraph 598a93b2-6cdb-4b2e-84b0-6e09bd041449--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-598a93b2-6cdb-4b2e-84b0-6e09bd041449&quot;&gt;Voilà, le test passe sans accroc :)&lt;/p&gt;
&lt;!-- end paragraph 598a93b2-6cdb-4b2e-84b0-6e09bd041449--&gt;
&lt;!-- begin paragraph af4d4348-fcd5-4c76-97e1-71dc6da4d11a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-af4d4348-fcd5-4c76-97e1-71dc6da4d11a&quot;&gt;Maintenant, penchons-nous dans le cas ou deux dataframes sont différents, tout en ayant des schémas similaires. Cela s&amp;#39;explique par une ou plusieurs erreurs de valeur, c&amp;#39;est-à-dire des dissonances au sein même des colonnes.&lt;/p&gt;
&lt;!-- end paragraph af4d4348-fcd5-4c76-97e1-71dc6da4d11a--&gt;
&lt;!-- begin code 74314eef-6ed4-4e44-b0a3-c099eda4e75f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-74314eef-6ed4-4e44-b0a3-c099eda4e75f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;test(&amp;quot;some test with same schema&amp;quot;) {
  val df1 = dataframe(&amp;quot;{a:1, b:true}&amp;quot;,  &amp;quot;{a:2, b:true}&amp;quot;, &amp;quot;{a:3, b:true}&amp;quot;)
  val df2 = dataframe(&amp;quot;{a:1, b:false}&amp;quot;, &amp;quot;{a:2, b:true}&amp;quot;, &amp;quot;{a:4, b:false}&amp;quot;)

  df1.assertEquals(df2)
  /*
   * io.univalence.sparktest.SparkTest$ValueError: 
   * The data set content is different :
   *
   * in value at b, false was not equal to true
   * dataframe({b: true, a: 1})
   * dataframe({b: false, a: 1})
   *
   * in value at b, false was not equal to true
   * in value at a, 4 was not equal to 3
   * dataframe({b: true, a: 3})
   * dataframe({b: false, a: 4})
   */
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 74314eef-6ed4-4e44-b0a3-c099eda4e75f--&gt;
&lt;!-- begin paragraph 94d5e4bd-1f7c-40aa-8304-188396b25e56--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-94d5e4bd-1f7c-40aa-8304-188396b25e56&quot;&gt;La voilà enfin la deuxième erreur. La fameuse &lt;code&gt;ValueError&lt;/code&gt;. Cette erreur nous renseigne sur les différences rencontrées entre les deux dataframes en précisant l&amp;#39;erreur, ainsi que les lignes où se trouvent les dites erreurs que ce soit dans &lt;code&gt;df1&lt;/code&gt; ou dans &lt;code&gt;df2&lt;/code&gt;. &lt;/p&gt;
&lt;!-- end paragraph 94d5e4bd-1f7c-40aa-8304-188396b25e56--&gt;
&lt;!-- begin paragraph 7547b06b-c698-4a33-9d15-dea8ba45010b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7547b06b-c698-4a33-9d15-dea8ba45010b&quot;&gt;Cette précision dans les erreurs que l&amp;#39;on va rencontrer, la flexibilité et la facilité d&amp;#39;utilisation, c&amp;#39;est ça la force de Spark-Test. Cette bibliothèque fournit d&amp;#39;autres fonctionnalités qui peuvent faciliter votre vie de data engineer. De plus, Spark-Test est Open Source. Il est disponible ici : &lt;a href=&quot;https://github.com/univalence/spark-tools/tree/master/spark-test&quot;&gt;https://github.com/univalence/spark-tools/tree/master/spark-test&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 7547b06b-c698-4a33-9d15-dea8ba45010b--&gt;
&lt;!-- begin paragraph 788cc623-95c3-4393-801c-5cec14bfddfd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-788cc623-95c3-4393-801c-5cec14bfddfd&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 788cc623-95c3-4393-801c-5cec14bfddfd--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:31b0afbb9fc54f5bbdb635d304fbfa88</id>
    <title>Le process mining pour tous</title>
    <updated>2019-07-04T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/le-process-mining-pour-tous.html"/>
    <!--summary L&#039;analyse de milliers de logs n&#039;a jamais été aussi simple qu&#039;avec le Process Mining. Vous voulez en savoir plus? Alors entrez!-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Process Mining"></category>    <category term="Introduction"></category>    <category term="Data"></category>    <category term="Découverte"></category>    <content type="html">
&lt;!-- begin paragraph 3b22f6d8-a255-41d8-a7bb-e6f4d06027ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b22f6d8-a255-41d8-a7bb-e6f4d06027ea&quot;&gt;&lt;a href=&quot;https://blog.univalence.io/process-mining-camp-2019-notre-retour-dexperience/&quot;&gt;Le Process Mining Camp 2019&lt;/a&gt; nous a fait goûter à l&amp;#39;univers du process mining. Dans le dernier article, nous avons pris le parti de nous concentrer sur l’expérience vécue à Eindhoven. Ainsi, nous revenons aujourd&amp;#39;hui pour vous présenter ce qu&amp;#39;est le process mining de manière plus détaillée et concrète. L&amp;#39;objectif étant de vous faire découvrir les bases de ce que nous pensons être un outil très intéressant.&lt;/p&gt;
&lt;!-- end paragraph 3b22f6d8-a255-41d8-a7bb-e6f4d06027ea--&gt;
&lt;!-- begin paragraph 7827844f-2c77-4979-9ba5-5a5339c37cfc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7827844f-2c77-4979-9ba5-5a5339c37cfc&quot;&gt;Le process mining peine à se développer en France. Et pourtant, il apporte énormément de visibilité à un process. Dans un monde où la data est devenue reine, le process mining n&amp;#39;a jamais autant eu sa place. Il créé le lien entre l&amp;#39;analyse des processus et la gestion de la data. Chaque process est composé d&amp;#39;actions qui s’enchaînent, formant ainsi une trace ayant un début et une fin. Ces traces sont à la source du process mining, puisque c&amp;#39;est grâce à elles que nous allons pouvoir générer un process model, comme le schéma ci-dessous :&lt;/p&gt;
&lt;!-- end paragraph 7827844f-2c77-4979-9ba5-5a5339c37cfc--&gt;
&lt;!-- begin paragraph 82bd7f37-f552-48d0-b1de-36f272bcf7e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-82bd7f37-f552-48d0-b1de-36f272bcf7e4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 82bd7f37-f552-48d0-b1de-36f272bcf7e4--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b0afbb-9fc5-4f5b-bdb6-35d304fbfa88/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph f8f4b197-edf1-4fc6-a059-0d7031c496bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f8f4b197-edf1-4fc6-a059-0d7031c496bc&quot;&gt;Image tirée du logiciel &lt;a href=&quot;https://fluxicon.com/disco/&quot;&gt;Disco&lt;/a&gt; prise du &lt;a href=&quot;https://fluxicon.com/blog/2014/01/&quot;&gt;blog fluxicon&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph f8f4b197-edf1-4fc6-a059-0d7031c496bc--&gt;
&lt;!-- begin paragraph c91a1f4a-3adc-48dc-87b5-877d81f9daa9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c91a1f4a-3adc-48dc-87b5-877d81f9daa9&quot;&gt;Ceci n&amp;#39;est qu&amp;#39;une simple introduction. Ainsi, si vous vous sentez l’âme d&amp;#39;un explorateur, je vous conseille de vous rendre sur &lt;a href=&quot;https://fluxicon.com/blog/&quot;&gt;le blog de Fluxicon&lt;/a&gt; qui se consacre entièrement sur le process mining, regroupant des interviews d&amp;#39;experts, des cas réels et des techniques utilisées dans le domaine.&lt;/p&gt;
&lt;!-- end paragraph c91a1f4a-3adc-48dc-87b5-877d81f9daa9--&gt;
&lt;!-- begin heading_1 84dbcff2-3d25-41d3-b63a-4500bfebf1b8--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-84dbcff2-3d25-41d3-b63a-4500bfebf1b8&quot;&gt;Glossaire&lt;/h2&gt;
&lt;!-- end heading_1 84dbcff2-3d25-41d3-b63a-4500bfebf1b8--&gt;
&lt;!-- begin paragraph 353c4a44-025b-4aa6-b563-a0b4257a47e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-353c4a44-025b-4aa6-b563-a0b4257a47e1&quot;&gt;Le process mining étant un peu verbeux, voici un glossaire expliquant chaque mot technique :&lt;/p&gt;
&lt;!-- end paragraph 353c4a44-025b-4aa6-b563-a0b4257a47e1--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Event ou event log : un &lt;b&gt;event &lt;/b&gt;est une action émise à un temps donné ayant ainsi au moins un timestamp (le moment auquel l&amp;#39;action a été produite), un case ID (l&amp;#39;id du process) et une activity (le type d&amp;#39;action).&lt;/li&gt;&lt;li&gt;End to end process : un process est une suite d’événements liés de manière chronologique entre eux afin de raconter son déroulement dans le temps.&lt;/li&gt;&lt;li&gt;Event data ou logfiles : fichiers constitués d&amp;#39;une multitude d&amp;#39;&lt;b&gt;event&lt;/b&gt; généralement stocker dans un tableau où chaque ligne est un event.&lt;/li&gt;&lt;li&gt;Trace ou case : une trace est la suite d&amp;#39;actions décrivant un &lt;b&gt;end to end process&lt;/b&gt;.&lt;/li&gt;&lt;li&gt;Process model : représentation systémique d&amp;#39;un &lt;b&gt;end to end process&lt;/b&gt; généré grâce au &lt;b&gt;logfiles.&lt;/b&gt;&lt;/li&gt;&lt;li&gt;ProM/Disco: logiciels de process mining générant un process model et fournissant des outils d&amp;#39;analyse sur ce dernier. Il en existe une multitude que nous n&amp;#39;avons pas eu le temps de consulter. Une bonne partie d&amp;#39;entre eux peuvent être trouver &lt;a href=&quot;https://en.wikipedia.org/wiki/Process_mining&quot;&gt;ici&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 e9fbe218-6982-45ba-975c-51a3bf21cdad--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e9fbe218-6982-45ba-975c-51a3bf21cdad&quot;&gt;De l&amp;#39;event data...&lt;/h2&gt;
&lt;!-- end heading_1 e9fbe218-6982-45ba-975c-51a3bf21cdad--&gt;
&lt;!-- begin paragraph daf003ca-ab79-4b11-9469-cf270ba6474f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-daf003ca-ab79-4b11-9469-cf270ba6474f&quot;&gt;L&amp;#39;action de créer un process model à partir de traces se nomme &amp;quot;play-in&amp;quot;. En réalité, il y a trois types de relations entre la data et les process :&lt;/p&gt;
&lt;!-- end paragraph daf003ca-ab79-4b11-9469-cf270ba6474f--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;les relations &lt;b&gt;play-in&lt;/b&gt;, : on créé un modèle depuis des traces existantes&lt;/li&gt;&lt;li&gt;les relations &lt;b&gt;play-out&lt;/b&gt; : on simule des traces (end to end process) depuis un modèle existant&lt;/li&gt;&lt;li&gt;les relations &lt;b&gt;replay&lt;/b&gt; : on rejoue des traces sur un modèle existant (fait à la main ou généré depuis des traces)&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 450dcbf6-2aca-47b7-9138-410108edb26a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-450dcbf6-2aca-47b7-9138-410108edb26a&quot;&gt;Cependant, généralement lorsqu&amp;#39;on parle de process mining, on se réfère plus aux relations de type play-in et replay. Le but étant d&amp;#39;utiliser un tableau rempli d&amp;#39;event ou log, de générer le process model correspondant puis d&amp;#39;effectuer certaines analyses telles que l&amp;#39;identification de bottleneck (activité bloquante ou surchargée), afin de mieux comprendre le process et de pouvoir l&amp;#39;optimiser.&lt;/p&gt;
&lt;!-- end paragraph 450dcbf6-2aca-47b7-9138-410108edb26a--&gt;
&lt;!-- begin paragraph 5bf04a17-ae87-4fb7-8bce-0cf08ca5b70d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5bf04a17-ae87-4fb7-8bce-0cf08ca5b70d&quot;&gt;Dans le cas du process mining, nous ne pouvons pas utiliser n&amp;#39;importe qu&amp;#39;elle type de data. Seul les &amp;quot;event data&amp;quot; sont acceptés et ces derniers composés d&amp;#39;événement respectent aux moins trois propriétés :&lt;/p&gt;
&lt;!-- end paragraph 5bf04a17-ae87-4fb7-8bce-0cf08ca5b70d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Chaque événement doit contenir un &lt;b&gt;case ID&lt;/b&gt;, l&amp;#39;ID du process en cours. Ce peut être le numéro d&amp;#39;une commande, le nom d&amp;#39;un étudiant, etc.&lt;/li&gt;&lt;li&gt;Chaque événement doit contenir un &lt;b&gt;timestamp&lt;/b&gt;, la date à laquelle l&amp;#39;événement à été effectué.&lt;/li&gt;&lt;li&gt;Chaque événement doit contenir un &lt;b&gt;activity name&lt;/b&gt;, l&amp;#39;activité qui a été effectuer à cette date.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 7e22bb10-3d5d-4d5d-9a92-5eb5c53e5ee5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7e22bb10-3d5d-4d5d-9a92-5eb5c53e5ee5&quot;&gt;Deux autres propriétés sont quant à elles dispensables ainsi :&lt;/p&gt;
&lt;!-- end paragraph 7e22bb10-3d5d-4d5d-9a92-5eb5c53e5ee5--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Chaque événement peut contenir une &lt;b&gt;ressource&lt;/b&gt;, la personne qui a effectué l&amp;#39;action (à noter que dans certains cas, la personne effectuant l&amp;#39;action apparaît dans le case ID lui-même).&lt;/li&gt;&lt;li&gt;Chaque événement peut contenir d&amp;#39;autres data complémentaires.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 628af4b9-68f4-4c65-ae93-0d3704f32aaf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-628af4b9-68f4-4c65-ae93-0d3704f32aaf&quot;&gt;Pour appuyer mes propos, rien ne vaut un exemple. Cet exemple à été fortement inspiré d&amp;#39;un exemple donné dans &lt;a href=&quot;https://www.coursera.org/learn/process-mining/home/welcome&quot;&gt;le cours Coursera à propos du process mining&lt;/a&gt; que je vous invite à aller consulter.&lt;/p&gt;
&lt;!-- end paragraph 628af4b9-68f4-4c65-ae93-0d3704f32aaf--&gt;
&lt;!-- begin table c06ebe90-1145-4452-a571-0cdfa751df77--&gt;
&lt;table class=&quot;block-table level-&quot; id=&quot;t-c06ebe90-1145-4452-a571-0cdfa751df77&quot;&gt;  &lt;thead&gt;
    &lt;tr&gt;      &lt;th scope=&quot;col&quot;&gt;order number&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;action&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;timestamp&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;User&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;product&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;quantity&lt;/th&gt;    &lt;/tr&gt;
  &lt;/thead&gt;  &lt;tbody&gt;
                    &lt;tr&gt;
                  &lt;td&gt;1&lt;/td&gt;                  &lt;td&gt;register&lt;/td&gt;                  &lt;td&gt;26/06/2019-09.25&lt;/td&gt;                  &lt;td&gt;John Doe&lt;/td&gt;                  &lt;td&gt;🚗&lt;/td&gt;                  &lt;td&gt;2&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;2&lt;/td&gt;                  &lt;td&gt;register&lt;/td&gt;                  &lt;td&gt;26/06/2019-09.34&lt;/td&gt;                  &lt;td&gt;John Doe&lt;/td&gt;                  &lt;td&gt;🚀&lt;/td&gt;                  &lt;td&gt;1&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;1&lt;/td&gt;                  &lt;td&gt;check stock&lt;/td&gt;                  &lt;td&gt;26/06/2019-09.43&lt;/td&gt;                  &lt;td&gt;Mickael Bae&lt;/td&gt;                  &lt;td&gt;🚗&lt;/td&gt;                  &lt;td&gt;2&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;2&lt;/td&gt;                  &lt;td&gt;check stock&lt;/td&gt;                  &lt;td&gt;26/06/2019-09.57&lt;/td&gt;                  &lt;td&gt;Mickael Bae&lt;/td&gt;                  &lt;td&gt;🚀&lt;/td&gt;                  &lt;td&gt;1&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;2&lt;/td&gt;                  &lt;td&gt;ship order&lt;/td&gt;                  &lt;td&gt;26/06/2019-10.02&lt;/td&gt;                  &lt;td&gt;Sarah Cabo&lt;/td&gt;                  &lt;td&gt;🚀&lt;/td&gt;                  &lt;td&gt;1&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;3&lt;/td&gt;                  &lt;td&gt;register&lt;/td&gt;                  &lt;td&gt;26/06/2019-10.12&lt;/td&gt;                  &lt;td&gt;John Doe&lt;/td&gt;                  &lt;td&gt;🚀&lt;/td&gt;                  &lt;td&gt;1&lt;/td&gt;          &lt;/tr&gt;      &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- end table c06ebe90-1145-4452-a571-0cdfa751df77--&gt;
&lt;!-- begin paragraph 1b414bc7-2710-447b-b50d-002c9d0c0270--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1b414bc7-2710-447b-b50d-002c9d0c0270&quot;&gt;Si l&amp;#39;on reprend l&amp;#39;exemple ci-dessus, une façon de construire notre process serait de voir l&amp;#39;order number comme étant notre case ID, l&amp;#39;action comme étant l&amp;#39;activity, le timestamp comme étant le timestamp, le user comme étant la ressource et le product/quantity comme étant des données supplémentaires (je parle ici de façon de construire notre process car on aurait très bien pu analyser ces logs en prenant les user comme étant notre case ID par exemple). Maintenant que nous avons définit nos différentes colonnes nous sommes prêt à passer à l&amp;#39;étape suivante.&lt;/p&gt;
&lt;!-- end paragraph 1b414bc7-2710-447b-b50d-002c9d0c0270--&gt;
&lt;!-- begin heading_1 5fe2f52b-7cb7-4d96-b99f-355c93d85960--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5fe2f52b-7cb7-4d96-b99f-355c93d85960&quot;&gt;... au Process Model&lt;/h2&gt;
&lt;!-- end heading_1 5fe2f52b-7cb7-4d96-b99f-355c93d85960--&gt;
&lt;!-- begin paragraph 7cf8d6dd-4eb4-4153-9784-41462472f75f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7cf8d6dd-4eb4-4153-9784-41462472f75f&quot;&gt;Avoir l&amp;#39;event data est un bon début, mais cela n&amp;#39;est qu&amp;#39;un début. Une fois notre event data remplit de millier de logs, il faut pouvoir générer le process model lui correspondant. Pour se faire, des algorithmes ont été créés générant ainsi depuis l&amp;#39;event data le process model correspondant. Le plus simple d&amp;#39;entre eux est l&amp;#39;&lt;a href=&quot;https://en.wikipedia.org/wiki/Alpha_algorithm&quot;&gt;alpha algorithm&lt;/a&gt;. Cependant, sa simplicité implique aussi certaines limitations. Nous n&amp;#39;allons pas rentrer dans les détails ici. Si le sujet vous intéresse, &lt;a href=&quot;https://www.coursera.org/learn/process-mining/home/welcome&quot;&gt;le cours Coursera sur le process mining&lt;/a&gt; rentre dans les détails. Dans notre cas, nous allons générer notre process model grâce un outil : Disco. Importer un fichier sur Disco est très simple.&lt;/p&gt;
&lt;!-- end paragraph 7cf8d6dd-4eb4-4153-9784-41462472f75f--&gt;
&lt;!-- begin paragraph 9cd5dbff-7e3c-4fdb-b4b5-cf1c1fba4a4c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9cd5dbff-7e3c-4fdb-b4b5-cf1c1fba4a4c&quot;&gt;L&amp;#39;exemple ci-dessus n&amp;#39;est pas assez complet pour pouvoir créer un véritable process model ainsi prenons un autre jeu de données qui provient du &lt;a href=&quot;https://www.win.tue.nl/bpi/doku.php?id=2014%3Achallenge&amp;redirect=1id%3D2014%2Fchallenge&quot;&gt;Business Processing Intelligence Challenge 2014&lt;/a&gt;, dans lequel les organisateurs donnent plusieurs fichiers à partir desquels les participants doivent y extraire des informations, afin d&amp;#39;avoir une vision globale de la situation. Nous allons nous concentrer pour la découverte du logiciel Disco sur le fichier nommé &lt;code&gt;Incident activity record&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 9cd5dbff-7e3c-4fdb-b4b5-cf1c1fba4a4c--&gt;
&lt;!-- begin paragraph 5ff742d1-155e-4131-b748-edc68572f90f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ff742d1-155e-4131-b748-edc68572f90f&quot;&gt;Commençons par l&amp;#39;importer afin de convertir les différents log en un process model :&lt;/p&gt;
&lt;!-- end paragraph 5ff742d1-155e-4131-b748-edc68572f90f--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b0afbb-9fc5-4f5b-bdb6-35d304fbfa88/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 0ad115d2-f3b2-4dc4-a2a5-9867486ddc86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0ad115d2-f3b2-4dc4-a2a5-9867486ddc86&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0ad115d2-f3b2-4dc4-a2a5-9867486ddc86--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b0afbb-9fc5-4f5b-bdb6-35d304fbfa88/create2.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 8fdc10c1-f47d-4757-b69d-cdf14e32ea99--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8fdc10c1-f47d-4757-b69d-cdf14e32ea99&quot;&gt;Il faut préciser le type des colonnes. Dans notre cas, ce n&amp;#39;est pas bien compliquer : l&amp;#39;incident ID est le case ID, DateStamp le timestamp, incidentActivity_Type l&amp;#39;activity, Assignment Group la ressource et les autres données ne sont que des compléments. N&amp;#39;oubliez pas de convertir vos timestamps dans un format qui correspond à votre data. Une fois cela fait, on peut générer notre process model:&lt;/p&gt;
&lt;!-- end paragraph 8fdc10c1-f47d-4757-b69d-cdf14e32ea99--&gt;
&lt;!-- begin paragraph 9b977349-88df-4891-973c-3fc1edcac92c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b977349-88df-4891-973c-3fc1edcac92c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9b977349-88df-4891-973c-3fc1edcac92c--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b0afbb-9fc5-4f5b-bdb6-35d304fbfa88/pm-full.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 09fc3bd9-0223-4f98-844f-5f56f01bc7e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-09fc3bd9-0223-4f98-844f-5f56f01bc7e0&quot;&gt;Le process model correspondant semble être totalement chaotique. Ceci s&amp;#39;explique surtout par le fait que nous avons de la data issue d&amp;#39;un cas réel. Les process dans la réalité ne sont pas totalement linéaires et nous nous retrouvons souvent avec des activités se produisant que très rarement et donc n&amp;#39;ayant pas une importance primordiale pour nos différentes analyses. Pour y pallier, nous avons plusieurs possibilités. Soit nous filtrons notre process model en prenant par exemple que les traces passant par une activité donnée, soit nous pouvons et c&amp;#39;est ce que nous allons faire ici réduire le nombre d&amp;#39;activités pris en compte. De base, le process model nous montre la totalité des activités. Vous pouvez remarquer que certaines d&amp;#39;entre elles ne sont sollicités qu&amp;#39;une quinzaine de fois sur environ une centaine de milliers de cas. Ce qui est négligeable. On réduit juste le pourcentage à droite de notre process model. Et nous obtenons ainsi le model suivant:&lt;/p&gt;
&lt;!-- end paragraph 09fc3bd9-0223-4f98-844f-5f56f01bc7e0--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b0afbb-9fc5-4f5b-bdb6-35d304fbfa88/pm-simplify.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 4fc86f14-6e69-4d0e-9573-0dd08c3277d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4fc86f14-6e69-4d0e-9573-0dd08c3277d2&quot;&gt;Ce model donne une vision globale du process que nous pouvons par la suite analyser. La phase d&amp;#39;analyse peut se faire à plusieurs niveaux que ce soit au niveau du process model, au niveau de l&amp;#39;onglet statistics, en utilisant des filtres, etc. On peut dans notre cas, à titre d&amp;#39;exemple, regarder la performance de nos activités ou encore analyser la performance de chaque équipe résolvant les différents problèmes. Qu&amp;#39;importe l&amp;#39;analyse, elle doit répondre à des questions posées au préalable et ceci est la résultante d&amp;#39;une compréhension du métier et de ses divers aboutissants.&lt;/p&gt;
&lt;!-- end paragraph 4fc86f14-6e69-4d0e-9573-0dd08c3277d2--&gt;
&lt;!-- begin heading_1 0ccc6977-eda3-4fd0-8d7f-b321e1e24eee--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-0ccc6977-eda3-4fd0-8d7f-b321e1e24eee&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 0ccc6977-eda3-4fd0-8d7f-b321e1e24eee--&gt;
&lt;!-- begin paragraph 3b96faaa-7f0a-44e4-8c82-8451d0ee8aba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b96faaa-7f0a-44e4-8c82-8451d0ee8aba&quot;&gt;Le process mining est une technique très puissante et très intéressante lorsqu&amp;#39;un process rentre en jeux. Des outils puissants existent et nous permettent de visualiser nos logs à travers un process model plus ou moins complexe et dans le cas de &lt;a href=&quot;https://fluxicon.com/disco/&quot;&gt;Disco&lt;/a&gt; totalement modulable. Néanmoins, ce dernier n&amp;#39;est pas magique et certains points sont à prendre en considération. Lors d&amp;#39;un talk de &lt;a href=&quot;https://twitter.com/wvdaalst&quot;&gt;Wil van der Aalst&lt;/a&gt; au &lt;a href=&quot;https://blog.univalence.io/process-mining-camp-2019-notre-retour-dexperience/&quot;&gt;Process Mining Camp 2019&lt;/a&gt;, on nous a mis en garde face à la différenciation entre causalité et corrélation. À cela, je rajouterais une vigilance lors de la data préparation et surtout beaucoup de recul lors de l&amp;#39;analyse d&amp;#39;un process puisqu’il est très facile de prendre un bouc émissaire, d&amp;#39;identifier un bottleneck bloquant la fluidité du process sans pour autant prendre en compte les difficultés du terrain. &lt;/p&gt;
&lt;!-- end paragraph 3b96faaa-7f0a-44e4-8c82-8451d0ee8aba--&gt;
&lt;!-- begin paragraph e8b9187c-b85a-457a-ad1c-32e4d003de24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e8b9187c-b85a-457a-ad1c-32e4d003de24&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e8b9187c-b85a-457a-ad1c-32e4d003de24--&gt;
&lt;!-- begin paragraph 6127f151-75d1-4d77-9e44-3272eb74f014--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6127f151-75d1-4d77-9e44-3272eb74f014&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6127f151-75d1-4d77-9e44-3272eb74f014--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:fcb5063100e8426780e4165c41297097</id>
    <title>Process mining camp 2019 - Notre retour d’expérience</title>
    <updated>2019-06-27T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/process-mining-camp-2019-notre-retour-dexperience.html"/>
    <!--summary Le process mining camp est une conférence qui a lieu tout les ans. L&#039;équipe Univalence a eu la chance d&#039;y participer et vous fait un retour d&#039;expérience.-->    <author>
      <name>Dylan Do Amaral</name>
      <uri>https://univalence.io/blog/auteurs/dylan-do-amaral.html</uri>
    </author>        <category term="Conférence"></category>    <category term="Process Mining"></category>    <category term="Partage"></category>    <category term="Data"></category>    <content type="html">
&lt;!-- begin paragraph d63ea89f-d22d-402b-9a78-4dfabc9180d6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d63ea89f-d22d-402b-9a78-4dfabc9180d6&quot;&gt;Il est mercredi 17h. Nous partons de la Gare Du Nord en direction d&amp;#39;Amsterdam afin d&amp;#39;assister au &lt;b&gt;Process Mining Camp 2019&lt;/b&gt; qui a lieu du 20 au 21 juin à Eindhoven. Tout juste le temps de goûter au &lt;a href=&quot;https://fr.wikipedia.org/wiki/Kapsalon&quot;&gt;kapsalon&lt;/a&gt;, une spécialité locale, avant de nous retrouver à cette conférence de deux jours au sein de l&amp;#39;Eindhoven University of Technology et animée par l&amp;#39;entreprise Fluxicon.&lt;/p&gt;
&lt;!-- end paragraph d63ea89f-d22d-402b-9a78-4dfabc9180d6--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/fcb50631-00e8-4267-80e4-165c41297097/20190620_185216.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 2cdf71d8-a363-49ef-bff8-fcc6392b6ad4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2cdf71d8-a363-49ef-bff8-fcc6392b6ad4&quot;&gt;La culture du vélo aux Pays-Bas n&amp;#39;a jamais été aussi vraie qu&amp;#39;à Eindhoven 🚲&lt;/p&gt;
&lt;!-- end paragraph 2cdf71d8-a363-49ef-bff8-fcc6392b6ad4--&gt;
&lt;!-- begin heading_1 aa31d54f-0396-4876-8c4d-322d958ec926--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-aa31d54f-0396-4876-8c4d-322d958ec926&quot;&gt;Le process mining, quésaco?&lt;/h2&gt;
&lt;!-- end heading_1 aa31d54f-0396-4876-8c4d-322d958ec926--&gt;
&lt;!-- begin paragraph 569aaa5a-fe99-4f11-bd2d-e08358a79a9c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-569aaa5a-fe99-4f11-bd2d-e08358a79a9c&quot;&gt;Process Mining Camp 2019 est une conférence tournant autour du &lt;i&gt;process mining&lt;/i&gt;... Le &lt;b&gt;process mining,&lt;/b&gt; pour ceux qui ne connaissent pas, a pour vocation d&amp;#39;utiliser la data (sous forme d&amp;#39;event data), afin de créer des modèles composés de processus que l&amp;#39;on va pouvoir par la suite analyser et interpréter. Un event doit au moins contenir les informations suivantes pour être utilisé en process mining : un &lt;b&gt;case ID&lt;/b&gt;, un &lt;b&gt;timestamp &lt;/b&gt;et une &lt;b&gt;activity&lt;/b&gt;. Ceci n&amp;#39;est qu&amp;#39;une brève introduction mais je vous invite à vous renseigner sur le process mining via &lt;a href=&quot;https://www.coursera.org/learn/process-mining/home/welcome&quot;&gt;le cours sur Coursera&lt;/a&gt; et &lt;a href=&quot;https://fluxicon.com/blog/&quot;&gt;le blog de fluxicon&lt;/a&gt; tous deux des mines d&amp;#39;or d&amp;#39;information. Un article de blog introduisant le process mining sera aussi bientôt présent sur notre blog alors ouvrez l’œil et le bon 😉 !&lt;/p&gt;
&lt;!-- end paragraph 569aaa5a-fe99-4f11-bd2d-e08358a79a9c--&gt;
&lt;!-- begin paragraph d4c7d1af-0211-4786-90cd-7a0104ae2347--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d4c7d1af-0211-4786-90cd-7a0104ae2347&quot;&gt;Rien de mieux qu&amp;#39;un exemple pour comprendre ce qu&amp;#39;est le process mining. Imaginons un use case qui pour la plupart d&amp;#39;entre nous est assez familier, une issue Github. Une issue peut être vue comme un process puisqu&amp;#39;il &amp;quot;raconte une histoire&amp;quot;.&lt;/p&gt;
&lt;!-- end paragraph d4c7d1af-0211-4786-90cd-7a0104ae2347--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/fcb50631-00e8-4267-80e4-165c41297097/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph ebb91bd7-1952-4ce9-a52e-a4e6dc351839--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ebb91bd7-1952-4ce9-a52e-a4e6dc351839&quot;&gt;Cet exemple vient de &lt;a href=&quot;https://github.com/univalence&quot;&gt;notre projet open source spark-tools&lt;/a&gt;. Nous pouvons le traduire en une suite d’événements ayant un début et une fin ainsi :&lt;/p&gt;
&lt;!-- end paragraph ebb91bd7-1952-4ce9-a52e-a4e6dc351839--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le &lt;i&gt;case ID&lt;/i&gt; dans ce cas serait #19, soit l&amp;#39;ID de l&amp;#39;issue.&lt;/li&gt;&lt;li&gt;Les &lt;i&gt;activities&lt;/i&gt; seraient l&amp;#39;ouverture de l&amp;#39;issue, le ou les commit(s) pour résoudre l&amp;#39;issue et la fermeture de l&amp;#39;issue.&lt;/li&gt;&lt;li&gt;Les &lt;i&gt;timestamps&lt;/i&gt; bien qu’imprécis car pas à la seconde prêt dans ce cas serait 19/06/2019, 24/06/2019, 24/06/2019 (2 commits) et 25/06/2019.&lt;/li&gt;&lt;li&gt;Les &lt;i&gt;resources&lt;/i&gt; seraient respectivement ahoy-jon, HarissonCheng, HarissonCheng et HarissonCheng (2 commits et une fermeture) c&amp;#39;est-à-dire les personnes qui ont fait l&amp;#39;action.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph bc6a68ac-8384-4c0b-a50c-a4837470d55e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bc6a68ac-8384-4c0b-a50c-a4837470d55e&quot;&gt;Voici l&amp;#39;&lt;i&gt;event data&lt;/i&gt; que nous obtiendrions suivi de son model process respectif généré par &lt;a href=&quot;https://fluxicon.com/disco/&quot;&gt;Disco&lt;/a&gt;:&lt;/p&gt;
&lt;!-- end paragraph bc6a68ac-8384-4c0b-a50c-a4837470d55e--&gt;
&lt;!-- begin table cbae9bbf-2655-46aa-847d-14171d616b78--&gt;
&lt;table class=&quot;block-table level-&quot; id=&quot;t-cbae9bbf-2655-46aa-847d-14171d616b78&quot;&gt;  &lt;thead&gt;
    &lt;tr&gt;      &lt;th scope=&quot;col&quot;&gt;Case ID&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;Activity&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;Timestamp&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;Ressource&lt;/th&gt;    &lt;/tr&gt;
  &lt;/thead&gt;  &lt;tbody&gt;
                    &lt;tr&gt;
                  &lt;td&gt;19&lt;/td&gt;                  &lt;td&gt;ouverture de l&amp;#39;issue&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;                  &lt;td&gt;ahoy-jon&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;19&lt;/td&gt;                  &lt;td&gt;commit&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;                  &lt;td&gt;HarissonCheng&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;19&lt;/td&gt;                  &lt;td&gt;commit&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;                  &lt;td&gt;HarissonCheng&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;19&lt;/td&gt;                  &lt;td&gt;fermeture de l&amp;#39;issue&lt;/td&gt;                  &lt;td&gt;&lt;/td&gt;                  &lt;td&gt;HarissonCheng&lt;/td&gt;          &lt;/tr&gt;      &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- end table cbae9bbf-2655-46aa-847d-14171d616b78--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/fcb50631-00e8-4267-80e4-165c41297097/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 918faa7a-69e7-4ed4-9b52-57d9a8c179d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-918faa7a-69e7-4ed4-9b52-57d9a8c179d8&quot;&gt;Nous somme ici témoins d&amp;#39;un exemple d&amp;#39;une simplicité inexistante. Pour comprendre l&amp;#39;importance du process mining il faut s&amp;#39;imaginer avoir des centaines de milliers d&amp;#39;issues toutes entreposées dans un CSV (par exemple), ayant toutes une histoire plus ou moins différente rendant la compréhension générale du process impossible. C&amp;#39;est à ce moment que le process mining devient une valeur ajoutée inestimable puisqu&amp;#39;une fois le process model créer, on peut observer en rejouant les traces le comportement général du processus, analyser les bottleneck, filtrer en bloquant par exemple un des process, regarder le process qui prend le plus de temps à être réalisé, se référer au temps pour détecter une mauvaise gestion du processus à un endroit ou à un autre etc. Valeur prouvée de mainte et mainte fois lors des différents talks que nous avons eus le premier jour.&lt;/p&gt;
&lt;!-- end paragraph 918faa7a-69e7-4ed4-9b52-57d9a8c179d8--&gt;
&lt;!-- begin heading_1 6bf6f66b-094d-4f40-b5c0-f2e01b7e29c9--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6bf6f66b-094d-4f40-b5c0-f2e01b7e29c9&quot;&gt;Le process mining notre ami pour la vie&lt;/h2&gt;
&lt;!-- end heading_1 6bf6f66b-094d-4f40-b5c0-f2e01b7e29c9--&gt;
&lt;!-- begin paragraph 6042ced2-ae86-4130-8b9f-f2f15f5a7b4c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6042ced2-ae86-4130-8b9f-f2f15f5a7b4c&quot;&gt;Nous avons été bien servis la première journée puisque nous avons eu la chance d&amp;#39;assister à pas moins de neuf talks autour du process mining, dont une réalisée par Wil van der Aalst, un acteur majeur dans le domaine, à propos de l&amp;#39;éthique au sein du process mining. &lt;/p&gt;
&lt;!-- end paragraph 6042ced2-ae86-4130-8b9f-f2f15f5a7b4c--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/fcb50631-00e8-4267-80e4-165c41297097/20190620_101106.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6de1ae49-3468-4126-a05a-55ba5d5c96e6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6de1ae49-3468-4126-a05a-55ba5d5c96e6&quot;&gt;Opening du Process Mining Camp par Anne Rozinat de Fluxicon&lt;/p&gt;
&lt;!-- end paragraph 6de1ae49-3468-4126-a05a-55ba5d5c96e6--&gt;
&lt;!-- begin paragraph ef19b7d0-d999-42f3-a1ad-289af43bef56--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ef19b7d0-d999-42f3-a1ad-289af43bef56&quot;&gt;Chaque talk nous a présenté un cas d&amp;#39;utilisation du process mining dans des milieux professionnels variés ayant tous des problématiques différentes. Cela nous a permis de nous rendre compte de l&amp;#39;importance du process mining, dès lors que nous avons un processus qui a un début et une fin et qui se fait en plusieurs étapes. Parmi ces talks, trois d&amp;#39;entre eux nous ont particulièrement interpellés.&lt;/p&gt;
&lt;!-- end paragraph ef19b7d0-d999-42f3-a1ad-289af43bef56--&gt;
&lt;!-- begin paragraph f92b7640-9bfb-4340-a5bc-e53f801105f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f92b7640-9bfb-4340-a5bc-e53f801105f3&quot;&gt;Boris Nikolov de chez Vanderlande nous a fait part de son expérience à travers l&amp;#39;optimisation de deux chaînes de traitement. Il a ainsi illustré les larges possibilités qu&amp;#39;offrent Disco, l&amp;#39;outil de process mining phare. Il a expliqué comment interpréter ces cas pour en ressortir des conclusions orientées métier. Il nous a montré comment, à partir de l&amp;#39;exploration d&amp;#39;un &lt;i&gt;process model&lt;/i&gt;, il valide et optimise les différents processus logistiques, que ce soit au sein d&amp;#39;un aéroport avec le scénario décrivant la durée de vie d&amp;#39;une valise de l&amp;#39;embarquement à l’atterrissage ou d&amp;#39;un entrepôt de colis où le scénario se concentre sur la sélection du colis et son transport jusqu&amp;#39;à sa destination.&lt;/p&gt;
&lt;!-- end paragraph f92b7640-9bfb-4340-a5bc-e53f801105f3--&gt;
&lt;!-- begin paragraph e7997b15-c3f2-4258-9402-e6764eb8b9d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e7997b15-c3f2-4258-9402-e6764eb8b9d1&quot;&gt;Hadi Sotudeh quant à lui a su nous montrer que le process mining pouvait être adapté et toucher à des domaines divers et variés tels qu&amp;#39;un match de foot. Au premier abord, cela semble farfelu d&amp;#39;utiliser le process mining pour analyser un match de foot. Mais avec un peu d&amp;#39;imagination, il est très facile de voir un match de foot comme un process ou chaque action menée par une équipe est une suite d&amp;#39;activités et dont la &amp;quot;ressource&amp;quot; serait le nom du joueur qui effectue l&amp;#39;action. Ce genre de process lui a par exemple permis de répondre à la question suivante : comment cette équipe de foot (l&amp;#39;Iran) joue, en analysant les différents patterns le long du match.&lt;/p&gt;
&lt;!-- end paragraph e7997b15-c3f2-4258-9402-e6764eb8b9d1--&gt;
&lt;!-- begin paragraph c14363fa-9618-4145-bd84-d8d83dac6fb1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c14363fa-9618-4145-bd84-d8d83dac6fb1&quot;&gt;Wil van der Aalst a tenu à clôturer la journée par un talk bienveillant ayant pour but de nous mettre en garde sur le process mining et de nous amener vers de la data &amp;quot;plus verte&amp;quot;. Comme bien d&amp;#39;autres domaines liés à la data, le process mining se heurte à des problèmes de confusion entre causalité et corrélation et de vie privé, point majeur aujourd&amp;#39;hui notamment depuis l&amp;#39;arrivée de &lt;a href=&quot;https://blog.univalence.io/introduction-au-rgpd/&quot;&gt;la RGPD qui a déjà été présenté sur notre blog par Harrison Cheng&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph c14363fa-9618-4145-bd84-d8d83dac6fb1--&gt;
&lt;!-- begin heading_1 b2162832-c59b-41b4-994e-ad8acff21469--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-b2162832-c59b-41b4-994e-ad8acff21469&quot;&gt;Il est temps de mettre la main à la pâte&lt;/h2&gt;
&lt;!-- end heading_1 b2162832-c59b-41b4-994e-ad8acff21469--&gt;
&lt;!-- begin paragraph de44e99a-2f68-42fd-92cc-9bbd8f026973--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-de44e99a-2f68-42fd-92cc-9bbd8f026973&quot;&gt;La deuxième journée nous a permis de mettre en pratique les concepts vus le jour précédent à travers quatre workshops tous centrés sur Disco, le logiciel créé par Fluxicon. Celui-ci permet à l&amp;#39;aide de logs de mettre en place un process model et d&amp;#39;y simuler les différentes traces.&lt;/p&gt;
&lt;!-- end paragraph de44e99a-2f68-42fd-92cc-9bbd8f026973--&gt;
&lt;!-- begin paragraph b01c7f6a-e47b-4e51-8444-b71a2557b5eb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b01c7f6a-e47b-4e51-8444-b71a2557b5eb&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b01c7f6a-e47b-4e51-8444-b71a2557b5eb--&gt;
&lt;!-- begin paragraph 2c1a6110-932f-497f-8b52-c8a35287d40d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c1a6110-932f-497f-8b52-c8a35287d40d&quot;&gt;Les quatre workshops étaient les suivants :&lt;/p&gt;
&lt;!-- end paragraph 2c1a6110-932f-497f-8b52-c8a35287d40d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;i&gt;How to improve processes in the digital age?&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;From ERP system to dataset: How do I prepare a useful event log?&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;How can I combine process mining with RPA?&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;What questions can I answer with process mining?&lt;/i&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph c6fecd9b-fd97-401f-b015-1acea30a51e9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c6fecd9b-fd97-401f-b015-1acea30a51e9&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c6fecd9b-fd97-401f-b015-1acea30a51e9--&gt;
&lt;!-- begin paragraph b0131b71-bc29-4784-9917-8713ada329d6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b0131b71-bc29-4784-9917-8713ada329d6&quot;&gt;Nous avons fait le choix de nous répartir dans les différents workshops. Pour ma part, je suis allé au premier workshop donné par Rudi Niks, un des ingénieurs de Fluxicon. Durant ce workshop, on nous a montré comment le process mining pouvait être utilisé dans chaque étape du DMAIC (Définir, Mesurer, Analyser, Améliorer/&lt;i&gt;Improve&lt;/i&gt; et Contrôler) qui nous a été présenté comme une méthode de résolution de problème qui repose sur les cinq principes cités ci-dessus. À travers un cas concret comportant l&amp;#39;étude d&amp;#39;un process visant à convaincre des personnes de s&amp;#39;inscrire pour une assurance, on a pu parcourir ces étapes et arriver à la conclusion que le process mining est un excellent complément pour comprendre et analyser la complexité d&amp;#39;un process.&lt;/p&gt;
&lt;!-- end paragraph b0131b71-bc29-4784-9917-8713ada329d6--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/fcb50631-00e8-4267-80e4-165c41297097/20190621_090343.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 788a2140-96aa-46d4-8c4b-4289ba24534a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-788a2140-96aa-46d4-8c4b-4289ba24534a&quot;&gt;La salle du workshop 4 où était présent deux de nos ingénieurs, Harrison Cheng et Philippe Hong&lt;/p&gt;
&lt;!-- end paragraph 788a2140-96aa-46d4-8c4b-4289ba24534a--&gt;
&lt;!-- begin heading_1 1cc2e9c1-22de-438f-85f1-d0c7e83651ad--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1cc2e9c1-22de-438f-85f1-d0c7e83651ad&quot;&gt;Les bonnes histoires ont une fin&lt;/h2&gt;
&lt;!-- end heading_1 1cc2e9c1-22de-438f-85f1-d0c7e83651ad--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://media.giphy.com/media/UQaRUOLveyjNC/giphy.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 8a86b774-0574-425a-a24b-8135ba264cb8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8a86b774-0574-425a-a24b-8135ba264cb8&quot;&gt;Il est vendredi 14h, nous repartons déjà vers Paris des idées plein la tête. Ce voyage a été très chargé, mais aussi et surtout très bénéfique. Nous avons eu en effet la chance de rencontrer la communauté du process mining, une communauté remplie de bonnes intentions et nous remercions l&amp;#39;équipe de Fluxicon : Anne Rozinat, Christian W. Günther et Rudi Niks pour avoir organisé ces deux journées ainsi que toute les personnes présentes à cette conférence.&lt;/p&gt;
&lt;!-- end paragraph 8a86b774-0574-425a-a24b-8135ba264cb8--&gt;
&lt;!-- begin paragraph 5d1593af-bf2b-4a2d-833d-a9ad203b3b85--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d1593af-bf2b-4a2d-833d-a9ad203b3b85&quot;&gt;Merci ❤🐇&lt;/p&gt;
&lt;!-- end paragraph 5d1593af-bf2b-4a2d-833d-a9ad203b3b85--&gt;
&lt;!-- begin paragraph 279c5ad8-e4d7-40b1-a32c-f6ff2c0440d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-279c5ad8-e4d7-40b1-a32c-f6ff2c0440d2&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 279c5ad8-e4d7-40b1-a32c-f6ff2c0440d2--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:bc38751079f742358fb793ab70b85d6c</id>
    <title>Scala 2.13.0</title>
    <updated>2019-06-19T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/scala-2-13-0.html"/>
    <!--summary Continuant progressivement son cheminement vers la version 3 et après ~1500 pull requests, la version 2.13.0 de Scala est sortie il y a quelques jours. Vous voulez en savoir plus alors entrer !-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="Changelog"></category>    <content type="html">
&lt;!-- begin paragraph c5ad0e32-2927-4472-92a4-4c34ee0f6310--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c5ad0e32-2927-4472-92a4-4c34ee0f6310&quot;&gt;Continuant progressivement son cheminement vers la version 3 et après ~1500 &lt;i&gt;pull requests&lt;/i&gt;, la &lt;a href=&quot;https://github.com/scala/scala/releases/tag/v2.13.0&quot;&gt;version 2.13.0 de Scala&lt;/a&gt; est sortie il y a quelques jours. Les nouveautés apportées par cette version concerne un refactoring de l&amp;#39;API collection, quelques modifications dans le SDK, de nouvelles possibilités dans le langage et des améliorations du côté du compilateur. Je vous propose de passer en revue les modifications qui m&amp;#39;ont le plus intéressées.&lt;/p&gt;
&lt;!-- end paragraph c5ad0e32-2927-4472-92a4-4c34ee0f6310--&gt;
&lt;!-- begin heading_1 c19e263a-2417-442a-86eb-4b560ea6d9a7--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c19e263a-2417-442a-86eb-4b560ea6d9a7&quot;&gt;Collection&lt;/h2&gt;
&lt;!-- end heading_1 c19e263a-2417-442a-86eb-4b560ea6d9a7--&gt;
&lt;!-- begin paragraph 1d996fd6-9e97-4c0b-9aae-df8102ebdf76--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d996fd6-9e97-4c0b-9aae-df8102ebdf76&quot;&gt;Le plus gros du travail de Scala 2.13.0 est avant tout le refactoring de l&amp;#39;API collection. L&amp;#39;un des objectifs de cette nouvelle version est une simplification du code : une réduction dans la hiérarchie des types, la conservation des méthodes réellement nécessaires, la création d&amp;#39;un package dédié aux collections parallèles en plus des package mutable et immutable, la disparition de CanBuildFrom... Ces simplifications apportent une meilleure lisibilité du code de l&amp;#39;API et permettent d&amp;#39;obtenir de meilleures performances. Elles ont aussi comme particularité d&amp;#39;apporter plus de cohérence.&lt;/p&gt;
&lt;!-- end paragraph 1d996fd6-9e97-4c0b-9aae-df8102ebdf76--&gt;
&lt;!-- begin paragraph ddac7885-7b10-4e2e-ac6a-d82b2ca52be5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddac7885-7b10-4e2e-ac6a-d82b2ca52be5&quot;&gt;Sinon, &lt;a href=&quot;https://www.scala-lang.org/api/2.13.0/scala/collection/immutable/LazyList.html&quot;&gt;LazyList&lt;/a&gt; est proposé en alternative à Stream. La différence est que cette nouvelle collection conserve en mémoire les résultats déjà calculés. Elle peut donc s&amp;#39;avérer plus performante que Stream.&lt;/p&gt;
&lt;!-- end paragraph ddac7885-7b10-4e2e-ac6a-d82b2ca52be5--&gt;
&lt;!-- begin paragraph 8045673e-f5d3-4bb5-ab11-32c228fd0d4a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8045673e-f5d3-4bb5-ab11-32c228fd0d4a&quot;&gt;Côté interopérabilité avec Java, les outils dédiés sont placés dans un package dédié : &lt;a href=&quot;https://www.scala-lang.org/api/2.13.0/scala/jdk/index.html&quot;&gt;scala.jdk&lt;/a&gt;. Ils inclus en plus l&amp;#39;interopérabilité avec les streams de Java, avec le type Optional et les interfaces fonctionnelles prédéfinies du JDK. Il a beaucoup à dire sur cette nouvelle API collection et cela nécessiterait un article à part entière.&lt;/p&gt;
&lt;!-- end paragraph 8045673e-f5d3-4bb5-ab11-32c228fd0d4a--&gt;
&lt;!-- begin paragraph 1b0feb5b-c86d-4421-9526-7a2ab27d35cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1b0feb5b-c86d-4421-9526-7a2ab27d35cf&quot;&gt;Il faut savoir qu&amp;#39;un backport de cette nouvelle API est proposé pour les versions 2.11 et 2.12 de Scala avec le projet &lt;a href=&quot;https://github.com/scala/scala-collection-compat&quot;&gt;scala-collection-compat&lt;/a&gt;. Ce backport inclut des règles de migration &lt;a href=&quot;https://scalacenter.github.io/scalafix/&quot;&gt;Scalafix&lt;/a&gt; pour :&lt;/p&gt;
&lt;!-- end paragraph 1b0feb5b-c86d-4421-9526-7a2ab27d35cf--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;soit réécrire le code pour une version compatible avec la 2.13 ou plus, si vous n&amp;#39;avez pas besoin de cross-compilation avec des versions précédentes (ie. compilation sur plusieurs versions de Scala),&lt;/li&gt;&lt;li&gt;soit réécrire le code en conservant une compatibilité avec la version 2.12 et en introduisant une dépendance vers scala-collection-compat.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 1afa92f9-c47f-4265-89f1-1c99cba47c14--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1afa92f9-c47f-4265-89f1-1c99cba47c14&quot;&gt;D&amp;#39;une manière ou d&amp;#39;une autre, la volonté de ceux qui font Scala et son SDK est de vous pousser à abandonner l&amp;#39;ancienne API quelque soit la version de Scala adoptée, afin de migrer vers une API qui paraît plus stable, plus accessible et mieux conçue. Il n&amp;#39;y a néanmoins aucune garantie que la migration se fera sans heurt. Si l&amp;#39;outillage assure une migration minimisant les problèmes à la compilation, des changements en terme de comportement sont probablement à prévoir.&lt;/p&gt;
&lt;!-- end paragraph 1afa92f9-c47f-4265-89f1-1c99cba47c14--&gt;
&lt;!-- begin heading_1 0096cc98-f958-49ad-b015-74ef2205813a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-0096cc98-f958-49ad-b015-74ef2205813a&quot;&gt;Autres nouveautés&lt;/h2&gt;
&lt;!-- end heading_1 0096cc98-f958-49ad-b015-74ef2205813a--&gt;
&lt;!-- begin paragraph b0a4236d-9b5b-4983-9e18-c9aa3d560d99--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b0a4236d-9b5b-4983-9e18-c9aa3d560d99&quot;&gt;L&amp;#39;utilisation des &lt;i&gt;string interpolator&lt;/i&gt; dans le pattern matching, dans le cadre d&amp;#39;une affectation.&lt;/p&gt;
&lt;!-- end paragraph b0a4236d-9b5b-4983-9e18-c9aa3d560d99--&gt;
&lt;!-- begin code 42a27d9a-03f9-47a2-ba55-fbcb102684a4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-42a27d9a-03f9-47a2-ba55-fbcb102684a4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val date = &amp;quot;2000-01-01&amp;quot;
val s&amp;quot;$year-$month-$day&amp;quot; = date
// year = 2000 - month = 01 - day = 01&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 42a27d9a-03f9-47a2-ba55-fbcb102684a4--&gt;
&lt;!-- begin paragraph e73384fd-c331-4eda-8886-cb525f51740e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e73384fd-c331-4eda-8886-cb525f51740e&quot;&gt;Et avec une structure &lt;code&gt;match...case&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph e73384fd-c331-4eda-8886-cb525f51740e--&gt;
&lt;!-- begin code b7f1db68-f66b-4bf4-b454-6828aa7e7712--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b7f1db68-f66b-4bf4-b454-6828aa7e7712&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def dateComponentOf(date: String): Option[(Int, Int, Int)] =
  date match {
    case s&amp;quot;$year-$month-$day&amp;quot; =&amp;gt; Option((year.toInt, month.toInt, day.toInt))
    case s&amp;quot;$day/$month/$year&amp;quot; =&amp;gt; Option((year.toInt, month.toInt, day.toInt))
    case _ =&amp;gt; None
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b7f1db68-f66b-4bf4-b454-6828aa7e7712--&gt;
&lt;!-- begin paragraph 8c5e235e-7b19-4e8a-b02b-573e32d84c54--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c5e235e-7b19-4e8a-b02b-573e32d84c54&quot;&gt;Cette fonctionnalité permet de réaliser des analyses de chaîne de caractères simples. Elle est moins puissante que les regexp, mais plus accessible.&lt;/p&gt;
&lt;!-- end paragraph 8c5e235e-7b19-4e8a-b02b-573e32d84c54--&gt;
&lt;!-- begin paragraph b8fd7848-9075-4af9-9405-0e32f8394b14--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b8fd7848-9075-4af9-9405-0e32f8394b14&quot;&gt;Scala propose maintenant dans sont SDK une alternative à la fonctionnalité &lt;i&gt;try-with-resources&lt;/i&gt; de Java. &lt;a href=&quot;https://scala-lang.org/files/archive/api/2.13.0/scala/util/Using$.html&quot;&gt;scala.util.Using&lt;/a&gt; tient ce rôle. Il est applicable à des ressources de type &lt;a href=&quot;https://scala-lang.org/files/archive/api/2.13.0/scala/util/Using$$Releasable.html&quot;&gt;Releasable&lt;/a&gt;, ou plus exactement de catégorie Releasable[R], car il s&amp;#39;agit d&amp;#39;une typeclasse. Cerise sur le gâteau, une instance Releasable est applicable à tous les types dérivant de AutoCloseable, par conversion implicite. Ce qui permet d&amp;#39;utiliser Using avec une bonne partie des types ressources du monde Java ! Une différence majeure avec la version Java est le fait que Using est une expression. Comme elle retourne une instance de type Try, on peut l&amp;#39;utiliser dans un &lt;i&gt;for-comprehension&lt;/i&gt;. Néanmoins, comprenez que Using est en évaluation stricte (son contenu est exécuté directement lors de son évaluation dans le code). Pour une version non-stricte, tournez-vous vers la fonctionnalité &lt;i&gt;bracket&lt;/i&gt; de Monix et de ZIO.&lt;/p&gt;
&lt;!-- end paragraph b8fd7848-9075-4af9-9405-0e32f8394b14--&gt;
&lt;!-- begin code 027478f5-9415-4787-820d-4b69fc0f2b26--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-027478f5-9415-4787-820d-4b69fc0f2b26&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val content1: Try[String] =
  for {
    _ &amp;lt;- Using(new BufferedWriter(new FileWriter(file))) {
      _.write(&amp;quot;Hello world&amp;quot;)
    }
    c &amp;lt;- Using(new BufferedReader(new FileReader(file))) {
      _.readLine()
    }
  } yield c
println(s&amp;quot;file content 1: $content1&amp;quot;) // print file content 1: Success(Hello world)

val content2: Try[String] =
  for {
    _ &amp;lt;- Using(new BufferedWriter(new FileWriter(file))) {
      _.write(&amp;quot;Hello world&amp;quot;)
    }
    c &amp;lt;- Using(new BufferedReader(new FileReader(file))) { f =&amp;gt;
      f.close() // in a view to have an exception ;)
      f.readLine()
    }
  } yield c
println(s&amp;quot;file content 2: $content2&amp;quot;) // file content 2: Failure(java.io.IOException: Stream closed)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 027478f5-9415-4787-820d-4b69fc0f2b26--&gt;
&lt;!-- begin paragraph 64a2a3f4-85b0-4256-823a-9a020aa38740--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-64a2a3f4-85b0-4256-823a-9a020aa38740&quot;&gt;L&amp;#39;opération tap peut être ajouté à toute sorte de valeur dans une expression. La fonction retourne la valeur elle-même et s&amp;#39;apparente en ce sens à la fonction identity. Sauf que tap va vous permettre d&amp;#39;introduire des effets de bord dans vos expressions. Son utilisation doit bien entendu être limité au débogage là où il était difficile à introduire auparavan&lt;/p&gt;
&lt;!-- end paragraph 64a2a3f4-85b0-4256-823a-9a020aa38740--&gt;
&lt;!-- begin code 34cb7adc-aec5-4c55-89e2-33243ba8be45--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-34cb7adc-aec5-4c55-89e2-33243ba8be45&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import scala.util.chaining._

val result1 = &amp;quot;hello&amp;quot;.tap(println) + &amp;quot; world&amp;quot; // print hello
val result2 = &amp;quot;hello&amp;quot; + &amp;quot; world&amp;quot;

println(result1 == result2) // print true&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 34cb7adc-aec5-4c55-89e2-33243ba8be45--&gt;
&lt;!-- begin paragraph fc14c5c5-3455-4b11-878e-2fcff28209f5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fc14c5c5-3455-4b11-878e-2fcff28209f5&quot;&gt;Scala 2.13 accepte le caractère &amp;quot;_&amp;quot; comme séparateur numérique. Les valeurs suivantes sont identiques :&lt;/p&gt;
&lt;!-- end paragraph fc14c5c5-3455-4b11-878e-2fcff28209f5--&gt;
&lt;!-- begin code cbe3db93-6296-43fb-9ec3-0ef6ca7604f2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-cbe3db93-6296-43fb-9ec3-0ef6ca7604f2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;100000
100_000
10_00_00&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code cbe3db93-6296-43fb-9ec3-0ef6ca7604f2--&gt;
&lt;!-- begin paragraph c5179990-66be-4579-a06f-da2f2c7513aa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c5179990-66be-4579-a06f-da2f2c7513aa&quot;&gt;Cette fonctionnalité existe déjà dans Java. Elle permet de faciliter la lecture du code en particulier lorsqu&amp;#39;on doit hard coder des grands nombres.&lt;/p&gt;
&lt;!-- end paragraph c5179990-66be-4579-a06f-da2f2c7513aa--&gt;
&lt;!-- begin paragraph 74f55656-54ba-44db-93be-e9b58b6bc656--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-74f55656-54ba-44db-93be-e9b58b6bc656&quot;&gt;L&amp;#39;unification partielle est maintenant tout le temps active et l&amp;#39;option &lt;code&gt;-Ypartial-unification&lt;/code&gt; est retirée. Cette fonctionnalité permet d&amp;#39;utiliser la forme &lt;code&gt;F[_]&lt;/code&gt; dans la déclaration des type — forme très appréciée par les développeur Scala &amp;quot;type level&amp;quot;. Vous trouverez plus de détails dans cette &lt;a href=&quot;https://blog.ippon.fr/2016/11/21/scala-2-12-tour-dhorizon-des-nouveautes/#si2712&quot;&gt;section parlant de SI-2712&lt;/a&gt; dans mon article sur Scala 2.12.&lt;/p&gt;
&lt;!-- end paragraph 74f55656-54ba-44db-93be-e9b58b6bc656--&gt;
&lt;!-- begin paragraph cf9d0ded-50dd-4988-b11d-c5f086b6cda1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf9d0ded-50dd-4988-b11d-c5f086b6cda1&quot;&gt;Les flèches au format unicode sont dépréciées et c&amp;#39;est une très bonne chose. Une flèche comme &lt;code&gt;→&lt;/code&gt; pose problème car elle n&amp;#39;a pas la même priorité que sont équivalent ASCII &lt;code&gt;-&amp;gt;&lt;/code&gt;. Elles sont donc pas interchangeables. Ainsi, l&amp;#39;expression &lt;code&gt;1 -&amp;gt; 2 / 4.0&lt;/code&gt; donne &lt;code&gt;(1, 0.5)&lt;/code&gt;, alors que &lt;code&gt;1 → 2 /  4.0&lt;/code&gt; donne une erreur de compilation : &lt;code&gt;value / is not a member of (Int, Int)&lt;/code&gt;. Il faut ajouter des parenthèses pour obtenir le même résultat &lt;code&gt;1 → (2 / 4.0)&lt;/code&gt;. La dépréciation de cette fonctionnalité devrait éviter pas mal de confusions.&lt;/p&gt;
&lt;!-- end paragraph cf9d0ded-50dd-4988-b11d-c5f086b6cda1--&gt;
&lt;!-- begin paragraph db9656a2-e297-480c-a6ce-d1891f060277--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-db9656a2-e297-480c-a6ce-d1891f060277&quot;&gt;L&amp;#39;implicite &lt;code&gt;any2stringadd&lt;/code&gt; définit dans Predef et responsable de comportements &amp;quot;inappropriés&amp;quot; dans Scala est déprécié. La syntaxe procédurale (&lt;code&gt;def m() {}&lt;/code&gt;) aussi. Il faudra désormais utiliser une signature complète (&lt;code&gt;def m(): Unit = {}&lt;/code&gt;). Les litéraux de type symbole (&lt;code&gt;&amp;#39;symb&lt;/code&gt;) sont dépréciés pour cette notation &lt;code&gt;Symbol(&amp;quot;symb&amp;quot;)&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph db9656a2-e297-480c-a6ce-d1891f060277--&gt;
&lt;!-- begin heading_1 0df83ec3-b81b-44a1-8af8-8c3acb3bbdc9--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-0df83ec3-b81b-44a1-8af8-8c3acb3bbdc9&quot;&gt;Du côté du compilateur&lt;/h2&gt;
&lt;!-- end heading_1 0df83ec3-b81b-44a1-8af8-8c3acb3bbdc9--&gt;
&lt;!-- begin paragraph 91e84ea0-5673-40ed-bcd0-f9f914988f26--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-91e84ea0-5673-40ed-bcd0-f9f914988f26&quot;&gt;Côtés compilateurs, les optimisations promettent une amélioration des performances de 5 à 10 % à la compilation. Et côté runtime, la nouvelles API collections apporte de meilleurs performances en donnant notamment au compilateur la possibilité de mettre en place plus d&amp;#39;inlining (le fait de replacer des appels de fonctions par leur contenu). Petite aide appréciable : le compilateur vous fera des suggestions en cas de faute de frappe sur des noms d&amp;#39;identifiants.&lt;/p&gt;
&lt;!-- end paragraph 91e84ea0-5673-40ed-bcd0-f9f914988f26--&gt;
&lt;!-- begin paragraph c635bfd3-388f-45a9-b229-161760fe100c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c635bfd3-388f-45a9-b229-161760fe100c&quot;&gt;Au niveau option de compilation, deux nouveaux types d&amp;#39;options seront créés, allégeant ainsi -X et -Y :&lt;/p&gt;
&lt;!-- end paragraph c635bfd3-388f-45a9-b229-161760fe100c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;-V pour toutes les options permettant d&amp;#39;afficher des informations sur le fonctionnement du compilateur (eg. &lt;code&gt;-Vphases&lt;/code&gt; affichage des phases de compilation, &lt;code&gt;-Vdoc&lt;/code&gt; affichage des activités de scaladoc).&lt;/li&gt;&lt;li&gt;-W pour toutes les options des gestions des avertissements à la compilation (eg. &lt;code&gt;-Wdead-code&lt;/code&gt; avertissement en cas de code inutilisé, &lt;code&gt;-Wunused:imports&lt;/code&gt; avertissement en cas d&amp;#39;imports inutilisés).&lt;/li&gt;&lt;li&gt;À ce propos, -&lt;code&gt;Xfatal-warnings&lt;/code&gt; va devenir &lt;code&gt;-Werror&lt;/code&gt;. Les deux options existent en 2.13. Mais la release note indique que &lt;code&gt;-Werror&lt;/code&gt; est recommandée. &lt;code&gt;-deprecation&lt;/code&gt; devient &lt;code&gt;-Xlint:deprecation&lt;/code&gt; et &lt;code&gt;-Xfuture&lt;/code&gt; devient &lt;code&gt;-Xsource:2.14&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph e41a96ba-5185-46d3-b53a-df01ba0d72c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e41a96ba-5185-46d3-b53a-df01ba0d72c1&quot;&gt;Car oui, il y aura une version 2.14 qui devrait sortir mi-2020, peu avant la version 3.&lt;/p&gt;
&lt;!-- end paragraph e41a96ba-5185-46d3-b53a-df01ba0d72c1--&gt;
&lt;!-- begin paragraph 851bddb4-40c1-4bff-b9d4-13fe10a7d9c4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-851bddb4-40c1-4bff-b9d4-13fe10a7d9c4&quot;&gt;Côté SBT, Scala 2.13 nécessite d&amp;#39;utiliser au minimum SBT 1.2.8 ou 0.13.18. Sinon, ça fonctionne bien avec Maven et Gradle.&lt;/p&gt;
&lt;!-- end paragraph 851bddb4-40c1-4bff-b9d4-13fe10a7d9c4--&gt;
&lt;!-- begin heading_1 86031a0d-a61d-4a27-96a8-028bd9ec522c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-86031a0d-a61d-4a27-96a8-028bd9ec522c&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 86031a0d-a61d-4a27-96a8-028bd9ec522c--&gt;
&lt;!-- begin paragraph 5d41fc26-c1ef-43d5-b18b-6ec6b6247e1e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d41fc26-c1ef-43d5-b18b-6ec6b6247e1e&quot;&gt;Cette nouvelle version de Scala 2 laisse entrevoir un premier lot de modifications dans le langage vers à la fois plus de maturité et de pragmatisme dans la conception du SDK et les choix appliqués dans les fonctionnalités du langage et le compilateur. Mais elle laisse aussi entrevoir plus de fun en rendant le langage et ses fonctionnalités plus accessibles. Il reste à voir comment se passera la migration vers Scala 3 proposé par ceux qui font le langage. &lt;/p&gt;
&lt;!-- end paragraph 5d41fc26-c1ef-43d5-b18b-6ec6b6247e1e--&gt;
&lt;!-- begin paragraph 0365be08-50ea-44fc-86db-dae3f046189d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0365be08-50ea-44fc-86db-dae3f046189d&quot;&gt;Actuellement, mon regard se tourne vers Spark, sachant que ce framework est aussi connu pour ses temps de latence assez long pour les montées de versions vis-à-vis de Scala et que les mainteneurs de lib abandonnent progressivement Scala 2.11. Néanmoins, un travail est en cours pour &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-25075&quot;&gt;intégrer la 2.13&lt;/a&gt;. &lt;/p&gt;
&lt;!-- end paragraph 0365be08-50ea-44fc-86db-dae3f046189d--&gt;
&lt;!-- begin divider ee46b6da-071f-4991-87aa-aab7c6c82bad--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-ee46b6da-071f-4991-87aa-aab7c6c82bad&quot;&gt;
&lt;!-- end divider ee46b6da-071f-4991-87aa-aab7c6c82bad--&gt;
&lt;!-- begin bookmark f842e8c0-0047-44df-abd3-1df2463f8c33--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-f842e8c0-0047-44df-abd3-1df2463f8c33&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://github.com/scala/scala/releases/tag/v2.13.0&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/62d3a5f68eaad4195677f4a81f3a9d561f79c7416950916ae1f74c625498ca7a/scala/scala/releases/tag/v2.13.0&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Release Scala 2.13.0 · scala/scala&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Scala 2 compiler and standard library. Scala 2 bugs at https://github.com/scala/bug; Scala 3 at https://github.com/scala/scala3 - Release Scala 2.13.0 · scala/scala&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://github.com/scala/scala/releases/tag/v2.13.0&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark f842e8c0-0047-44df-abd3-1df2463f8c33--&gt;
&lt;!-- begin bookmark d4ba041b-4223-4230-af05-9c95bfc82025--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-d4ba041b-4223-4230-af05-9c95bfc82025&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://x.com/omervk/status/1138049573799321600&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://x.com/omervk/status/1138049573799321600&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark d4ba041b-4223-4230-af05-9c95bfc82025--&gt;
&lt;!-- begin bookmark f56e654f-3710-47cd-bf99-7ce6279c2f4e--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-f56e654f-3710-47cd-bf99-7ce6279c2f4e&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://x.com/fpmortals/status/1138349705711300608?s=17&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://x.com/fpmortals/status/1138349705711300608?s=17&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark f56e654f-3710-47cd-bf99-7ce6279c2f4e--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:e726939821e3404d910ec4fbdc88d6de</id>
    <title>Introduction au RGPD (ou GDPR en anglais)</title>
    <updated>2019-06-13T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/introduction-au-rgpd-ou-gdpr-en-anglais.html"/>
    <!--summary Une petite introduction au RGPD : Règlement Général sur la Protection des Données.-->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>        <category term="RGPD"></category>    <content type="html">
&lt;!-- begin paragraph c69f54aa-340c-417d-b303-d73202727fe4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c69f54aa-340c-417d-b303-d73202727fe4&quot;&gt;&amp;quot;We&amp;#39;ve updated our privacy policy&amp;quot;. Si vous habitez en Europe, vous n&amp;#39;avez pas pu rater les millions de mails provenant de sites auquels vous avez oublié que vous étiez inscrit. La cause ? Le 25 mai 2018 est entré en vigueur le RGPD, acronyme de &amp;quot;Règlement Général sur la Protection des Données&amp;quot;.  Cette mesure a forcé les organisations à être conforme avec les différentes règles du RGPD. D&amp;#39;où le flood de mail vous informant que celles-ci ont mis à jour leur politique de confidentialité.&lt;/p&gt;
&lt;!-- end paragraph c69f54aa-340c-417d-b303-d73202727fe4--&gt;
&lt;!-- begin paragraph f7074d6f-17db-42f6-871f-7367c9d37743--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f7074d6f-17db-42f6-871f-7367c9d37743&quot;&gt;Comme toutes les entreprises centrées sur la data (mais pas uniquement, comme nous le verrons), Univalence se doit d&amp;#39;en savoir plus sur l&amp;#39;application du RGPD et ses conséquences. Mais pourquoi avoir mis en place ce règlement ? En quoi consiste ces règles ? Qui est concerné ? Si vous vous posez ces questions et autres, vous êtes au bon endroit.&lt;/p&gt;
&lt;!-- end paragraph f7074d6f-17db-42f6-871f-7367c9d37743--&gt;
&lt;!-- begin paragraph ba2f22e7-85d3-4ff3-97c9-1e35c5d993a6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba2f22e7-85d3-4ff3-97c9-1e35c5d993a6&quot;&gt;C&amp;#39;est parti !&lt;/p&gt;
&lt;!-- end paragraph ba2f22e7-85d3-4ff3-97c9-1e35c5d993a6--&gt;
&lt;!-- begin heading_1 1b79943b-e1f5-46e6-bf6c-70af65706690--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1b79943b-e1f5-46e6-bf6c-70af65706690&quot;&gt;Every breath you take [...] I&amp;#39;ll be watching you&lt;/h2&gt;
&lt;!-- end heading_1 1b79943b-e1f5-46e6-bf6c-70af65706690--&gt;
&lt;!-- begin paragraph a0ab7c8f-1a4b-4a21-9155-c1e30975132a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a0ab7c8f-1a4b-4a21-9155-c1e30975132a&quot;&gt;Pour bien comprendre d&amp;#39;où vient le RGPD, il faut remonter presque 50 ans en arrière. En 1974, le projet SAFARI (non, pas le navigateur Web) vise à répertorier et identifier chaque individu en France. En bref, les Français commencent à s&amp;#39;inquiéter de cette collecte de données et le font savoir : le projet ne verra jamais le jour. Cependant, cela incite le gouvernement à créer la CNIL (Commission nationale de l&amp;#39;informatique et des libertés) en 1978, dont une des missions était (et reste toujours à ce jour) de protéger les données personnelles. &lt;b&gt;C&amp;#39;est le début de la protection des données personnelles en France.&lt;/b&gt; Par la suite, la directive européenne en 1995 ajoute une couche de protection aux états européens en reprenant les principes et concepts Français de protection des données.&lt;/p&gt;
&lt;!-- end paragraph a0ab7c8f-1a4b-4a21-9155-c1e30975132a--&gt;
&lt;!-- begin paragraph f0b6b891-b665-4e40-ac53-cc8d973912d5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f0b6b891-b665-4e40-ac53-cc8d973912d5&quot;&gt;Voilà qui devrait suffire à réglementer et protéger nos données à caractère personnel, non ?&lt;/p&gt;
&lt;!-- end paragraph f0b6b891-b665-4e40-ac53-cc8d973912d5--&gt;
&lt;!-- begin paragraph f6211696-d0b5-4e2f-b638-dbe58c175f98--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f6211696-d0b5-4e2f-b638-dbe58c175f98&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph f6211696-d0b5-4e2f-b638-dbe58c175f98--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/e7269398-21e3-404d-910e-c4fbdc88d6de/aot_data.jpg&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;                                                                    Nope&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 23807f5f-b784-4d69-b499-a1f7b1efab22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-23807f5f-b784-4d69-b499-a1f7b1efab22&quot;&gt;Avec l&amp;#39;apparition d&amp;#39;Internet, l&amp;#39;augmentation de la quantité de données, du e-commerce, de la biométrie, des réseaux sociaux, de l&amp;#39;affaire Snowden et autres joyeuseries technologiques, les risques pour les droits et libertés (données confidentielles, contrôle, censure), la vie privée, ainsi que les risques de manipulations (profilage) ont fortement augmentés.&lt;/p&gt;
&lt;!-- end paragraph 23807f5f-b784-4d69-b499-a1f7b1efab22--&gt;
&lt;!-- begin paragraph 3a05dbef-b108-4b4c-9dce-53e34dd9b422--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3a05dbef-b108-4b4c-9dce-53e34dd9b422&quot;&gt;Face à cette situation, le RGPD a été adopté en 2016 et appliqué en mai 2018.&lt;/p&gt;
&lt;!-- end paragraph 3a05dbef-b108-4b4c-9dce-53e34dd9b422--&gt;
&lt;!-- begin paragraph 12f0b356-59c1-480c-af7d-35c0dc42f540--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-12f0b356-59c1-480c-af7d-35c0dc42f540&quot;&gt;Yay !&lt;/p&gt;
&lt;!-- end paragraph 12f0b356-59c1-480c-af7d-35c0dc42f540--&gt;
&lt;!-- begin heading_1 803c43c1-858d-4f7f-9aac-4c8191a1e1cc--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-803c43c1-858d-4f7f-9aac-4c8191a1e1cc&quot;&gt;En quoi ça consiste en gros ?&lt;/h2&gt;
&lt;!-- end heading_1 803c43c1-858d-4f7f-9aac-4c8191a1e1cc--&gt;
&lt;!-- begin paragraph 4e2e910c-d7a0-43da-a487-b54e45118ba2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e2e910c-d7a0-43da-a487-b54e45118ba2&quot;&gt;Le RGPD représente une grosse MAJ sur tout ce qui est protection des données. On a principalement :&lt;/p&gt;
&lt;!-- end paragraph 4e2e910c-d7a0-43da-a487-b54e45118ba2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Un renforcement &lt;b&gt;quantitatif&lt;/b&gt; et &lt;b&gt;qualitatif&lt;/b&gt; des droits des personnes.&lt;/li&gt;&lt;li&gt;Un &lt;b&gt;enrichissement&lt;/b&gt; (ajout, précision) des notions clés (&lt;b&gt;données à caractère personnel&lt;/b&gt;,&lt;b&gt; responsable traitement&lt;/b&gt;, &lt;b&gt;sous-traitant&lt;/b&gt;...).&lt;/li&gt;&lt;li&gt;Un renforcement du &lt;b&gt;pouvoir de sanction&lt;/b&gt; des CNIL européennes.&lt;/li&gt;&lt;li&gt;Une nouvelle logique de responsabilité.&lt;/li&gt;&lt;li&gt;Une fixation des règles de &lt;b&gt;traitement des données&lt;/b&gt; (collecte, traitement / exploitation, stockage).&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 8841f32c-bb10-4ce6-ad79-b5c760aee0cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8841f32c-bb10-4ce6-ad79-b5c760aee0cf&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8841f32c-bb10-4ce6-ad79-b5c760aee0cf--&gt;
&lt;!-- begin paragraph 3a43ebed-4a2a-444c-b8ca-5580fc2814e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3a43ebed-4a2a-444c-b8ca-5580fc2814e0&quot;&gt;Hmm c&amp;#39;est bien beau tout ça, mais qu&amp;#39;est-ce qui définit des &lt;b&gt;données à caractère personnel&lt;/b&gt; à votre avis ? (Parce que c&amp;#39;est bien là le sujet du problème !) Pouvez-vous me donner un exemple ?&lt;/p&gt;
&lt;!-- end paragraph 3a43ebed-4a2a-444c-b8ca-5580fc2814e0--&gt;
&lt;!-- begin paragraph e5345299-7a3d-42a0-8f6f-1270db8aa5ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5345299-7a3d-42a0-8f6f-1270db8aa5ed&quot;&gt;Si vous avez répondu le nom, le prénom, une photo du visage, le numéro client de chez SFR/Bouygues/Free/Orange..., vous avez raison ! Les données à caractère personnel représentent toute information (ou combinaison d&amp;#39;informations) se rapportant à une personne physique identifiée ou identifiable, qu&amp;#39;elle le soit directement (nom, photo, prénom...) ou indirectement (numéro client, numéro téléphone, pseudonyme...). C&amp;#39;est donc tout cela que vise à réglementer le RGPD.&lt;/p&gt;
&lt;!-- end paragraph e5345299-7a3d-42a0-8f6f-1270db8aa5ed--&gt;
&lt;!-- begin paragraph 062ce859-21ce-4d3c-9ab4-80b6936b1e7a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-062ce859-21ce-4d3c-9ab4-80b6936b1e7a&quot;&gt;Lorsqu&amp;#39;on parle des &lt;b&gt;traitements de données&lt;/b&gt;, ils concernent aussi bien ceux mis en place pour le fonctionnement interne (tout ce qui est RH par exemple) que ceux mis en place pour les activités (information clients, BDD...). Donc ce n&amp;#39;est pas que ceux travaillant dans la data qui sont concernés.&lt;/p&gt;
&lt;!-- end paragraph 062ce859-21ce-4d3c-9ab4-80b6936b1e7a--&gt;
&lt;!-- begin heading_1 9cf48c25-5b14-4b70-9539-eb9a489f8d62--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-9cf48c25-5b14-4b70-9539-eb9a489f8d62&quot;&gt;À qui s&amp;#39;adresse le RGPD justement ?&lt;/h2&gt;
&lt;!-- end heading_1 9cf48c25-5b14-4b70-9539-eb9a489f8d62--&gt;
&lt;!-- begin paragraph 2267e39d-1c26-43f2-950b-9f06c765feaf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2267e39d-1c26-43f2-950b-9f06c765feaf&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2267e39d-1c26-43f2-950b-9f06c765feaf--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/e7269398-21e3-404d-910e-c4fbdc88d6de/everyone.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 2781fa83-3583-46e0-a12d-fcfa624d7ee1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2781fa83-3583-46e0-a12d-fcfa624d7ee1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2781fa83-3583-46e0-a12d-fcfa624d7ee1--&gt;
&lt;!-- begin paragraph 247bd31e-9362-44cb-bc39-b4a2d7796ae3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-247bd31e-9362-44cb-bc39-b4a2d7796ae3&quot;&gt;... Non, en fait ce n&amp;#39;est pas vrai :)&lt;/p&gt;
&lt;!-- end paragraph 247bd31e-9362-44cb-bc39-b4a2d7796ae3--&gt;
&lt;!-- begin paragraph e9b71385-f0f3-491c-96f8-351b736821dc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e9b71385-f0f3-491c-96f8-351b736821dc&quot;&gt;Le RGPD s&amp;#39;addresse à tous les organismes (privés ou publics) quels que soient leur taille, qui sont &lt;b&gt;dans l&amp;#39;UE (que le traitement des données ait lieu ou non dans l&amp;#39;UE) &lt;/b&gt;ou dont leur&lt;b&gt; activité cible des personnes dans l&amp;#39;UE&lt;/b&gt;. En bref, ça concerne beaucoup d&amp;#39;organismes.&lt;/p&gt;
&lt;!-- end paragraph e9b71385-f0f3-491c-96f8-351b736821dc--&gt;
&lt;!-- begin paragraph 140c9d54-866b-4f10-86ba-22d910dc8efa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-140c9d54-866b-4f10-86ba-22d910dc8efa&quot;&gt;Plus haut, nous avons évoqué le responsable de traitement et le sous-traitant. Un organisme peut être soit l&amp;#39;un, soit l&amp;#39;autre. Il est :&lt;/p&gt;
&lt;!-- end paragraph 140c9d54-866b-4f10-86ba-22d910dc8efa--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Responsable de traitement&lt;/b&gt; (dit RT) s&amp;#39;il est responsable du pourquoi et comment du traitement. La question du pourquoi est particulièrement importante car c&amp;#39;est elle qui définit la &lt;b&gt;finalité&lt;/b&gt; : elle fait le lien entre les données, le traitement, et les organismes acteurs.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Sous-traitant&lt;/b&gt; (dit ST) s&amp;#39;il traite des données personnelles pour le compte d&amp;#39;un RT&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f71e905e-2180-40d2-ac30-571d016efe7f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f71e905e-2180-40d2-ac30-571d016efe7f&quot;&gt;De manière générale, c&amp;#39;est sur le RT que repose la responsabilité et le respect des règles.&lt;/p&gt;
&lt;!-- end paragraph f71e905e-2180-40d2-ac30-571d016efe7f--&gt;
&lt;!-- begin paragraph 345951c6-7362-41dd-9e17-9ba9cd87acd9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-345951c6-7362-41dd-9e17-9ba9cd87acd9&quot;&gt;En pratique (mais pas dans la théorie), il s&amp;#39;agira :&lt;/p&gt;
&lt;!-- end paragraph 345951c6-7362-41dd-9e17-9ba9cd87acd9--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;de son dirigeant pour le secteur privé (président, directeur général, PDG...)&lt;/li&gt;&lt;li&gt;du ministre, du maire, du conseil départemental... pour le secteur public.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 3ddecf72-67cf-4793-8a09-970a9a44dcc2--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-3ddecf72-67cf-4793-8a09-970a9a44dcc2&quot;&gt;TL;DR&lt;/h2&gt;
&lt;!-- end heading_1 3ddecf72-67cf-4793-8a09-970a9a44dcc2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Big Data signifie big changement pour les données persos.&lt;/li&gt;&lt;li&gt;Le RGPD représente une MAJ de la réglementation des données persos.&lt;/li&gt;&lt;li&gt;Il s&amp;#39;adresse à tous les organismes dans l&amp;#39;UE ou dont leur activité cible des personnes dans l&amp;#39;UE.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 1e5e2a18-6023-4016-bbc1-8497e2f5931c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1e5e2a18-6023-4016-bbc1-8497e2f5931c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1e5e2a18-6023-4016-bbc1-8497e2f5931c--&gt;
&lt;!-- begin paragraph 3d14405b-203a-466a-a194-f3f2fe0c7a6c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d14405b-203a-466a-a194-f3f2fe0c7a6c&quot;&gt;Dans un article suivant, nous verrons comment en pratique traiter le RGPD.&lt;/p&gt;
&lt;!-- end paragraph 3d14405b-203a-466a-a194-f3f2fe0c7a6c--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:ef072e54e62149ddae7d22ec485e4b63</id>
    <title>Transparence référentielle - I : la perte de la prédictibilité</title>
    <updated>2019-06-06T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/transparence-referentielle-i-la-perte-de-la-predictibilite.html"/>
    <!--summary La transparence référentielle en quelques mots une expression qui peut dans toute situation être échangé par son résultat et en un article voilà ce que ça donne.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Programmation fonctionnelle"></category>    <category term="Transparence référentielle"></category>    <category term="Effet"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin paragraph 9ce86068-62aa-444c-b2c1-1cf3e0b722b4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9ce86068-62aa-444c-b2c1-1cf3e0b722b4&quot;&gt;La transparence référentielle est ce que vous recherchez lorsque vous développez. L&amp;#39;immutabilité ? Le déterminisme ? C&amp;#39;est dépassé ! La transparence référentielle est le spécialiste des problèmes techniques. Elle va vous aider à retrouver le plaisir de coder. Résolution de la dette technique, succès social, réussite dans vos projets. Les résultats sont immédiats.&lt;/p&gt;
&lt;!-- end paragraph 9ce86068-62aa-444c-b2c1-1cf3e0b722b4--&gt;
&lt;!-- begin paragraph 05df65c6-204f-4a23-93dd-1baf2f8e3beb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-05df65c6-204f-4a23-93dd-1baf2f8e3beb&quot;&gt;Dans cette première partie, nous allons voir ce à quoi correspond la transparence référentielle et différents use cases. Puis, nous allons parler de son pire ennemi : les effets.&lt;/p&gt;
&lt;!-- end paragraph 05df65c6-204f-4a23-93dd-1baf2f8e3beb--&gt;
&lt;!-- begin heading_1 08a87bf4-812a-4666-b8ad-cdd263707875--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-08a87bf4-812a-4666-b8ad-cdd263707875&quot;&gt;Définition&lt;/h2&gt;
&lt;!-- end heading_1 08a87bf4-812a-4666-b8ad-cdd263707875--&gt;
&lt;!-- begin paragraph 66b404bf-2427-4256-ac58-774a86ae7779--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-66b404bf-2427-4256-ac58-774a86ae7779&quot;&gt;Selon &lt;a href=&quot;https://fr.wikipedia.org/wiki/Transparence_r%C3%A9f%C3%A9rentielle&quot;&gt;Wikipédia&lt;/a&gt;, une expression est &lt;b&gt;référentiellement transparente&lt;/b&gt; si elle peut être remplacée par sa valeur sans changer le comportement du programme (c&amp;#39;est-à-dire que le programme a les mêmes effets et les mêmes sorties pour les mêmes entrées, quel que soit son contexte d&amp;#39;exécution). À l&amp;#39;inverse, une expression est &lt;b&gt;référentiellement opaque&lt;/b&gt; si elle n&amp;#39;est pas référentiellement transparente.&lt;/p&gt;
&lt;!-- end paragraph 66b404bf-2427-4256-ac58-774a86ae7779--&gt;
&lt;!-- begin paragraph 12052e0f-1a7d-41aa-858e-663dcb0ffc22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-12052e0f-1a7d-41aa-858e-663dcb0ffc22&quot;&gt;En dehors de l’enthousiasme qu&amp;#39;elle semble susciter, comment cette chimère académique peut-elle avoir le moindre intérêt ?&lt;/p&gt;
&lt;!-- end paragraph 12052e0f-1a7d-41aa-858e-663dcb0ffc22--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;https://i.imgflip.com/1lu8cl.jpg&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;La transparence + référentielle quand on ne sait pas ce que c&amp;#39;est !&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 4e327c8f-83af-4670-ae2e-c6c0c78be4cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e327c8f-83af-4670-ae2e-c6c0c78be4cc&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4e327c8f-83af-4670-ae2e-c6c0c78be4cc--&gt;
&lt;!-- begin paragraph 95a60fdc-4136-4eda-a719-f432c897b2c2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-95a60fdc-4136-4eda-a719-f432c897b2c2&quot;&gt;Bien qu&amp;#39;elle n&amp;#39;aide pas forcément tant que ça, cette définition fait apparaître la notion de &amp;quot;comportement&amp;quot; et le fait qu&amp;#39;on ne change pas ce comportement. Ce qui est intéressant, puisque c&amp;#39;est à la base du &lt;a href=&quot;https://fr.wikipedia.org/wiki/R%C3%A9usinage_de_code&quot;&gt;refactoring&lt;/a&gt; (la &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_refactoring&quot;&gt;version anglaise de cette notion&lt;/a&gt; est un peu plus parlante). Nous allons voir que les deux termes sont intimement liés et que nous allons trouver dans la transparence référentielle des conditions pour améliorer la lisibilité du code, sa maintenabilité et sa testabilité. Nous avons donc là un concept digne d&amp;#39;être connu par les partisans du &lt;i&gt;software crafting&lt;/i&gt;, qui nous amène à nous poser de bonnes questions, tout en faisant (indirectement) la promotion de la programmation fonctionnelle pure.&lt;/p&gt;
&lt;!-- end paragraph 95a60fdc-4136-4eda-a719-f432c897b2c2--&gt;
&lt;!-- begin heading_1 914a5221-e2a4-45e1-a2d5-d92ea338c34f--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-914a5221-e2a4-45e1-a2d5-d92ea338c34f&quot;&gt;Use cases&lt;/h2&gt;
&lt;!-- end heading_1 914a5221-e2a4-45e1-a2d5-d92ea338c34f--&gt;
&lt;!-- begin paragraph ffdca5cd-1aa9-48b1-a476-9d147f97bc8c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ffdca5cd-1aa9-48b1-a476-9d147f97bc8c&quot;&gt;Pour commencer, nous allons partir à l&amp;#39;inverse de la définition vue plus haut : nous avons des valeurs et nous allons les remplacer par des expressions. Voici une expression où une valeur est répétée.&lt;/p&gt;
&lt;!-- end paragraph ffdca5cd-1aa9-48b1-a476-9d147f97bc8c--&gt;
&lt;!-- begin code 0ff545ba-83c9-440e-8688-c19a1c0ef025--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0ff545ba-83c9-440e-8688-c19a1c0ef025&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;List(42, 42, 42)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0ff545ba-83c9-440e-8688-c19a1c0ef025--&gt;
&lt;!-- begin paragraph 38dfbde1-9b3f-4cc6-ad00-d4ed76da5646--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38dfbde1-9b3f-4cc6-ad00-d4ed76da5646&quot;&gt;Par refactoring, cette expression est équivalente à :&lt;/p&gt;
&lt;!-- end paragraph 38dfbde1-9b3f-4cc6-ad00-d4ed76da5646--&gt;
&lt;!-- begin code e39aec3c-54e7-4106-bbad-bbea6247eeef--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e39aec3c-54e7-4106-bbad-bbea6247eeef&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val lifeTheUniverseAndEverythingElse = 42

List(
  lifeTheUniverseAndEverythingElse,
  lifeTheUniverseAndEverythingElse,
  lifeTheUniverseAndEverythingElse)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e39aec3c-54e7-4106-bbad-bbea6247eeef--&gt;
&lt;!-- begin paragraph 9135c48c-93ce-41c3-99a4-3e9b6d6f7e56--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9135c48c-93ce-41c3-99a4-3e9b6d6f7e56&quot;&gt;Nous avons un cas classique où par transparence référentielle, on peut passer par la création d&amp;#39;une constante pour donner un sens à un &lt;a href=&quot;https://fr.wikipedia.org/wiki/Nombre_magique_(programmation)#Constantes_num%C3%A9riques_non_nomm%C3%A9es&quot;&gt;nombre magique&lt;/a&gt;, sans changer le comportement de ces lignes de code.&lt;/p&gt;
&lt;!-- end paragraph 9135c48c-93ce-41c3-99a4-3e9b6d6f7e56--&gt;
&lt;!-- begin paragraph 06402fb5-d573-444d-92e6-21b3c4e3e4b5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06402fb5-d573-444d-92e6-21b3c4e3e4b5&quot;&gt;Nous pouvons faire le même exercice avec un chaînage d&amp;#39;appel de méthode. L&amp;#39;exemple ci-dessous est un cas typique de &lt;i&gt;oneline&lt;/i&gt;, c&amp;#39;est-à-dire écrire tout une expression virtuellement en une seule ligne ou en un seul chaînage d&amp;#39;appels. Malheureusement, cette pratique ne facilite pas la lecture du code.&lt;/p&gt;
&lt;!-- end paragraph 06402fb5-d573-444d-92e6-21b3c4e3e4b5--&gt;
&lt;!-- begin code f747fd03-a189-4f9d-b883-ea43ae3d210a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f747fd03-a189-4f9d-b883-ea43ae3d210a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val wordCounts =
  myText
    .split(&amp;quot;[\r\n]+&amp;quot;)
    .flatMap(line =&amp;gt; line.split(&amp;quot;\\s+&amp;quot;))
    .groupBy(word =&amp;gt; word)
    .mapValues(occurrences =&amp;gt; occurrences.size())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f747fd03-a189-4f9d-b883-ea43ae3d210a--&gt;
&lt;!-- begin paragraph 05c9d425-75fc-476b-83a2-38414abe92d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-05c9d425-75fc-476b-83a2-38414abe92d8&quot;&gt;Par transparence référentielle, il est possible de refactorer le code ci-dessus en introduisant au choix une variable intermédiaire ou une fonction, à nouveau sans changer de comportement.&lt;/p&gt;
&lt;!-- end paragraph 05c9d425-75fc-476b-83a2-38414abe92d8--&gt;
&lt;!-- begin code 206ed8da-5ba5-4b6c-a296-dcc2577d8c53--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-206ed8da-5ba5-4b6c-a296-dcc2577d8c53&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def wordsIn(text: String): Array[String] =
  text
    .split(&amp;quot;[\r\n]+&amp;quot;)
    .flatMap(line =&amp;gt; line.split(&amp;quot;\\s+&amp;quot;))

val wordCounts =
  wordIn(myText)
    .groupBy(word =&amp;gt; word)
    .mapValues(occurrences =&amp;gt; occurrences.size())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 206ed8da-5ba5-4b6c-a296-dcc2577d8c53--&gt;
&lt;!-- begin paragraph a894a50a-c69e-4f47-824c-ebe9fb6fc4c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a894a50a-c69e-4f47-824c-ebe9fb6fc4c3&quot;&gt;Le nom ici permet de donner un plus de sens au code, d&amp;#39;en clarifier l&amp;#39;intention.&lt;/p&gt;
&lt;!-- end paragraph a894a50a-c69e-4f47-824c-ebe9fb6fc4c3--&gt;
&lt;!-- begin paragraph fa4f59d3-d8ca-430b-a88f-c99e69c1c8f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa4f59d3-d8ca-430b-a88f-c99e69c1c8f6&quot;&gt;La notion de &amp;quot;changer de comportement&amp;quot; est plutôt subjectif et va potentiellement varier en fonction du contexte. Au minimum, il va s&amp;#39;agir de s&amp;#39;intéresser uniquement au résultat de ces expressions. Mais si on est sur un projet où l&amp;#39;on doit porter une attention aux performances, alors cette performance perçue fait partie du comportement et il est possible de constater une différence de comportement dans les différentes solutions vues plus haut. Dans ce cas et dans ce cas uniquement les différentes alternatives ne sont pas référentiellement transparentes.&lt;/p&gt;
&lt;!-- end paragraph fa4f59d3-d8ca-430b-a88f-c99e69c1c8f6--&gt;
&lt;!-- begin heading_1 f28a712c-0a75-4314-9be1-46cd94978457--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f28a712c-0a75-4314-9be1-46cd94978457&quot;&gt;Anti use cases&lt;/h2&gt;
&lt;!-- end heading_1 f28a712c-0a75-4314-9be1-46cd94978457--&gt;
&lt;!-- begin paragraph 2718e5d5-fe79-4467-9125-0d90d63e8a39--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2718e5d5-fe79-4467-9125-0d90d63e8a39&quot;&gt;Voyons d&amp;#39;autres cas moins anecdotiques où la transparence référentielle ne fonctionne pas.&lt;/p&gt;
&lt;!-- end paragraph 2718e5d5-fe79-4467-9125-0d90d63e8a39--&gt;
&lt;!-- begin code bfc99806-11c8-42e2-828a-b2e7c3094d0a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bfc99806-11c8-42e2-828a-b2e7c3094d0a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def makeCoffee(flavour: Flavour): Coffee = {
  launchNuke(&amp;quot;HAPPY THERMONUCLEAR WAR&amp;quot;) // wow such benevolent

  Coffee.of(flavour)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bfc99806-11c8-42e2-828a-b2e7c3094d0a--&gt;
&lt;!-- begin paragraph 7c9e468f-4a7b-476a-9ebb-4384da7e54cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7c9e468f-4a7b-476a-9ebb-4384da7e54cf&quot;&gt;Si le principe de transparence référentielle est respecté, on devrait avoir une équivalence entre les deux parties du code ci-dessous.&lt;/p&gt;
&lt;!-- end paragraph 7c9e468f-4a7b-476a-9ebb-4384da7e54cf--&gt;
&lt;!-- begin code d3c4e294-32b2-451e-abdd-9c778e5f96bd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d3c4e294-32b2-451e-abdd-9c778e5f96bd&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;List(makeCoffee(Expresso), makeCoffee(Expresso), makeCoffee(Expresso))

// vs

val expresso = makeCoffee(Expresso)
List(expresso, expresso, expresso)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d3c4e294-32b2-451e-abdd-9c778e5f96bd--&gt;
&lt;!-- begin paragraph 22e841e7-179a-4648-935b-c887468dfe93--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-22e841e7-179a-4648-935b-c887468dfe93&quot;&gt;Or, les deux parties dans ce code n&amp;#39;ont pas le même comportement. En effet, la première ligne de code envoie trois ogives nucléaires, alors que les deux suivantes en envoient qu&amp;#39;une seule. (Bon. Lancer une tête nucléaire ou trois, en terme de résultat... comment dire ?)&lt;/p&gt;
&lt;!-- end paragraph 22e841e7-179a-4648-935b-c887468dfe93--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;https://i.imgflip.com/32seu1.jpg&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Le Dr Folamour approuve l&amp;#39;opacité référentielle.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph bee9be36-4989-4095-835d-821830888649--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bee9be36-4989-4095-835d-821830888649&quot;&gt;Et on retrouve ça dans des fonctions plus innocentes (... en apparence).&lt;/p&gt;
&lt;!-- end paragraph bee9be36-4989-4095-835d-821830888649--&gt;
&lt;!-- begin code f153229a-5d49-4d76-81d1-4a85d4fcb601--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f153229a-5d49-4d76-81d1-4a85d4fcb601&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def commit(contract: Contract): Contract = {
  LocalDateTime now = LocalDateTime.now()

  contract
    .sign
    .timestamp(now)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f153229a-5d49-4d76-81d1-4a85d4fcb601--&gt;
&lt;!-- begin paragraph 75feabc6-5c1d-4e89-a984-4fb85adec443--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75feabc6-5c1d-4e89-a984-4fb85adec443&quot;&gt;Deux appels successifs à &lt;code&gt;commit&lt;/code&gt; avec le même contrat peut donner des contrats avec un timestamp différent. Ce qui ne facilite pas la mise en place de tests, car il faudrait changer l&amp;#39;heure du système pour pouvoir retrouver un contexte dans lequel la fonction &lt;code&gt;commit&lt;/code&gt; a un comportement prédictif.&lt;/p&gt;
&lt;!-- end paragraph 75feabc6-5c1d-4e89-a984-4fb85adec443--&gt;
&lt;!-- begin paragraph 5d3595fd-3c6a-4477-851b-38dfef68b487--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5d3595fd-3c6a-4477-851b-38dfef68b487&quot;&gt;Comparons aussi cette fonction :&lt;/p&gt;
&lt;!-- end paragraph 5d3595fd-3c6a-4477-851b-38dfef68b487--&gt;
&lt;!-- begin code 177f5b7a-91ea-4f9e-a5a9-ba0b9f919113--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-177f5b7a-91ea-4f9e-a5a9-ba0b9f919113&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def intSqrt_1(n: Int): Int {
  if (n &amp;lt; 0) throw new IllegalArgumentException()

  // ... do computation here ...

  result
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 177f5b7a-91ea-4f9e-a5a9-ba0b9f919113--&gt;
&lt;!-- begin paragraph a7e85e8d-3cbe-4f6e-856a-085e7cfd59a6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a7e85e8d-3cbe-4f6e-856a-085e7cfd59a6&quot;&gt;Avec celle-ci :&lt;/p&gt;
&lt;!-- end paragraph a7e85e8d-3cbe-4f6e-856a-085e7cfd59a6--&gt;
&lt;!-- begin code bb2016a3-f943-4d6c-ab3c-5a0815f62c94--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bb2016a3-f943-4d6c-ab3c-5a0815f62c94&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def intSqrt_2(n: Int): Option[Int] =
  if (n &amp;lt; 0) None
  else {
    // ... do computation here ...

    Some(result)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bb2016a3-f943-4d6c-ab3c-5a0815f62c94--&gt;
&lt;!-- begin paragraph 8ba288ec-4104-47e1-aa50-6ef87224a1f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8ba288ec-4104-47e1-aa50-6ef87224a1f0&quot;&gt;Selon la version de intSqrt utilisée, le résultat de l&amp;#39;expression suivante sera différente.&lt;/p&gt;
&lt;!-- end paragraph 8ba288ec-4104-47e1-aa50-6ef87224a1f0--&gt;
&lt;!-- begin code 1900d76a-8966-4492-a505-f5ddfab52db4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1900d76a-8966-4492-a505-f5ddfab52db4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;List(intSqrt(-1), intSqrt(1), intSqrt(-1))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1900d76a-8966-4492-a505-f5ddfab52db4--&gt;
&lt;!-- begin paragraph 46dacb5d-b8eb-46e6-9f31-aba6df3126cd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-46dacb5d-b8eb-46e6-9f31-aba6df3126cd&quot;&gt;Pour &lt;code&gt;intSqrt_1&lt;/code&gt;, on obtient une exception. Pour &lt;code&gt;intSqrt_2&lt;/code&gt;, on obtient la liste suivante : &lt;code&gt;List(None, Some(1), None)&lt;/code&gt;. Libre au développeur par la suite de lancer une exception au premier None rencontré, au second None rencontré ou de filtrer les None et de continuer les traitements.&lt;/p&gt;
&lt;!-- end paragraph 46dacb5d-b8eb-46e6-9f31-aba6df3126cd--&gt;
&lt;!-- begin paragraph 2457a76f-bc9a-4dec-9f00-b8fec1e96953--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2457a76f-bc9a-4dec-9f00-b8fec1e96953&quot;&gt;Quoiqu&amp;#39;il en soit, &lt;code&gt;intSqrt_1&lt;/code&gt; n&amp;#39;est pas référentiellement transparent. La transparence référentielle nécessite d&amp;#39;être dans le cadre d&amp;#39;une expression et de ne traiter qu&amp;#39;avec des valeurs. Or &lt;code&gt;intSqrt_1&lt;/code&gt; ne retourne pas de valeur lorsque le paramètre est négatif et casse le flux de traitement.&lt;/p&gt;
&lt;!-- end paragraph 2457a76f-bc9a-4dec-9f00-b8fec1e96953--&gt;
&lt;!-- begin paragraph 688a369f-ff72-423e-9a81-5edcc15ca73b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-688a369f-ff72-423e-9a81-5edcc15ca73b&quot;&gt;Autrement dit, pour pouvoir respecter la transparence référentielle, nous avons besoin de fonctions pures. Une fonction pure est une fonction déterministe (elle retourne la même valeur pour un même paramètre), totale (elle retourne toujours une valeur quelque soit la valeur permise en entrée) et sans effet.&lt;/p&gt;
&lt;!-- end paragraph 688a369f-ff72-423e-9a81-5edcc15ca73b--&gt;
&lt;!-- begin paragraph e3f1a2d4-7718-4f26-ad80-4a0b34c2c595--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e3f1a2d4-7718-4f26-ad80-4a0b34c2c595&quot;&gt;Les trois exemples précédents montre que ce qui ne permet pas d&amp;#39;être référentiellement transparent, c&amp;#39;est cette notion d&amp;#39;&lt;b&gt;effet&lt;/b&gt;. Nous allons voir ce dont il s&amp;#39;agit.&lt;/p&gt;
&lt;!-- end paragraph e3f1a2d4-7718-4f26-ad80-4a0b34c2c595--&gt;
&lt;!-- begin heading_1 2f9d10f9-9585-407a-8f94-2b9566c097c5--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2f9d10f9-9585-407a-8f94-2b9566c097c5&quot;&gt;Effet&lt;/h2&gt;
&lt;!-- end heading_1 2f9d10f9-9585-407a-8f94-2b9566c097c5--&gt;
&lt;!-- begin paragraph 03fd462a-86d1-47eb-8c46-c964cf18f7d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-03fd462a-86d1-47eb-8c46-c964cf18f7d2&quot;&gt;Les effets représentent un concept fondamental en programmation impérative. Dans ce paradigme de programmation, un programme correspond à une composition d&amp;#39;instructions. Une instruction est un élément du programme qui permet de modifier l&amp;#39;état du système ou de constater son état actuel. Le propre d&amp;#39;un effet est ainsi de modifier ou de constater l&amp;#39;état du système.&lt;/p&gt;
&lt;!-- end paragraph 03fd462a-86d1-47eb-8c46-c964cf18f7d2--&gt;
&lt;!-- begin paragraph 75a5dc62-3a49-48ea-9dff-056306be7391--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-75a5dc62-3a49-48ea-9dff-056306be7391&quot;&gt;Un effet crée un couplage fort entre une application ou un service et un service tier, quelque soit sa nature (OS, service externe, IoT...). Et puis, un effet, c&amp;#39;est plutôt invisible. Ce couplage fort et implicite ne facilite pas :&lt;/p&gt;
&lt;!-- end paragraph 75a5dc62-3a49-48ea-9dff-056306be7391--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;le déterminisme, puisque des valeurs différentes peuvent être retournées pour le même appel (eg. now(), random(), forexService.getRate(&amp;quot;USD&amp;quot;, &amp;quot;EUR&amp;quot;)),&lt;/li&gt;&lt;li&gt;la testabilité, qui a besoin de déterminisme,&lt;/li&gt;&lt;li&gt;le refactoring, par définition avec la transparence référentielle,&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 67a5dcf5-b542-4b99-a0bf-75168d9875d0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67a5dcf5-b542-4b99-a0bf-75168d9875d0&quot;&gt;Et de par la nature même des effets, le code devient plus difficile à analyser, nous devons passer plus de temps pour comprendre l&amp;#39;étendue de ses actions. Ce qui nécessite plus de concentration, requiert plus d&amp;#39;énergie et réduit le courage des développeurs pour attaquer des problèmes avec le code. La notion de courage étant à la base de l&amp;#39;agilité, les effets ne seraient pas &amp;quot;agiles&amp;quot; ?&lt;/p&gt;
&lt;!-- end paragraph 67a5dcf5-b542-4b99-a0bf-75168d9875d0--&gt;
&lt;!-- begin paragraph 73f5904f-b8be-4a7b-b851-28902b5cc6d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-73f5904f-b8be-4a7b-b851-28902b5cc6d8&quot;&gt;Mais les effets sont nécessaires. Dans les applications de la vraie vie, nous avons tous besoin de connaître l&amp;#39;heure et la date actuelle, nous avons tous besoin de stocker et de lire des trucs en base de données, nous avons tous besoin de dialoguer avec un service qui va potentiellement fournir des réponses différentes, même si on lui fournit la même requête. Sans ça, quel utilité donner à notre application ?&lt;/p&gt;
&lt;!-- end paragraph 73f5904f-b8be-4a7b-b851-28902b5cc6d8--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/haskell_2x.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;a href=&quot;https://xkcd.com/1312/&quot;&gt;XKCD 1312&lt;/a&gt; (CC BY-NC 2.5)&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph d9f1b97b-dc84-4d75-8f94-1417345b33f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d9f1b97b-dc84-4d75-8f94-1417345b33f3&quot;&gt;Alors, dans un cadre où les effets sont une nécessité, comment faciliter la testabilité et prédictibilité de notre code ? Comment pouvons-nous mettre en place la transparence référentielle ? C&amp;#39;est ce que nous verrons dans un prochain article.&lt;/p&gt;
&lt;!-- end paragraph d9f1b97b-dc84-4d75-8f94-1417345b33f3--&gt;
&lt;!-- begin paragraph 03d12618-5045-424c-84ff-4488702e7189--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-03d12618-5045-424c-84ff-4488702e7189&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 03d12618-5045-424c-84ff-4488702e7189--&gt;
&lt;!-- begin paragraph 92c087be-eb82-4221-be22-73b977a31676--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-92c087be-eb82-4221-be22-73b977a31676&quot;&gt;Photo : &lt;a href=&quot;https://unsplash.com/photos/B2mq60Ksrsg&quot;&gt;https://unsplash.com/photos/B2mq60Ksrsg&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 92c087be-eb82-4221-be22-73b977a31676--&gt;
&lt;!-- begin bookmark cd2568fa-8472-4575-829c-0d4da01178b1--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-cd2568fa-8472-4575-829c-0d4da01178b1&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;&quot;&gt; &lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark cd2568fa-8472-4575-829c-0d4da01178b1--&gt;
&lt;!-- begin paragraph 5598ab9e-7297-4763-9748-bfe4eea9f638--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5598ab9e-7297-4763-9748-bfe4eea9f638&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5598ab9e-7297-4763-9748-bfe4eea9f638--&gt;
&lt;!-- begin divider 3ed58ba5-f57c-4fc7-a64d-10e7511ac0ab--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-3ed58ba5-f57c-4fc7-a64d-10e7511ac0ab&quot;&gt;
&lt;!-- end divider 3ed58ba5-f57c-4fc7-a64d-10e7511ac0ab--&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/X-cEGEJMx_4?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 555858a4-1a48-4455-916b-f6044d3e53b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-555858a4-1a48-4455-916b-f6044d3e53b7&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 555858a4-1a48-4455-916b-f6044d3e53b7--&gt;
&lt;!-- begin bookmark b2c5d06c-0a8b-43d4-a111-9aafb2be1715--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-b2c5d06c-0a8b-43d4-a111-9aafb2be1715&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://sderosiaux.medium.com/why-referential-transparency-matters-7c179424dab5&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:987/0*1kp19wF42MhzvdBB.jpg&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Why Referential Transparency matters?&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Why Referential Transparency matters? It’s not always obvious to understand what referential transparency is and why it matters. It’s often intertwined with “complex” frameworks, dealing with …&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;&quot;&gt; https://sderosiaux.medium.com/why-referential-transparency-matters-7c179424dab5&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark b2c5d06c-0a8b-43d4-a111-9aafb2be1715--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:25a1c559efc84a02a6b6799ec14956b9</id>
    <title>sbt-dynver : la gestion dynamique des numéros de version</title>
    <updated>2019-06-04T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/sbt-dynver-la-gestion-dynamique-des-numeros-de-version.html"/>
    <!--summary sbt-dynver offre une souplesse et facilité de mise en œuvre d&#039;une application en reléguant notamment la gestion du numéro de version... à Git.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="SBT"></category>    <category term="Scala"></category>    <category term="sbt-dynver"></category>    <category term="Outil"></category>    <content type="html">
&lt;!-- begin paragraph b12e048a-c841-4bf0-b56a-bf6bc8a8d1ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b12e048a-c841-4bf0-b56a-bf6bc8a8d1ed&quot;&gt;La livraison d&amp;#39;une application est souvent une étape qui met du temps à être automatisée. Et malheureusement les outils existants n&amp;#39;aident pas forcément dans ce sens, à moins de parfaitement les maîtriser. Dans ce cadre, &lt;a href=&quot;https://github.com/dwijnand/sbt-dynver&quot;&gt;sbt-dynver&lt;/a&gt; offre une souplesse et facilité de mise en œuvre en reléguant notamment la gestion du numéro de version... à Git. Et ça change pas mal de chose dans le process de livraison !&lt;/p&gt;
&lt;!-- end paragraph b12e048a-c841-4bf0-b56a-bf6bc8a8d1ed--&gt;
&lt;!-- begin heading_1 e3408eb5-c2f8-41e3-9912-3899ab6d0a24--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e3408eb5-c2f8-41e3-9912-3899ab6d0a24&quot;&gt;Numéro de version&lt;/h2&gt;
&lt;!-- end heading_1 e3408eb5-c2f8-41e3-9912-3899ab6d0a24--&gt;
&lt;!-- begin paragraph c6658f6a-76ca-4126-a04e-723c313fa992--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c6658f6a-76ca-4126-a04e-723c313fa992&quot;&gt;La particularité de sbt-dynver réside dans sa façon de numéroter les versions. Cette numérotation varie en fonction de l&amp;#39;état du code au sein de Git. Tout en restant compatible avec &lt;a href=&quot;https://semver.org/&quot;&gt;SemVer&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph c6658f6a-76ca-4126-a04e-723c313fa992--&gt;
&lt;!-- begin paragraph 132af3a6-7346-40a5-be2f-6e35ca720474--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-132af3a6-7346-40a5-be2f-6e35ca720474&quot;&gt;Si au moins un tag sous Git est présent, c&amp;#39;est le dernier tag créé qui préfixera le numéro de version. Il faut utiliser &lt;code&gt;v1.2.3&lt;/code&gt; ou &lt;code&gt;v3.4&lt;/code&gt; comme format de tag ; la lettre &lt;code&gt;v&lt;/code&gt;  est nécessaire et est automatiquement retirée dans le numéro de version.&lt;/p&gt;
&lt;!-- end paragraph 132af3a6-7346-40a5-be2f-6e35ca720474--&gt;
&lt;!-- begin paragraph 97fc846d-532e-4ed0-8273-80719287fd27--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-97fc846d-532e-4ed0-8273-80719287fd27&quot;&gt;Si des commits ont été ajoutés depuis le dernier tag, sbt-dynver ajoutera dans le numéro de version le nombre de commits créés depuis le dernier tag, ainsi que l&amp;#39;ID du dernier commit.&lt;/p&gt;
&lt;!-- end paragraph 97fc846d-532e-4ed0-8273-80719287fd27--&gt;
&lt;!-- begin paragraph 5058c9b9-89f9-413f-9818-e47b36da536d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5058c9b9-89f9-413f-9818-e47b36da536d&quot;&gt;Si des modifications apparaissent, mais n&amp;#39;ont pas été committées, sbt-dynver ajoutera un timestamp.&lt;/p&gt;
&lt;!-- end paragraph 5058c9b9-89f9-413f-9818-e47b36da536d--&gt;
&lt;!-- begin paragraph 9c576fad-06da-47af-875d-eef8b9feb6f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9c576fad-06da-47af-875d-eef8b9feb6f6&quot;&gt;sbt-dynver intervient sur les settings &lt;code&gt;version in ThisBuild&lt;/code&gt;, &lt;code&gt;isSnapshot in ThisBuild&lt;/code&gt; et &lt;code&gt;isVersionStable in ThisBuild&lt;/code&gt;. Voici un tableau présentant les différents formats.&lt;/p&gt;
&lt;!-- end paragraph 9c576fad-06da-47af-875d-eef8b9feb6f6--&gt;
&lt;!-- begin paragraph e9704ad4-114a-40f2-9624-cc169354588c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e9704ad4-114a-40f2-9624-cc169354588c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e9704ad4-114a-40f2-9624-cc169354588c--&gt;
&lt;!-- begin table 46156ac3-baac-46ac-845e-73e90c70758a--&gt;
&lt;table class=&quot;block-table level-&quot; id=&quot;t-46156ac3-baac-46ac-845e-73e90c70758a&quot;&gt;  &lt;tbody&gt;
                                                              &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- end table 46156ac3-baac-46ac-845e-73e90c70758a--&gt;
&lt;!-- begin paragraph b0420351-dd3a-4a51-ae2b-c588c87dc372--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b0420351-dd3a-4a51-ae2b-c588c87dc372&quot;&gt;sbt-dynver donne ainsi plus de précisions sur l&amp;#39;état du projet, même lorsque le produit est livré. Le plugin permet d&amp;#39;indiquer ainsi s&amp;#39;il s&amp;#39;agit d&amp;#39;une version officielle (format &lt;code&gt;&amp;lt;tag&amp;gt;&lt;/code&gt;), d&amp;#39;une version en cours de développement comme pour les snapshots/canary releases/nightly builds (format &lt;code&gt;&amp;lt;tag&amp;gt;+&amp;lt;delta&amp;gt;-&amp;lt;last_commit_id&amp;gt;&lt;/code&gt;) ou d&amp;#39;une version livrée en urgence sans passer par les process de livraison habituels (formats incluant le &lt;code&gt;&amp;lt;timestamp&amp;gt;&lt;/code&gt;). Cette numérotation facilite la mise en place de process de livraison continue.&lt;/p&gt;
&lt;!-- end paragraph b0420351-dd3a-4a51-ae2b-c588c87dc372--&gt;
&lt;!-- begin heading_1 97f42d7a-40ac-4165-ae1f-8c6b0ea5ed1a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-97f42d7a-40ac-4165-ae1f-8c6b0ea5ed1a&quot;&gt;Livraison&lt;/h2&gt;
&lt;!-- end heading_1 97f42d7a-40ac-4165-ae1f-8c6b0ea5ed1a--&gt;
&lt;!-- begin paragraph eab9296d-b361-414d-b169-eda7c6abbe74--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eab9296d-b361-414d-b169-eda7c6abbe74&quot;&gt;Le process de livraison permis par sbt-dynver est d&amp;#39;ailleurs plus simple. Il permet de se passer de la déclaration du numéro de version dans le fichier &lt;code&gt;build.sbt&lt;/code&gt; ou dans le fichier &lt;code&gt;version.sbt&lt;/code&gt;, comme le recommande de son côté sbt-release. En fait, il ne faut surtout pas déclarer de version.&lt;/p&gt;
&lt;!-- end paragraph eab9296d-b361-414d-b169-eda7c6abbe74--&gt;
&lt;!-- begin paragraph f7798490-991b-40ab-b62e-8cae65a2389f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f7798490-991b-40ab-b62e-8cae65a2389f&quot;&gt;Supposons maintenant que vous ayez un serveur d&amp;#39;intégration continue à l&amp;#39;écoute des push sur votre repo Git. Et que le workflow qui se déclenche passe par un &lt;code&gt;sbt publish&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph f7798490-991b-40ab-b62e-8cae65a2389f--&gt;
&lt;!-- begin paragraph 6edac279-ddb7-47ac-bb96-58ec5305bac4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6edac279-ddb7-47ac-bb96-58ec5305bac4&quot;&gt;Du coup, pour livrer une version officielle, vous devez faire :&lt;/p&gt;
&lt;!-- end paragraph 6edac279-ddb7-47ac-bb96-58ec5305bac4--&gt;
&lt;!-- begin code 988cc32b-5048-40ca-b423-8119923f2f29--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-988cc32b-5048-40ca-b423-8119923f2f29&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ git tag v1.2
$ git push --follow-tags&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 988cc32b-5048-40ca-b423-8119923f2f29--&gt;
&lt;!-- begin paragraph 781407ed-caf2-4b71-a1f8-0dc278336394--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-781407ed-caf2-4b71-a1f8-0dc278336394&quot;&gt;Si vous avez fait &lt;code&gt;git config --global push.followTags true&lt;/code&gt;, l&amp;#39;option &lt;code&gt;--follow-tags&lt;/code&gt; n&amp;#39;est pas nécessaire.&lt;/p&gt;
&lt;!-- end paragraph 781407ed-caf2-4b71-a1f8-0dc278336394--&gt;
&lt;!-- begin paragraph f4699c7d-c951-42ca-9c09-2f9f5080cf7a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f4699c7d-c951-42ca-9c09-2f9f5080cf7a&quot;&gt;Le process permis par sbt-dynver diffère du process de livraison à la Maven, dans lequel les divers snapshots ne sont pas différenciés. Il diffère aussi du process proposé par sbt-release dans la mesure où c&amp;#39;est le développeur qui gère les tags de version.&lt;/p&gt;
&lt;!-- end paragraph f4699c7d-c951-42ca-9c09-2f9f5080cf7a--&gt;
&lt;!-- begin paragraph 31c495e3-435a-4b3c-8b4d-8fd5d44e31c4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-31c495e3-435a-4b3c-8b4d-8fd5d44e31c4&quot;&gt;Le plugin &lt;a href=&quot;https://github.com/scalacenter/sbt-release-early&quot;&gt;sbt-release-early&lt;/a&gt; se base sur sbt-dynver pour la gestion des numéros de version. sbt-release-early s&amp;#39;assure en plus que la livraison est faite dans le contexte d&amp;#39;un serveur d&amp;#39;intégration continue et est compatible avec Sonatype OSSRH et JFrog Bintray. C&amp;#39;est intéressant dans un cadre Open Source et permet de livrer souvent, même des versions intermédiaires !&lt;/p&gt;
&lt;!-- end paragraph 31c495e3-435a-4b3c-8b4d-8fd5d44e31c4--&gt;
&lt;!-- begin paragraph 7a8f9b09-9b92-4ded-9151-950abee363ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7a8f9b09-9b92-4ded-9151-950abee363ae&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7a8f9b09-9b92-4ded-9151-950abee363ae--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:8005a4eb1dd94586aff2cb1376738c86</id>
    <title>Les onzes commandements de Jon Pretty (2e partie)</title>
    <updated>2019-05-23T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/les-onzes-commandements-de-jon-pretty-2e-partie.html"/>
    <!--summary -->    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="Scala"></category>    <category term="Jon Pretty"></category>    <content type="html">
&lt;!-- begin paragraph 74b9dd40-3c76-442c-a6b8-b40051129e76--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-74b9dd40-3c76-442c-a6b8-b40051129e76&quot;&gt;Aujourd&amp;#39;hui, suite et fin de l&amp;#39;&lt;a href=&quot;https://blog.univalence.io/les-onzes-commandements-de-jon-pretty/&quot;&gt;article&lt;/a&gt; sur les onzes librairies présentées lors de Scalar 2019 😇&lt;/p&gt;
&lt;!-- end paragraph 74b9dd40-3c76-442c-a6b8-b40051129e76--&gt;
&lt;!-- begin paragraph 6f77ab20-f330-43d0-a9b5-0314eec92fa6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6f77ab20-f330-43d0-a9b5-0314eec92fa6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6f77ab20-f330-43d0-a9b5-0314eec92fa6--&gt;
&lt;!-- begin heading_3 10c45d00-8ef7-47a3-9b72-4218ad418b29--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-10c45d00-8ef7-47a3-9b72-4218ad418b29&quot;&gt;&lt;a href=&quot;https://github.com/propensive/caesura&quot;&gt;Caesura&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 10c45d00-8ef7-47a3-9b72-4218ad418b29--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8005a4eb-1dd9-4586-aff2-cb1376738c86/photo-1513270327160-516b92ed40e9&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 95d11634-1cac-4133-bdf8-9f1539a803a3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-95d11634-1cac-4133-bdf8-9f1539a803a3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 95d11634-1cac-4133-bdf8-9f1539a803a3--&gt;
&lt;!-- begin paragraph e731dbb5-b616-490c-ab0a-dce9f3f9f708--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e731dbb5-b616-490c-ab0a-dce9f3f9f708&quot;&gt;Caesura est une librairie qui vous aide à parser vos CSV en Row puis en collections Scala ou bien directement en case classes, le dernier étant l&amp;#39;intérêt principal de cette librairie. Caesura se basant sur Magnolia, il est possible de parser une ligne de CSV directement dans une case class ayant des membres complexe, à savoir ayant des case classes imbriquées.&lt;/p&gt;
&lt;!-- end paragraph e731dbb5-b616-490c-ab0a-dce9f3f9f708--&gt;
&lt;!-- begin paragraph bd2181e7-9e6c-478f-abeb-116d31d63aa8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd2181e7-9e6c-478f-abeb-116d31d63aa8&quot;&gt;Exemple : &lt;/p&gt;
&lt;!-- end paragraph bd2181e7-9e6c-478f-abeb-116d31d63aa8--&gt;
&lt;!-- begin code 126190e1-70d3-43f4-8ffa-1659c0cb85eb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-126190e1-70d3-43f4-8ffa-1659c0cb85eb&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Address(city: String, zipcode: String)
case class Person(name: String, age: String, gender: String, address: Address)

val csv: String = &amp;quot;&amp;quot;&amp;quot;toto,10,male,Paris,75002&amp;quot;&amp;quot;&amp;quot;
val toto: Person = Csv.parse(csv).as[Person]
println(toto)
//Person(toto,10,male,Address(Paris,75002))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 126190e1-70d3-43f4-8ffa-1659c0cb85eb--&gt;
&lt;!-- begin paragraph b5eb51cf-6f5b-41e6-b2d6-eb54c2e4c7ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b5eb51cf-6f5b-41e6-b2d6-eb54c2e4c7ee&quot;&gt;Ici nous décodons une case class complexe mais nous pouvons aussi aller dans l&amp;#39;autre sens et encoder :&lt;/p&gt;
&lt;!-- end paragraph b5eb51cf-6f5b-41e6-b2d6-eb54c2e4c7ee--&gt;
&lt;!-- begin code a3ccba0e-58db-428a-a4e8-4a156acffa15--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a3ccba0e-58db-428a-a4e8-4a156acffa15&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val csv: String = Row.from(
      Person(&amp;quot;toto&amp;quot;,&amp;quot;10&amp;quot;,&amp;quot;male&amp;quot;,Address(&amp;quot;Paris&amp;quot;,&amp;quot;75002&amp;quot;))
    ).elems.mkString(&amp;quot;,&amp;quot;)
//toto,10,male,Paris,75002&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a3ccba0e-58db-428a-a4e8-4a156acffa15--&gt;
&lt;!-- begin paragraph 718daf6d-43ab-4447-9f28-4d6ac365a084--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-718daf6d-43ab-4447-9f28-4d6ac365a084&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 718daf6d-43ab-4447-9f28-4d6ac365a084--&gt;
&lt;!-- begin heading_3 d52dd1d6-7d53-4ca8-98ff-9f0431d6f01e--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-d52dd1d6-7d53-4ca8-98ff-9f0431d6f01e&quot;&gt;&lt;a href=&quot;https://github.com/propensive/contextual&quot;&gt;Contextual&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 d52dd1d6-7d53-4ca8-98ff-9f0431d6f01e--&gt;
&lt;!-- begin paragraph 57836175-1971-45a5-84ac-3ed2e0ce377e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-57836175-1971-45a5-84ac-3ed2e0ce377e&quot;&gt;Contextual permet de définir comment vos String sont lues et d&amp;#39;avoir des retours au compile time sur la validité de vos String.&lt;/p&gt;
&lt;!-- end paragraph 57836175-1971-45a5-84ac-3ed2e0ce377e--&gt;
&lt;!-- begin paragraph 81a4d8ee-032e-4d8b-8a49-05f73189ee97--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81a4d8ee-032e-4d8b-8a49-05f73189ee97&quot;&gt;Exemple :&lt;/p&gt;
&lt;!-- end paragraph 81a4d8ee-032e-4d8b-8a49-05f73189ee97--&gt;
&lt;!-- begin code 6819b135-7807-4a41-9daa-14e5874e469e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6819b135-7807-4a41-9daa-14e5874e469e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;email&amp;quot;bourriquet@univalence.io&amp;quot; //La string bien crée car c&amp;#39;est bien un email
bin&amp;quot;11111111&amp;quot; //255
txt&amp;quot;&amp;quot;&amp;quot;Avengers,
|assemble&amp;quot;&amp;quot;&amp;quot; // &amp;quot;Avengers,\nassemble&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6819b135-7807-4a41-9daa-14e5874e469e--&gt;
&lt;!-- begin paragraph ba921f60-f393-433f-b2ba-bb19ebc0c49c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba921f60-f393-433f-b2ba-bb19ebc0c49c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ba921f60-f393-433f-b2ba-bb19ebc0c49c--&gt;
&lt;!-- begin heading_3 d06f5eed-d77c-41dc-855c-c9b64045b9e4--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-d06f5eed-d77c-41dc-855c-c9b64045b9e4&quot;&gt;&lt;a href=&quot;https://github.com/propensive/guillotine&quot;&gt;Guillotine&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 d06f5eed-d77c-41dc-855c-c9b64045b9e4--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8005a4eb-1dd9-4586-aff2-cb1376738c86/photo-1514849670938-e170858106ee&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph dfb18610-89dd-4389-8941-e694e2531b25--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dfb18610-89dd-4389-8941-e694e2531b25&quot;&gt;Mais c’est une révolte ? — Non, Sire, c’est une révolution !&lt;/p&gt;
&lt;!-- end paragraph dfb18610-89dd-4389-8941-e694e2531b25--&gt;
&lt;!-- begin paragraph a8df1475-6668-4840-b0e9-bc39dd1d418a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a8df1475-6668-4840-b0e9-bc39dd1d418a&quot;&gt;Grâce à Guillotine vous pourrez enfin exécuter vos commandes shell de manière sympa :&lt;/p&gt;
&lt;!-- end paragraph a8df1475-6668-4840-b0e9-bc39dd1d418a--&gt;
&lt;!-- begin code a7352c2d-6e59-4682-93d6-eed9f33f75a0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a7352c2d-6e59-4682-93d6-eed9f33f75a0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val thanosQuote = sh&amp;quot;echo I am inevitable&amp;quot;.exec[String]
//thanosQuote: String = &amp;quot;I am inevitable&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a7352c2d-6e59-4682-93d6-eed9f33f75a0--&gt;
&lt;!-- begin paragraph d3851fba-d429-4299-9a3c-77e3d581a1fa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d3851fba-d429-4299-9a3c-77e3d581a1fa&quot;&gt;Bien sûr vous pouvez exécuter des commandes plus utiles tels que des &lt;/p&gt;
&lt;!-- end paragraph d3851fba-d429-4299-9a3c-77e3d581a1fa--&gt;
&lt;!-- begin code b4700b9e-67da-401f-aac6-9bbce304143e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b4700b9e-67da-401f-aac6-9bbce304143e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val gitCommit = sh&amp;quot;git commit -m \&amp;quot;yolo\&amp;quot;&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b4700b9e-67da-401f-aac6-9bbce304143e--&gt;
&lt;!-- begin paragraph c1ce8f08-d6bb-4f12-b34a-b6061ca7e72a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c1ce8f08-d6bb-4f12-b34a-b6061ca7e72a&quot;&gt;et si jamais vous avez des erreurs dans votre commande, avec par exemple des guillemets mal placés, c&amp;#39;est lors du compile-time que vous aurez un retour.&lt;/p&gt;
&lt;!-- end paragraph c1ce8f08-d6bb-4f12-b34a-b6061ca7e72a--&gt;
&lt;!-- begin paragraph 618ee612-df7a-4f5c-b0e9-a8cf809a2aea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-618ee612-df7a-4f5c-b0e9-a8cf809a2aea&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 618ee612-df7a-4f5c-b0e9-a8cf809a2aea--&gt;
&lt;!-- begin heading_3 19da5555-25a5-4611-81a8-bfd9fc073da6--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-19da5555-25a5-4611-81a8-bfd9fc073da6&quot;&gt;&lt;a href=&quot;https://github.com/propensive/kaleidoscope&quot;&gt;Kaleidoscope&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 19da5555-25a5-4611-81a8-bfd9fc073da6--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8005a4eb-1dd9-4586-aff2-cb1376738c86/photo-1532442150204-e388efe7751c&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 154e1ea8-3ffa-4db3-b602-49eca3893e49--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-154e1ea8-3ffa-4db3-b602-49eca3893e49&quot;&gt;Une des librairies les plus utiles, dixit Jon Pretty, Kaleidoscope exauce vos rêves les plus fous et vous permet de faire du pattern matching sur des expressions régulières.&lt;/p&gt;
&lt;!-- end paragraph 154e1ea8-3ffa-4db3-b602-49eca3893e49--&gt;
&lt;!-- begin paragraph 1c7d1943-3104-4a4e-9857-207882b95218--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1c7d1943-3104-4a4e-9857-207882b95218&quot;&gt;Exemple sympa :&lt;/p&gt;
&lt;!-- end paragraph 1c7d1943-3104-4a4e-9857-207882b95218--&gt;
&lt;!-- begin code 25182aee-ea85-4ec2-a9b0-e2c5b8649d66--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-25182aee-ea85-4ec2-a9b0-e2c5b8649d66&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val email = &amp;quot;tigrou@univalence.io&amp;quot;

val optEmail: Option[String] = email match {
  case r&amp;quot;^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}$$&amp;quot; =&amp;gt; Some(email)
  case _ =&amp;gt; None
}
//optEmail contiendra bien &amp;quot;tigrou@univalence.io&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 25182aee-ea85-4ec2-a9b0-e2c5b8649d66--&gt;
&lt;!-- begin paragraph 0bc22bc1-2198-4d21-a7bd-399d3d27ba19--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0bc22bc1-2198-4d21-a7bd-399d3d27ba19&quot;&gt;Bien sûr vous avez aussi la possibilité d&amp;#39;extraire certaines parties de la String que vous matchez avec une expression régulière avec la syntaxe suivante :&lt;/p&gt;
&lt;!-- end paragraph 0bc22bc1-2198-4d21-a7bd-399d3d27ba19--&gt;
&lt;!-- begin code 37b6b420-883a-4bb9-9548-13796b6e2103--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-37b6b420-883a-4bb9-9548-13796b6e2103&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val r&amp;quot;^$prefix@([a-z0-9._%+-]+)@$domain@([a-z0-9.-]+)\.$tld@([a-z]{2,6})$$&amp;quot; = &amp;quot;tigrou@univalence.io&amp;quot;

//prefix: String = &amp;quot;tigrou&amp;quot;
//domain: String = &amp;quot;univalence.io&amp;quot;
//tld: String = &amp;quot;io&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 37b6b420-883a-4bb9-9548-13796b6e2103--&gt;
&lt;!-- begin paragraph da6c347e-afc6-43b2-b2f7-afa0950304d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-da6c347e-afc6-43b2-b2f7-afa0950304d4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph da6c347e-afc6-43b2-b2f7-afa0950304d4--&gt;
&lt;!-- begin heading_3 514fe167-d745-46db-a1c2-8b22b0d4228f--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-514fe167-d745-46db-a1c2-8b22b0d4228f&quot;&gt;&lt;a href=&quot;https://github.com/propensive/mutatus&quot;&gt;Mutatus&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 514fe167-d745-46db-a1c2-8b22b0d4228f--&gt;
&lt;!-- begin paragraph c31a85d3-c650-4313-b187-53c5c0275a08--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c31a85d3-c650-4313-b187-53c5c0275a08&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c31a85d3-c650-4313-b187-53c5c0275a08--&gt;
&lt;!-- begin paragraph 589e4a96-b228-4ab7-a749-d0bfd1eccf36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-589e4a96-b228-4ab7-a749-d0bfd1eccf36&quot;&gt;Mutatus pour faire des mutations dans le cloud, ici GCP. La librairie propose une API simple pour sérialiser de la donnée vers GCP :&lt;/p&gt;
&lt;!-- end paragraph 589e4a96-b228-4ab7-a749-d0bfd1eccf36--&gt;
&lt;!-- begin code 0a7532ce-76b1-4e01-8260-a49dec985bd5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0a7532ce-76b1-4e01-8260-a49dec985bd5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import mutatus._

case class Person(id: String, age: Int, address: Ref[Address])
case class Address(city: String, zipcode: String)

implicit val addressId: Id[Address] = _.id
implicit val personId: Id[Person] = _.id

val address: Address = Address(&amp;quot;Forêt des rêves bleus&amp;quot;, &amp;quot;12345&amp;quot;)
//On sauvegarde address dans GCP Datastore
val addressRef: Ref[Address] = address.save()
//De même pour winnie
val winnie: Ref[Person] = Person(&amp;quot;Winnie l&amp;#39;ourson&amp;quot;, 93, addressRef).save()
//On peut accéder à l&amp;#39;addresse de winnie
val addressWinnie = winnie.address()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0a7532ce-76b1-4e01-8260-a49dec985bd5--&gt;
&lt;!-- begin paragraph e13a7ca9-cf04-4b2d-8eb8-74c040f5970a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e13a7ca9-cf04-4b2d-8eb8-74c040f5970a&quot;&gt;Winnie et son adresse sont maintenant dans votre GCP Datastore.&lt;/p&gt;
&lt;!-- end paragraph e13a7ca9-cf04-4b2d-8eb8-74c040f5970a--&gt;
&lt;!-- begin paragraph 3b104b0d-99f6-41af-b338-3c43f9abd93b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b104b0d-99f6-41af-b338-3c43f9abd93b&quot;&gt;Vous pouvez aussi récupérer Winnie du Datastore avec :&lt;/p&gt;
&lt;!-- end paragraph 3b104b0d-99f6-41af-b338-3c43f9abd93b--&gt;
&lt;!-- begin code f882dff2-c3eb-4ef2-822a-cc08d2a23ee1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f882dff2-c3eb-4ef2-822a-cc08d2a23ee1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val winnie = Dao[Person].all()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f882dff2-c3eb-4ef2-822a-cc08d2a23ee1--&gt;
&lt;!-- begin paragraph e27c9e21-aa74-4591-b608-4b80d1092f13--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e27c9e21-aa74-4591-b608-4b80d1092f13&quot;&gt;Le type Ref représente une référence à un datatype qui est stockée autre part, par exemple sur GCP.&lt;/p&gt;
&lt;!-- end paragraph e27c9e21-aa74-4591-b608-4b80d1092f13--&gt;
&lt;!-- begin paragraph ddabbdfb-a818-468d-8c1a-791be3db51eb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddabbdfb-a818-468d-8c1a-791be3db51eb&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ddabbdfb-a818-468d-8c1a-791be3db51eb--&gt;
&lt;!-- begin heading_3 e8352c08-4202-4e4a-91f6-61f8d47fb54a--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-e8352c08-4202-4e4a-91f6-61f8d47fb54a&quot;&gt;&lt;a href=&quot;https://github.com/propensive/optometry&quot;&gt;Optometry&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 e8352c08-4202-4e4a-91f6-61f8d47fb54a--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/8005a4eb-1dd9-4586-aff2-cb1376738c86/photo-1535209821329-b6318a68a547&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 133e31ec-9bd0-4b19-a8f4-5f5b47021dbd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-133e31ec-9bd0-4b19-a8f4-5f5b47021dbd&quot;&gt;Optometry est une librairie semblable à &lt;a href=&quot;https://github.com/julien-truffaut/Monocle&quot;&gt;Monocle&lt;/a&gt;, qui permet de créer des lens avec un syntaxe assez légère.&lt;/p&gt;
&lt;!-- end paragraph 133e31ec-9bd0-4b19-a8f4-5f5b47021dbd--&gt;
&lt;!-- begin paragraph 62c8337f-ab7d-49a1-bada-ed6e772dda34--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-62c8337f-ab7d-49a1-bada-ed6e772dda34&quot;&gt;Créer une Lens est assez simple :&lt;/p&gt;
&lt;!-- end paragraph 62c8337f-ab7d-49a1-bada-ed6e772dda34--&gt;
&lt;!-- begin code 6bb16bce-b465-4c96-91e6-3222fd40e9bb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6bb16bce-b465-4c96-91e6-3222fd40e9bb&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Person(name: String, address: Address, age: Int)
case class Address(city: String, zipcode: String)

val nameLens = Lens[Person](_.name)
val person: Person = Person(&amp;quot;porcinet&amp;quot;, Address(&amp;quot;Forêt des rêves bleus&amp;quot;, &amp;quot;12345&amp;quot;), 1) 

// nameLens(person) = &amp;quot;porcinet&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6bb16bce-b465-4c96-91e6-3222fd40e9bb--&gt;
&lt;!-- begin paragraph 507bf4d1-47ad-4261-a07e-4a1b8f26b305--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-507bf4d1-47ad-4261-a07e-4a1b8f26b305&quot;&gt;Vous pouvez bien sûr aller dans des niveaux plus profonds :&lt;/p&gt;
&lt;!-- end paragraph 507bf4d1-47ad-4261-a07e-4a1b8f26b305--&gt;
&lt;!-- begin code fb02c13a-5b29-4c61-8fb9-e89bdd9761f0--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fb02c13a-5b29-4c61-8fb9-e89bdd9761f0&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Person(name: String, address: Address, age: Int)
case class Address(city: String, zipcode: String)


val getCity = Lens[Person](_.address.city)
// getLines(person()) = &amp;quot;Forêt des rêves bleus&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fb02c13a-5b29-4c61-8fb9-e89bdd9761f0--&gt;
&lt;!-- begin paragraph c3d3dea3-15c8-4c91-a124-c11e9a155cf6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c3d3dea3-15c8-4c91-a124-c11e9a155cf6&quot;&gt;D&amp;#39;autres opérations intéressantes sont disponibles comme le fait de pouvoir mettre à jour des membres spécifiques d&amp;#39;une case class imbriquée, de combiner des Lens, ...&lt;/p&gt;
&lt;!-- end paragraph c3d3dea3-15c8-4c91-a124-c11e9a155cf6--&gt;
&lt;!-- begin heading_1 f9ac3c63-148d-4752-87a1-da52d25818be--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f9ac3c63-148d-4752-87a1-da52d25818be&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 f9ac3c63-148d-4752-87a1-da52d25818be--&gt;
&lt;!-- begin paragraph 2e5e3b32-78f1-4ebf-bc69-6bcfc9316e96--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2e5e3b32-78f1-4ebf-bc69-6bcfc9316e96&quot;&gt;Certaines librairies telles que Kaleidoscope ou Contextual apportent une vraie valeur ajoutée et vous seront utiles dans la plupart de vos projets. &lt;br /&gt;
Néanmoins il est vrai que ces librairies tombent souvent dans l&amp;#39;oubli car ce sont des libraries qui ne sont pas très connues et il s&amp;#39;agit alors de les utiliser si le cas se présente et d&amp;#39;envoyer des idées d&amp;#39;amélioration.&lt;/p&gt;
&lt;!-- end paragraph 2e5e3b32-78f1-4ebf-bc69-6bcfc9316e96--&gt;
&lt;!-- begin paragraph 0b1494dc-4626-445a-b65a-bfb4d0da9e64--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0b1494dc-4626-445a-b65a-bfb4d0da9e64&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0b1494dc-4626-445a-b65a-bfb4d0da9e64--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:97c223dcb7974407a9c8d665e2ad6f30</id>
    <title>Spark &amp; ZIO  : rencontre du 3e type</title>
    <updated>2019-05-16T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/spark-et-zio-rencontre-du-3e-type.html"/>
    <!--summary -->    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="ZIO"></category>    <category term="Spark"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin heading_1 c6f9e0a2-7c37-426a-9ae3-9b4c49cd9cf6--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-c6f9e0a2-7c37-426a-9ae3-9b4c49cd9cf6&quot;&gt;Zed Aïe Oh&lt;/h2&gt;
&lt;!-- end heading_1 c6f9e0a2-7c37-426a-9ae3-9b4c49cd9cf6--&gt;
&lt;!-- begin paragraph 372ed05e-ba4c-464f-997f-146af53d3e7f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-372ed05e-ba4c-464f-997f-146af53d3e7f&quot;&gt;Avec l&amp;#39;arrivée imminente de ZIO 1.0, une grande nouveauté apparaît dans cette bibliothèque : à savoir l&amp;#39;adoption d&amp;#39;un troisième type paramétré pour l&amp;#39;IO. Le type &lt;code&gt;ZIO[R, E, A]&lt;/code&gt; fut ainsi créé.&lt;/p&gt;
&lt;!-- end paragraph 372ed05e-ba4c-464f-997f-146af53d3e7f--&gt;
&lt;!-- begin paragraph e24877f1-202f-4112-9490-289dc2ab38de--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e24877f1-202f-4112-9490-289dc2ab38de&quot;&gt;Auparavant, le type utilisé par ZIO pour encapsuler votre code avec effet était tout simplement un &lt;code&gt;IO[E, A]&lt;/code&gt; avec :&lt;/p&gt;
&lt;!-- end paragraph e24877f1-202f-4112-9490-289dc2ab38de--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;E → Le type utilisé en cas d&amp;#39;échec, souvent un &lt;code&gt;Throwable&lt;/code&gt; ou des erreurs métiers. Mais il peut aussi être &lt;code&gt;Nothing&lt;/code&gt;, si votre fonction n&amp;#39;est pas amené à échouer. &lt;/li&gt;&lt;li&gt;A → Le type retourné en cas de succès, avec &lt;code&gt;Nothing&lt;/code&gt; indiquant que la fonction ne se finit jamais ou un &lt;code&gt;Unit&lt;/code&gt; si la fonction à des effets et ne retourne pas de valeur.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph b797b43d-fe5a-4283-9bf0-e08395fa1fd6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b797b43d-fe5a-4283-9bf0-e08395fa1fd6&quot;&gt;Maintenant nous avons accès à un troisième type paramétré noté R par convention. R représente tout simplement l&amp;#39;environnement / le contexte nécessaire pour pouvoir faire fonctionner le code à effet dans vos fonctions, comme par exemple la connexion à une base de donnée ou la session Web. Cet environnement peut être composé de plusieurs modules, d&amp;#39;une simple configuration ou bien si vous n&amp;#39;avez besoin de rien, dans ce cas vous utiliserez le type &lt;code&gt;Any&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph b797b43d-fe5a-4283-9bf0-e08395fa1fd6--&gt;
&lt;!-- begin heading_1 8fb5cb42-d4e8-468a-88ed-c5ae279f1594--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-8fb5cb42-d4e8-468a-88ed-c5ae279f1594&quot;&gt;Et Spark dans tout ça... ?&lt;/h2&gt;
&lt;!-- end heading_1 8fb5cb42-d4e8-468a-88ed-c5ae279f1594--&gt;
&lt;!-- begin paragraph 41ab2a69-019e-42a9-9796-eac0402f333d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-41ab2a69-019e-42a9-9796-eac0402f333d&quot;&gt;Que se passe-t-il si vous mettez ZIO et Spark dans un grand bol de céréales ? Paf, ça ne fait pas des ChocapicsTM, mais plutôt un &lt;code&gt;ZIO[SparkEnv, E, A]&lt;/code&gt; que nous pouvons habilement renommer &lt;code&gt;TaskS[A]&lt;/code&gt; pour les fans de Monix 😉.&lt;/p&gt;
&lt;!-- end paragraph 41ab2a69-019e-42a9-9796-eac0402f333d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/97c223dc-b797-4407-a9c8-d665e2ad6f30/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Mais vous pouvez aussi prendre des Chocapics™️ !&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin code 0fb15259-376d-4676-b2b2-b69518526e2f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0fb15259-376d-4676-b2b2-b69518526e2f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;type TaskS[X] = TaskR[SparkEnv, X]
// Avec TaskR[SparkEnv, X] = ZIO[SparkEnv, Throwable, X]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0fb15259-376d-4676-b2b2-b69518526e2f--&gt;
&lt;!-- begin paragraph 9a4844bc-d241-4461-b43d-33e690bd7c1e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9a4844bc-d241-4461-b43d-33e690bd7c1e&quot;&gt;Maintenant que vous détenez le pouvoir de Spark et ZIO dans vos mains, vous pouvez maintenant vous amuser à écrire des &lt;i&gt;for comprehension&lt;/i&gt; partout.&lt;/p&gt;
&lt;!-- end paragraph 9a4844bc-d241-4461-b43d-33e690bd7c1e--&gt;
&lt;!-- begin paragraph 7adbe49d-11cc-4887-b42a-75cba6185875--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7adbe49d-11cc-4887-b42a-75cba6185875&quot;&gt;L&amp;#39;avantage des &lt;i&gt;for comprehension&lt;/i&gt; dans Scala est la facilité de manipulation de tout votre code englobé dans des Task.&lt;/p&gt;
&lt;!-- end paragraph 7adbe49d-11cc-4887-b42a-75cba6185875--&gt;
&lt;!-- begin paragraph 17d26f22-667d-42ea-ae12-d50239fd7251--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-17d26f22-667d-42ea-ae12-d50239fd7251&quot;&gt;Un exemple de programme utilisant ZIO ayant pour environnement Spark :&lt;/p&gt;
&lt;!-- end paragraph 17d26f22-667d-42ea-ae12-d50239fd7251--&gt;
&lt;!-- begin code 35c2778e-5505-4b50-b86c-8e3d04a8419e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-35c2778e-5505-4b50-b86c-8e3d04a8419e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;//Mise en place d&amp;#39;une session Spark
val ss: SparkSession   =
  SparkSession.builder.master(&amp;quot;local[*]&amp;quot;).getOrCreate()
//SparkZIO est une classe qui étend le trait SparkEnv et qui embarque une session Spark
val sparkEnv: SparkZIO = new SparkZIO(ss)

//Votre programme
val prg: TaskS[(DataFrame, DataFrame)] =
  for {
    //Supposons que totofile contienne de la donnée texte
    df  &amp;lt;- sparkEnv.read.textFile(&amp;quot;totofile&amp;quot;)
    _   &amp;lt;- Task(df.createTempView(&amp;quot;totoview&amp;quot;))
    df2 &amp;lt;- SparkEnv.sql(s&amp;quot;&amp;quot;&amp;quot;SELECT * FROM totoview&amp;quot;&amp;quot;&amp;quot;)
  } yield (df, df2)

//Ici nous donnons à notre programme l&amp;#39;environnement Spark
val liveProgram: IO[Throwable, (DataFrame, DataFrame)] = prg.provide(sparkEnv)

//Un runtime par défaut est fourni par ZIO qui sera utilisé pour exécuter les effets
val runtime: DefaultRuntime = new DefaultRuntime {}
//L&amp;#39;exécution des effets a effectivement lieu ci-dessous. On récupère aussi les retours de ces effets
val resRun: (DataFrame, DataFrame) = runtime.unsafeRun(liveProgram)

//resRun._1 et resRun._2 ont le même contenu&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 35c2778e-5505-4b50-b86c-8e3d04a8419e--&gt;
&lt;!-- begin paragraph 0e38afcb-a778-4c3f-9e7c-c74b4ac5bf57--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e38afcb-a778-4c3f-9e7c-c74b4ac5bf57&quot;&gt;&lt;code&gt;SparkEnv.sql()&lt;/code&gt; est un helper qui masque l&amp;#39;accès à l&amp;#39;environnement Spark fourni à ZIO :&lt;/p&gt;
&lt;!-- end paragraph 0e38afcb-a778-4c3f-9e7c-c74b4ac5bf57--&gt;
&lt;!-- begin code 0902567b-2a9f-43a2-878b-62072e20a728--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0902567b-2a9f-43a2-878b-62072e20a728&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def sql(queryString: String): TaskS[DataFrame] =
    ZIO.accessM(_.query.sql(queryString))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0902567b-2a9f-43a2-878b-62072e20a728--&gt;
&lt;!-- begin paragraph d69eb6c3-6ac4-4e27-a1b6-f814740a5748--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d69eb6c3-6ac4-4e27-a1b6-f814740a5748&quot;&gt;&lt;code&gt;ZIO.accessM&lt;/code&gt; est LA fonction qui vous permet d&amp;#39;accéder à l&amp;#39;environnement. L&amp;#39;environnement SparkEnv est représenté sous la forme d&amp;#39;un trait Scala. L&amp;#39;inspiration venant principalement du &lt;a href=&quot;http://degoes.net/articles/zio-environment&quot;&gt;blog post&lt;/a&gt; sur cette nouvelle fonctionnalité décrit par John de Goes.&lt;/p&gt;
&lt;!-- end paragraph d69eb6c3-6ac4-4e27-a1b6-f814740a5748--&gt;
&lt;!-- begin paragraph 2122fc3d-364e-4fe4-9bed-b0ce78bfdeda--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2122fc3d-364e-4fe4-9bed-b0ce78bfdeda&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2122fc3d-364e-4fe4-9bed-b0ce78bfdeda--&gt;
&lt;!-- begin paragraph 5f314839-db53-4168-96e5-a3cec90fb9f1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f314839-db53-4168-96e5-a3cec90fb9f1&quot;&gt;Si vous voulez en savoir plus sur le mariage entre ZIO et Spark, je vous invite à aller voir notre repo git &lt;a href=&quot;https://github.com/univalence/spark-tools&quot;&gt;spark-tools&lt;/a&gt; qui contient divers outils utiles pour le data engineering. &lt;a href=&quot;https://github.com/univalence/spark-tools/tree/master/spark-zio&quot;&gt;Spark-zio&lt;/a&gt; est l&amp;#39;un des projets qui se trouve dans spark-tools et contient notre implémentation. Spark-zio est aussi disponible sous &lt;a href=&quot;https://repo1.maven.org/maven2/io/univalence/spark-zio_2.11/&quot;&gt;Maven Central&lt;/a&gt; (nous utilisons &lt;a href=&quot;https://github.com/dwijnand/sbt-dynver#detail&quot;&gt;sbt-dynver&lt;/a&gt; pour déterminer le numéro de version).&lt;/p&gt;
&lt;!-- end paragraph 5f314839-db53-4168-96e5-a3cec90fb9f1--&gt;
&lt;!-- begin paragraph 81097c1f-6307-45d8-988e-87c79f471b9c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81097c1f-6307-45d8-988e-87c79f471b9c&quot;&gt;Nous avons choisi de wrapper la méthode &lt;code&gt;sql(sqlText: String): DataFrame&lt;/code&gt; de &lt;code&gt;SparkSession&lt;/code&gt;, car nous n&amp;#39;avons aucun contrôle sur la requête faite. Elle peut en effet faire des &lt;i&gt;drop tables&lt;/i&gt; ou autres actions à effet dans Spark.&lt;/p&gt;
&lt;!-- end paragraph 81097c1f-6307-45d8-988e-87c79f471b9c--&gt;
&lt;!-- begin paragraph 46c35fb1-b30f-4b18-8998-0040c8f24075--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-46c35fb1-b30f-4b18-8998-0040c8f24075&quot;&gt;Nous avons aussi wrappé toutes les méthodes à effet du type &lt;i&gt;load&lt;/i&gt; et &lt;i&gt;write&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 46c35fb1-b30f-4b18-8998-0040c8f24075--&gt;
&lt;!-- begin paragraph b886c383-5709-4bee-a260-73b1fcff5d00--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b886c383-5709-4bee-a260-73b1fcff5d00&quot;&gt;En se limitant à cela et en ne se préoccupant pas des méthodes du type &lt;code&gt;.select()&lt;/code&gt;, &lt;code&gt;.where()&lt;/code&gt; que nous pouvons considérer comme raisonnablement pur, nous obtenons ainsi une bibliothèque beaucoup plus maintenable, sachant qu&amp;#39;il aurait été très coûteux de devoir wrapper toutes les APIs Spark.&lt;/p&gt;
&lt;!-- end paragraph b886c383-5709-4bee-a260-73b1fcff5d00--&gt;
&lt;!-- begin paragraph d48fe621-e5ca-4641-b95d-01aa2b575de9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d48fe621-e5ca-4641-b95d-01aa2b575de9&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d48fe621-e5ca-4641-b95d-01aa2b575de9--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:b73ba25fc5654710b780deddcb59d646</id>
    <title>Les onzes commandements de Jon Pretty (1re partie)</title>
    <updated>2019-05-09T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/les-onzes-commandements-de-jon-pretty-1re-partie.html"/>
    <!--summary -->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>        <category term="Scala"></category>    <category term="Jon Pretty"></category>    <content type="html">
&lt;!-- begin paragraph 73e62a92-ac0f-40d2-9500-4665c6f5291b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-73e62a92-ac0f-40d2-9500-4665c6f5291b&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 73e62a92-ac0f-40d2-9500-4665c6f5291b--&gt;
&lt;!-- begin paragraph e760d6bb-a68c-411b-889a-0482fdb1855f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e760d6bb-a68c-411b-889a-0482fdb1855f&quot;&gt;&lt;a href=&quot;https://blog.univalence.io/retour-sur-scalar-2019/&quot;&gt;Suite au retour sur Scalar 2019 que nous avons fait&lt;/a&gt;, nous avons promis de vous en dire plus sur les onzes librairies que Jon Pretty avait présenté en vitesse lors de son passage à Scalar.Chose promise, chose due !&lt;/p&gt;
&lt;!-- end paragraph e760d6bb-a68c-411b-889a-0482fdb1855f--&gt;
&lt;!-- begin paragraph bcfff36d-940f-4e7d-b5f9-314d1b12aec0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bcfff36d-940f-4e7d-b5f9-314d1b12aec0&quot;&gt;Jon Pretty a besoin d&amp;#39;aide pour gérer ses libs. Êtes-vous prêts à l&amp;#39;aider ?&lt;/p&gt;
&lt;!-- end paragraph bcfff36d-940f-4e7d-b5f9-314d1b12aec0--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/b73ba25f-c565-4710-b780-deddcb59d646/batman_signal-1.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 8ca96b5c-ed4a-4a76-a65a-170ded916949--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8ca96b5c-ed4a-4a76-a65a-170ded916949&quot;&gt;Commençons donc sans plus tarder !&lt;/p&gt;
&lt;!-- end paragraph 8ca96b5c-ed4a-4a76-a65a-170ded916949--&gt;
&lt;!-- begin heading_3 554478ab-f534-4c75-95e2-345d6716400a--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-554478ab-f534-4c75-95e2-345d6716400a&quot;&gt;&lt;a href=&quot;https://github.com/propensive/adversaria&quot;&gt;Adversaria&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 554478ab-f534-4c75-95e2-345d6716400a--&gt;
&lt;!-- begin paragraph b5ae2cc7-ec01-4314-8fc5-9e46d5d645ca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b5ae2cc7-ec01-4314-8fc5-9e46d5d645ca&quot;&gt;Adversaria permet d&amp;#39;obtenir des informations sur des annotations à l&amp;#39;aide&lt;br /&gt;
d&amp;#39;interfaces typeclass et des macros. Il y a actuellement trois&lt;br /&gt;
utilisations de cette lib :&lt;/p&gt;
&lt;!-- end paragraph b5ae2cc7-ec01-4314-8fc5-9e46d5d645ca--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;obtenir toutes les annotations appliquées à un type en particulier&lt;/li&gt;&lt;li&gt;trouver un paramètre annoté dans une case class&lt;/li&gt;&lt;li&gt;obtenir toutes les annotations associées à un champ d&amp;#39;une case class&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 0d7ceb0c-8fff-406a-b463-50665387fa85--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0d7ceb0c-8fff-406a-b463-50665387fa85&quot;&gt;Dans les exemples de la page github d&amp;#39;Adversia, on retrouve deux typeclass permettant de faire cela : &lt;code&gt;TypeMetadata&lt;/code&gt; et &lt;code&gt;FindMetadata&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 0d7ceb0c-8fff-406a-b463-50665387fa85--&gt;
&lt;!-- begin heading_3 e398a140-37e1-40f7-890e-0ffa4543a5fc--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-e398a140-37e1-40f7-890e-0ffa4543a5fc&quot;&gt;&lt;a href=&quot;https://github.com/propensive/gastronomy&quot;&gt;Gastronomy&lt;/a&gt; (&lt;a href=&quot;https://propensive.com/opensource/gastronomy&quot;&gt;Doc&lt;/a&gt;)&lt;/h4&gt;
&lt;!-- end heading_3 e398a140-37e1-40f7-890e-0ffa4543a5fc--&gt;
&lt;!-- begin paragraph e0e67513-f9fe-4e49-8a6a-838f36c93144--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e0e67513-f9fe-4e49-8a6a-838f36c93144&quot;&gt;Miam ! Cette lib permet de calculer des &amp;quot;hash digests&amp;quot; pour :&lt;/p&gt;
&lt;!-- end paragraph e0e67513-f9fe-4e49-8a6a-838f36c93144--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;des objets simples&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 751fe541-b112-4f68-83d1-b10447142ae2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-751fe541-b112-4f68-83d1-b10447142ae2&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;import gastronomy._
  val intDigest: Digest = 42.digest[Md5]
  val stringDigest: Digest = &amp;quot;Hello world!&amp;quot;.digest[Sha1]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 751fe541-b112-4f68-83d1-b10447142ae2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;des objets plus compliqués comme des case class&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code ca8c92d2-09b3-4376-bd2d-ec56d7ccd661--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ca8c92d2-09b3-4376-bd2d-ec56d7ccd661&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;import gastronomy._
  case class Person(name: String, age: Int)
  case class Group(people: List[Person])
  
  val group = Group(List(Person(&amp;quot;Bill&amp;quot;, 18), Person(&amp;quot;Jane&amp;quot;, 22)))
  val groupDigest: Digest = group.digest[Sha256]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ca8c92d2-09b3-4376-bd2d-ec56d7ccd661--&gt;
&lt;!-- begin paragraph deca3091-f1ac-4d44-8470-ca8b65338854--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-deca3091-f1ac-4d44-8470-ca8b65338854&quot;&gt;Actuellement, les hash acceptés sont Md5, SHA1, et SHA256.&lt;/p&gt;
&lt;!-- end paragraph deca3091-f1ac-4d44-8470-ca8b65338854--&gt;
&lt;!-- begin paragraph 936c0f0d-c8cb-4f3b-b649-664aae8d6c3d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-936c0f0d-c8cb-4f3b-b649-664aae8d6c3d&quot;&gt;Cette lib permet également d&amp;#39;encoder par la suite les digest avec la fonction &lt;code&gt;encode&lt;/code&gt;. Les encodages acceptés sont &lt;code&gt;Hex&lt;/code&gt; et &lt;code&gt;Base64&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 936c0f0d-c8cb-4f3b-b649-664aae8d6c3d--&gt;
&lt;!-- begin heading_3 dd2924c5-0129-46da-a25f-f6f5277727a9--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-dd2924c5-0129-46da-a25f-f6f5277727a9&quot;&gt;&lt;a href=&quot;https://github.com/propensive/honeycomb&quot;&gt;Honeycomb&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 dd2924c5-0129-46da-a25f-f6f5277727a9--&gt;
&lt;!-- begin paragraph 8b26cc58-662e-4da7-95e1-67f6e0fd6d67--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8b26cc58-662e-4da7-95e1-67f6e0fd6d67&quot;&gt;DSL pour générer du code HTML bien formé (table, tbody, tr, td, div, ...).&lt;/p&gt;
&lt;!-- end paragraph 8b26cc58-662e-4da7-95e1-67f6e0fd6d67--&gt;
&lt;!-- begin paragraph 98f4723b-e80c-48da-8847-b0ce697a769e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-98f4723b-e80c-48da-8847-b0ce697a769e&quot;&gt;En bonus, les erreurs sont affichées lors de la compilation, et non pas au runtime/display !&lt;/p&gt;
&lt;!-- end paragraph 98f4723b-e80c-48da-8847-b0ce697a769e--&gt;
&lt;!-- begin heading_3 737bb12b-58b1-4714-853a-5e8b960550f3--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-737bb12b-58b1-4714-853a-5e8b960550f3&quot;&gt;&lt;a href=&quot;https://github.com/propensive/mercator&quot;&gt;Mercator&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 737bb12b-58b1-4714-853a-5e8b960550f3--&gt;
&lt;!-- begin paragraph dace42b8-da51-4f17-b21a-9e5c9fda2425--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dace42b8-da51-4f17-b21a-9e5c9fda2425&quot;&gt;Grâce à l&amp;#39;utilisation des macros, Mercator permet d&amp;#39;automatiquement&lt;br /&gt;
construire une &amp;quot;preuve&amp;quot; qu&amp;#39;un type connu peut être utilisé dans un&lt;br /&gt;
for-comprehension (List, Option, ...). Il s&amp;#39;agit d&amp;#39;abstraire les types&lt;br /&gt;
similaires aux monad sans impacts sur la performance.&lt;/p&gt;
&lt;!-- end paragraph dace42b8-da51-4f17-b21a-9e5c9fda2425--&gt;
&lt;!-- begin paragraph e89d7b62-4e58-4aeb-a9c8-a69862252f70--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e89d7b62-4e58-4aeb-a9c8-a69862252f70&quot;&gt;Par exemple normalement, il n&amp;#39;est pas possible de faire un for-comprehension avec en paramètre de type &lt;code&gt;F[_]&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph e89d7b62-4e58-4aeb-a9c8-a69862252f70--&gt;
&lt;!-- begin code dcd61ed4-07c9-46c3-9a0d-a278567d15ef--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dcd61ed4-07c9-46c3-9a0d-a278567d15ef&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;// ne compile pas
def increment[F[_]](xs: F[Int]) = for(x &amp;lt;- xs) yield x + 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dcd61ed4-07c9-46c3-9a0d-a278567d15ef--&gt;
&lt;!-- begin paragraph c46fd0d1-facf-4d77-bd7d-a5a1b98a970e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c46fd0d1-facf-4d77-bd7d-a5a1b98a970e&quot;&gt;car le compilateur ne sait pas si F possède les méthodes &lt;code&gt;map&lt;/code&gt; et &lt;code&gt;flatMap&lt;/code&gt; , qui sont indispensables dans la construction d&amp;#39;un for-comprehension.&lt;/p&gt;
&lt;!-- end paragraph c46fd0d1-facf-4d77-bd7d-a5a1b98a970e--&gt;
&lt;!-- begin paragraph 43f47ea0-212a-48dd-9c18-ad3706d462cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-43f47ea0-212a-48dd-9c18-ad3706d462cb&quot;&gt;Avec Mercator, on peut instancier automatiquement une instance implicite &lt;code&gt;Monadic[F]&lt;/code&gt; pour n&amp;#39;importe quel type F[_] possédant map et flatMap. Cela permet de fournir ces méthodes à l&amp;#39;instance de F[_] :&lt;/p&gt;
&lt;!-- end paragraph 43f47ea0-212a-48dd-9c18-ad3706d462cb--&gt;
&lt;!-- begin code 5f107e4f-e3b4-447f-b660-e1ad82751a16--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5f107e4f-e3b4-447f-b660-e1ad82751a16&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;// compile !
import mercator._
def increment[F[_]: Monadic](xs: F[Int]) = for(x &amp;lt;- xs) yield x + 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5f107e4f-e3b4-447f-b660-e1ad82751a16--&gt;
&lt;!-- begin heading_3 8abba17c-d29d-46f4-b60b-1e6006180b47--&gt;
&lt;h4 class=&quot;block-heading_3 level-0&quot; id=&quot;h-8abba17c-d29d-46f4-b60b-1e6006180b47&quot;&gt;&lt;a href=&quot;https://github.com/propensive/pyroclastic&quot;&gt;Pyroclastic&lt;/a&gt;&lt;/h4&gt;
&lt;!-- end heading_3 8abba17c-d29d-46f4-b60b-1e6006180b47--&gt;
&lt;!-- begin paragraph 7825433f-039f-4cc5-9fff-28c3aa6e17f7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7825433f-039f-4cc5-9fff-28c3aa6e17f7&quot;&gt;Permet de créer des graphes monadiques composables.&lt;/p&gt;
&lt;!-- end paragraph 7825433f-039f-4cc5-9fff-28c3aa6e17f7--&gt;
&lt;!-- begin paragraph 49cf8124-a9c8-43cc-a817-c3caa0969204--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-49cf8124-a9c8-43cc-a817-c3caa0969204&quot;&gt;Voici un &lt;a href=&quot;https://github.com/propensive/pyroclastic/blob/master/src/example/test.scala&quot;&gt;exemple&lt;/a&gt; de graphe, correspondant à la préparation de thé et café.&lt;/p&gt;
&lt;!-- end paragraph 49cf8124-a9c8-43cc-a817-c3caa0969204--&gt;
&lt;!-- begin divider 2227e7b9-6aaf-40de-a88d-b8c537c5ef7e--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-2227e7b9-6aaf-40de-a88d-b8c537c5ef7e&quot;&gt;
&lt;!-- end divider 2227e7b9-6aaf-40de-a88d-b8c537c5ef7e--&gt;
&lt;!-- begin paragraph 9aeebc84-8f03-4fe9-aa8c-05b186529129--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9aeebc84-8f03-4fe9-aa8c-05b186529129&quot;&gt;La suite dans un prochain article :)&lt;/p&gt;
&lt;!-- end paragraph 9aeebc84-8f03-4fe9-aa8c-05b186529129--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:c4a5a112f854404e9f45fd43f60a61e2</id>
    <title>Apache Kafka VS Apache Pulsar</title>
    <updated>2019-04-29T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/apache-kafka-vs-apache-pulsar.html"/>
    <!--summary -->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>        <category term="Kafka"></category>    <category term="Apache Pulsar"></category>    <content type="html">
&lt;!-- begin paragraph e2df7da7-17a6-4df2-bf38-bf8c24988220--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e2df7da7-17a6-4df2-bf38-bf8c24988220&quot;&gt;Le monde des systèmes de messageries de type publish-subscribe a vu plusieurs acteurs émerger comme Apache Kafka, Apache Pulsar, les solutions proposées par Amazon (Kinesis) et Google (PubSub), et autres. Dans cet article, nous allons nous attarder sur les deux projets open-source que sont Apache Kafka et Apache Pulsar. Ces deux systèmes se veulent distribués (donc extensible), fiables, de faible latences dans l&amp;#39;envoi de messages, et autres joyeuseries. Le but n&amp;#39;est pas que de constater les différences, mais également les similarités et équivalences qui existent entre ces deux systèmes. Sans plus tarder, voici donc un tableau faisant la comparaison entre ceux-ci :&lt;/p&gt;
&lt;!-- end paragraph e2df7da7-17a6-4df2-bf38-bf8c24988220--&gt;
&lt;!-- begin paragraph 372a7b8b-b767-47fe-9d75-4e50991f3356--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-372a7b8b-b767-47fe-9d75-4e50991f3356&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 372a7b8b-b767-47fe-9d75-4e50991f3356--&gt;
&lt;!-- begin table 007f5726-b291-4c92-a438-b682f094bf54--&gt;
&lt;table class=&quot;block-table level-&quot; id=&quot;t-007f5726-b291-4c92-a438-b682f094bf54&quot;&gt;  &lt;thead&gt;
    &lt;tr&gt;      &lt;th scope=&quot;col&quot;&gt;Feature&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;Apache Kafka&lt;/th&gt;      &lt;th scope=&quot;col&quot;&gt;Apache Pulsar&lt;/th&gt;    &lt;/tr&gt;
  &lt;/thead&gt;  &lt;tbody&gt;
                    &lt;tr&gt;
                  &lt;td&gt;Concept général&lt;/td&gt;                  &lt;td&gt;Publish-subscribe, producer push et consumer pull&lt;/td&gt;                  &lt;td&gt;Publish-subscribe, producer push et consumer pull&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Parallelisme&lt;/td&gt;                  &lt;td&gt;Topics partitionnés. Un topic possède plusieurs partitions. Une partition représente un ou plusieurs fichiers logs et index. &lt;/td&gt;                  &lt;td&gt;Topics partitionés. Un topic possède plusieurs partitions représentés par le concept de ledgers et fragments d&amp;#39;Apache BookKeeper.&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Architecture&lt;/td&gt;                  &lt;td&gt;Cluster brokers Kafka + cluster ZooKeeper&lt;/td&gt;                  &lt;td&gt;Cluster broker Pulsar + cluster BookKeeper + cluster ZookKeeper&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Stockage&lt;/td&gt;                  &lt;td&gt;Système de structure de logs distribués (à l&amp;#39;intérieur du cluster de brokers)&lt;/td&gt;                  &lt;td&gt;Apache BookKeeper (en dehors du cluster de brokers)&lt;br /&gt;
Tiered Storage ⇒ stockage vers aws&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Geo-Réplication&lt;/td&gt;                  &lt;td&gt;Kafka MirrorMaker&lt;/td&gt;                  &lt;td&gt;Présent et géré au niveau des tenants. ,&lt;a href=&quot;https://pulsar.apache.org/docs/en/administration-geo/&quot;&gt;Doc Pulsar&lt;/a&gt;&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Registre de schémas&lt;/td&gt;                  &lt;td&gt;Confluent Schema Registry&lt;br /&gt;
Chaque message est envoyé avec un ID unique représentant un schéma&lt;/td&gt;                  &lt;td&gt;Approche client ou serveur.&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Retention et expiration des messages (TTL)&lt;/td&gt;                  &lt;td&gt;Possède seulement le concept de rétention. 7 jours de rétention par défaut. Peut également être configuré avec une taille maximale.&lt;/td&gt;                  &lt;td&gt;Possède les deux. Plus de détails ,&lt;a href=&quot;https://univalence.io/blog/articles/un-apercu-d-apache-pulsar/#acknowledgement&quot;&gt;ici&lt;/a&gt;, dans la partie ,&lt;i&gt;Acknowledgement&lt;/i&gt;,.&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Acknowledgement&lt;/td&gt;                  &lt;td&gt;Géré par un topic dédié aux offsets, ,&lt;code&gt;__consumer_offsets&lt;/code&gt;, .&lt;/td&gt;                  &lt;td&gt;Géré par les cursors, l&amp;#39;ack est soit cumulatif (à la Kafka), soit par message (ce dernier permet de mieux gérer les erreurs et une utilisation comme un système de queue plus traditionnelle)&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Capacité de traitement et latence&lt;/td&gt;                  &lt;td&gt;Grande capacité de traitement grâce aux techniques de consommation avec zéro copies.&lt;/td&gt;                  &lt;td&gt;Faible latence.&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Consumer&lt;/td&gt;                  &lt;td&gt;Groupes de consommateurs&lt;br /&gt;
(Voir l&amp;#39;,&lt;a href=&quot;https://blog.univalence.io/kafka-et-les-groupes-de-consommateurs/&quot;&gt;article s&lt;/a&gt;,&lt;a href=&quot;https://univalence.io/blog/articles/kafka-et-les-groupes-de-consommateurs/&quot;&gt;uivant&lt;/a&gt;, sur notre blog pour plus de détail).&lt;/td&gt;                  &lt;td&gt;3 types de consommations différentes (appelés subscriptions).&lt;br /&gt;
Possibilité d&amp;#39;avoir plus de consommateurs que de partitions dans une même subscription.&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Topics&lt;/td&gt;                  &lt;td&gt;Constitué de plusieurs partitions répartis sur plusieurs brokers.&lt;br /&gt;
Multi-tenancy : paramétrage sur les topics pour permettre ou non la consommation/production de données dans les topics + quotas pour contrôler les ressources des brokers utilisées par les clients.&lt;/td&gt;                  &lt;td&gt;Persistant ou non persistant.&lt;br /&gt;
Constitué de plusieurs partitions répartis sur plusieurs brokers.&lt;br /&gt;
Multi-tenancy : Pulsar est conçu pour cela. Les topics sont organisés en namespace, et chaque namespace appartient à un tenant.&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Écosystème&lt;/td&gt;                  &lt;td&gt;Kafka Connect, Kafka Streams&lt;/td&gt;                  &lt;td&gt;Pulsar IO, Pulsar Functions&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Communauté&lt;/td&gt;                  &lt;td&gt;Très Mature&lt;/td&gt;                  &lt;td&gt;En croissance&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Production indempotente&lt;/td&gt;                  &lt;td&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/Idempotent+Producer&quot;&gt;Exactly Once&lt;/a&gt;&lt;/td&gt;                  &lt;td&gt;&lt;a href=&quot;https://streaml.io/blog/pulsar-effectively-once&quot;&gt;Effectively Once&lt;/a&gt;, ⇒ Déduplication des messages&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Headers des messages&lt;/td&gt;                  &lt;td&gt;Depuis 2017 dans la 0.11.0&lt;br /&gt;
,&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-82+-+Add+Record+Headers+-+Typed&quot;&gt;KIP-82&lt;/a&gt;&lt;/td&gt;                  &lt;td&gt;Non implémenté pour l&amp;#39;instant (v2.3.1)&lt;/td&gt;          &lt;/tr&gt;            &lt;tr&gt;
                  &lt;td&gt;Sécurité&lt;/td&gt;                  &lt;td&gt;Authentification + Chiffrement + Autorisation via SSL + SASL (Kerberos, SCRAM-SHA-256/512)&lt;/td&gt;                  &lt;td&gt;Authentification + Chiffrement via TLS.&lt;br /&gt;
Autorisation et authentification avec Athenz ⇒ Concept de &amp;quot;role tokens&amp;quot;.&lt;/td&gt;          &lt;/tr&gt;      &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- end table 007f5726-b291-4c92-a438-b682f094bf54--&gt;
&lt;!-- begin paragraph d439c7a8-6da1-4420-8831-c8ce6ce2ad31--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d439c7a8-6da1-4420-8831-c8ce6ce2ad31&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d439c7a8-6da1-4420-8831-c8ce6ce2ad31--&gt;
&lt;!-- begin paragraph fc9e02b9-86fb-43d7-bf55-91126d1ec3ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fc9e02b9-86fb-43d7-bf55-91126d1ec3ad&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fc9e02b9-86fb-43d7-bf55-91126d1ec3ad--&gt;
&lt;!-- begin paragraph b41717d2-563c-4014-9e62-0ecd959aad4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b41717d2-563c-4014-9e62-0ecd959aad4d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b41717d2-563c-4014-9e62-0ecd959aad4d--&gt;
&lt;!-- begin paragraph fd384288-cd86-4156-b7e9-a8cffe734e83--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fd384288-cd86-4156-b7e9-a8cffe734e83&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph fd384288-cd86-4156-b7e9-a8cffe734e83--&gt;
&lt;!-- begin paragraph 9931b7f6-685a-41da-a03a-0cc2c47a0884--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9931b7f6-685a-41da-a03a-0cc2c47a0884&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9931b7f6-685a-41da-a03a-0cc2c47a0884--&gt;
&lt;!-- begin paragraph 593c6956-f81e-4a15-98c0-25b102dca942--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-593c6956-f81e-4a15-98c0-25b102dca942&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 593c6956-f81e-4a15-98c0-25b102dca942--&gt;
&lt;!-- begin paragraph ca8a0f50-7c0f-4507-bfc6-3ef55f47db45--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ca8a0f50-7c0f-4507-bfc6-3ef55f47db45&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ca8a0f50-7c0f-4507-bfc6-3ef55f47db45--&gt;
&lt;!-- begin paragraph 79eafe79-832a-412c-8064-5d1417b74641--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79eafe79-832a-412c-8064-5d1417b74641&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 79eafe79-832a-412c-8064-5d1417b74641--&gt;
&lt;!-- begin paragraph cb0bb875-2ef0-4a79-896c-60167d5e36de--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cb0bb875-2ef0-4a79-896c-60167d5e36de&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph cb0bb875-2ef0-4a79-896c-60167d5e36de--&gt;
&lt;!-- begin paragraph 9f607943-3e9b-4612-a869-25cc1f0048e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9f607943-3e9b-4612-a869-25cc1f0048e1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9f607943-3e9b-4612-a869-25cc1f0048e1--&gt;
&lt;!-- begin paragraph e8e3ddee-3e20-4cab-baa2-b55ea6c35bfa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e8e3ddee-3e20-4cab-baa2-b55ea6c35bfa&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e8e3ddee-3e20-4cab-baa2-b55ea6c35bfa--&gt;
&lt;!-- begin paragraph 6e0db981-0ea6-4620-adf5-eb4f219a9f83--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6e0db981-0ea6-4620-adf5-eb4f219a9f83&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6e0db981-0ea6-4620-adf5-eb4f219a9f83--&gt;
&lt;!-- begin paragraph 13ae0a0b-8df5-4d20-84d5-3705745050a1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-13ae0a0b-8df5-4d20-84d5-3705745050a1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 13ae0a0b-8df5-4d20-84d5-3705745050a1--&gt;
&lt;!-- begin paragraph 4820c28b-2d1b-49a0-9e18-0edeecb7d59d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4820c28b-2d1b-49a0-9e18-0edeecb7d59d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4820c28b-2d1b-49a0-9e18-0edeecb7d59d--&gt;
&lt;!-- begin paragraph c2bb71a4-3bd0-4357-940d-2b333e126bb0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c2bb71a4-3bd0-4357-940d-2b333e126bb0&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c2bb71a4-3bd0-4357-940d-2b333e126bb0--&gt;
&lt;!-- begin paragraph b1ddce38-4de1-4d63-a7e5-19ac0ad02ea1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b1ddce38-4de1-4d63-a7e5-19ac0ad02ea1&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b1ddce38-4de1-4d63-a7e5-19ac0ad02ea1--&gt;
&lt;!-- begin paragraph 7fefedd1-48ff-4aa3-8d7e-eba4287bba28--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7fefedd1-48ff-4aa3-8d7e-eba4287bba28&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7fefedd1-48ff-4aa3-8d7e-eba4287bba28--&gt;
&lt;!-- begin paragraph d768635b-d474-4ea2-8f9f-1a87223725ff--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d768635b-d474-4ea2-8f9f-1a87223725ff&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d768635b-d474-4ea2-8f9f-1a87223725ff--&gt;
&lt;!-- begin divider 99000740-3d84-4ade-b927-574079e76063--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-99000740-3d84-4ade-b927-574079e76063&quot;&gt;
&lt;!-- end divider 99000740-3d84-4ade-b927-574079e76063--&gt;
&lt;!-- begin paragraph 39289d90-bf08-4bab-98b0-86ba7de3a52c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-39289d90-bf08-4bab-98b0-86ba7de3a52c&quot;&gt;Notes pour l&amp;#39;article :&lt;/p&gt;
&lt;!-- end paragraph 39289d90-bf08-4bab-98b0-86ba7de3a52c--&gt;
&lt;!-- begin paragraph 7add3c6a-46aa-4b6e-a0ae-85c7755e23fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7add3c6a-46aa-4b6e-a0ae-85c7755e23fb&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7add3c6a-46aa-4b6e-a0ae-85c7755e23fb--&gt;
&lt;!-- begin paragraph bc01d5eb-9ba1-4237-88e2-48cbbd83a030--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bc01d5eb-9ba1-4237-88e2-48cbbd83a030&quot;&gt;Effectively once pulsar : &lt;a href=&quot;https://fr.slideshare.net/merlimat/effectivelyonce-semantics-in-apache-pulsar&quot;&gt;https://fr.slideshare.net/merlimat/effectivelyonce-semantics-in-apache-pulsar&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://streaml.io/blog/pulsar-effectively-once&quot;&gt;https://streaml.io/blog/pulsar-effectively-once&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph bc01d5eb-9ba1-4237-88e2-48cbbd83a030--&gt;
&lt;!-- begin paragraph 68e85528-0739-489f-a77c-b3ba228e59b3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-68e85528-0739-489f-a77c-b3ba228e59b3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 68e85528-0739-489f-a77c-b3ba228e59b3--&gt;
&lt;!-- begin paragraph 9291297f-a691-473e-a251-ba99cff3f141--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9291297f-a691-473e-a251-ba99cff3f141&quot;&gt;Chaos Testing Pulsar : &lt;a href=&quot;https://jack-vanlightly.com/blog/2018/10/21/how-to-not-lose-messages-on-an-apache-pulsar-cluster&quot;&gt;https://jack-vanlightly.com/blog/2018/10/21/how-to-not-lose-messages-on-an-apache-pulsar-cluster&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 9291297f-a691-473e-a251-ba99cff3f141--&gt;&lt;div class=&quot;block-quote level-0&quot;&gt;&lt;blockquote&gt;We can test that by enabling the deduplication feature. When enabled, a broker stores the last sequence number of each producer in a hashtable. When it receives a lower sequence number it knows that it is a duplicate and ignores it. It stores the (producer, sequence number) per topic data in a cursor so that, in a broker fail-over, the new broker can recreate the hashtable. The hashtable is snapshotted periodically so in the event of a broker failure, the latest sequence numbers in the hashtable might be lost. This would create a window of opportunity for duplication in the event of a broker fail-over if the new broker only relied on that snapshot. To avoid that scenario the new Pulsar owner reads the last N entries from the ledger and adds them to the hashtable, thereby covering any gap induced by the fail-over.&lt;/blockquote&gt;&lt;/div&gt;
&lt;!-- begin paragraph 78af9968-0318-469c-8fa5-90a610f8c2a9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78af9968-0318-469c-8fa5-90a610f8c2a9&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 78af9968-0318-469c-8fa5-90a610f8c2a9--&gt;
&lt;!-- begin paragraph d026b9f3-1930-4260-a398-e6c87a229cc2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d026b9f3-1930-4260-a398-e6c87a229cc2&quot;&gt;Kafka producer idempotent : &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/Idempotent+Producer&quot;&gt;https://cwiki.apache.org/confluence/display/KAFKA/Idempotent+Producer&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph d026b9f3-1930-4260-a398-e6c87a229cc2--&gt;
&lt;!-- begin paragraph 15de61a3-9012-485c-ab25-e425d4156058--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-15de61a3-9012-485c-ab25-e425d4156058&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 15de61a3-9012-485c-ab25-e425d4156058--&gt;
&lt;!-- begin paragraph fa00bb8b-e45e-4752-a6d0-79d876348acf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fa00bb8b-e45e-4752-a6d0-79d876348acf&quot;&gt;Com between pulsar brokers and producer/consumers : &lt;a href=&quot;https://pulsar.apache.org/docs/en/develop-binary-protocol/&quot;&gt;https://pulsar.apache.org/docs/en/develop-binary-protocol/&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph fa00bb8b-e45e-4752-a6d0-79d876348acf--&gt;
&lt;!-- begin paragraph 84c9340d-b5b6-4b15-b43f-8cba3a0869d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-84c9340d-b5b6-4b15-b43f-8cba3a0869d7&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 84c9340d-b5b6-4b15-b43f-8cba3a0869d7--&gt;
&lt;!-- begin paragraph 6679e7de-4274-4c74-a236-9d6512a445f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6679e7de-4274-4c74-a236-9d6512a445f3&quot;&gt;Kafka Headers : &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-82+-+Add+Record+Headers+-+Typed&quot;&gt;https://cwiki.apache.org/confluence/display/KAFKA/KIP-82+-+Add+Record+Headers+-+Typed&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 6679e7de-4274-4c74-a236-9d6512a445f3--&gt;
&lt;!-- begin paragraph 1e96f346-a652-4ce5-9620-2965f2f4a8ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1e96f346-a652-4ce5-9620-2965f2f4a8ad&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1e96f346-a652-4ce5-9620-2965f2f4a8ad--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:004aa498a4ce461bb6e2d7621f66a6c3</id>
    <title>Univalence à Devoxx France 2019</title>
    <updated>2019-04-23T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/univalence-a-devoxx-france-2019.html"/>
    <!--summary Retour d&#039;Univalence sur le Devoxx France 2019 avec toute l&#039;équipe Univalence. Un événement où était abordé Kotlin, Kafka, Kubernetes et bien plus encore.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Devoxx"></category>    <category term="clojure"></category>    <category term="Scala"></category>    <category term="Data"></category>    <content type="html">
&lt;!-- begin paragraph 6252e3b0-0e27-403a-95cc-c2d096e0cd40--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6252e3b0-0e27-403a-95cc-c2d096e0cd40&quot;&gt;Univalence était présente à Devoxx France 2019. Parmi les sujets abordés dans cette édition : Kotlin, Kafka, Kubernetes, Quarkus et GraalVM, du front, du microservice et de la data. Mais aussi de la bienveillance au travail, du télétravail et de l&amp;#39;histoire du Web.&lt;/p&gt;
&lt;!-- end paragraph 6252e3b0-0e27-403a-95cc-c2d096e0cd40--&gt;
&lt;!-- begin paragraph 379084bf-a02a-4264-921d-10aab1c5b8b0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-379084bf-a02a-4264-921d-10aab1c5b8b0&quot;&gt;Nous étions nous-mêmes venus pour vous présenter de tout sauf du Java !&lt;/p&gt;
&lt;!-- end paragraph 379084bf-a02a-4264-921d-10aab1c5b8b0--&gt;
&lt;!-- begin paragraph 33d68d1c-087c-45c8-9224-ad558e7d32b3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-33d68d1c-087c-45c8-9224-ad558e7d32b3&quot;&gt;Retrouvez ci-dessous les slides de nos deux sessions :&lt;/p&gt;
&lt;!-- end paragraph 33d68d1c-087c-45c8-9224-ad558e7d32b3--&gt;
&lt;!-- begin paragraph 7778ec25-8bec-4cf7-9c8a-1b994904e675--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7778ec25-8bec-4cf7-9c8a-1b994904e675&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7778ec25-8bec-4cf7-9c8a-1b994904e675--&gt;&lt;div class=&quot;block-embed level-0&quot;&gt;&lt;/div&gt;
&lt;!-- begin paragraph dce042f5-aa10-4bde-8cd4-2feb47ef8b5c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dce042f5-aa10-4bde-8cd4-2feb47ef8b5c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph dce042f5-aa10-4bde-8cd4-2feb47ef8b5c--&gt;
&lt;!-- begin paragraph 1236b896-316b-490b-8ada-054a3dd9cd41--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1236b896-316b-490b-8ada-054a3dd9cd41&quot;&gt;Slides de la session &amp;quot;Avez-vous pris des nouvelles de Scala ?&amp;quot; par François Sarradin&lt;/p&gt;
&lt;!-- end paragraph 1236b896-316b-490b-8ada-054a3dd9cd41--&gt;
&lt;!-- begin bookmark 68406a26-5eb0-4583-94f2-dce4b1f4ebd1--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-68406a26-5eb0-4583-94f2-dce4b1f4ebd1&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;/blog/assets/favicon_4056d22df4566d3b688d4a513b39eafc.png&quot;&gt; &lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 68406a26-5eb0-4583-94f2-dce4b1f4ebd1--&gt;
&lt;!-- begin paragraph 9fbce41c-042c-4683-97d6-53704e227f24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9fbce41c-042c-4683-97d6-53704e227f24&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9fbce41c-042c-4683-97d6-53704e227f24--&gt;
&lt;!-- begin paragraph 27568865-9db6-4f6e-a248-fd360d17e0ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27568865-9db6-4f6e-a248-fd360d17e0ee&quot;&gt;Nous avons aussi coorganisé le BoF du ParisDataEng&amp;#39; avec Sara Himmich de Converteo pour donner un aperçu de l&amp;#39;évolution du meetup et Bachir Aït M&amp;#39;Barek de DataGemme qui nous a fait l&amp;#39;honneur d&amp;#39;une présentation sur le besoin de mieux rapprocher data scientist et data engineer.&lt;/p&gt;
&lt;!-- end paragraph 27568865-9db6-4f6e-a248-fd360d17e0ee--&gt;
&lt;!-- begin paragraph 16ee4317-1826-4d23-96b4-a2bd761c7352--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-16ee4317-1826-4d23-96b4-a2bd761c7352&quot;&gt;Nous souhaitons remercier l&amp;#39;équipe d&amp;#39;organisation de Devoxx France pour leur accueil, la richesse de l&amp;#39;événement et les rencontres.&lt;/p&gt;
&lt;!-- end paragraph 16ee4317-1826-4d23-96b4-a2bd761c7352--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/004aa498-a4ce-461b-b6e2-d7621f66a6c3/img_20190418_183909.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:31b8123126234aa993c532a8693a0691</id>
    <title>Retour sur Scalar 2019</title>
    <updated>2019-04-18T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/retour-sur-scalar-2019.html"/>
    <!--summary -->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="Scala"></category>    <category term="Scalar"></category>    <category term="Conférence"></category>    <content type="html">
&lt;!-- begin heading_1 f710bcca-67d9-4c96-9216-a4572657566d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f710bcca-67d9-4c96-9216-a4572657566d&quot;&gt;Dzień dobry&lt;/h2&gt;
&lt;!-- end heading_1 f710bcca-67d9-4c96-9216-a4572657566d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/scalar.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 69e6ad3f-ee23-41c7-865d-bf418eb87023--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69e6ad3f-ee23-41c7-865d-bf418eb87023&quot;&gt;Scalar est une conférence sur Scala se déroulant à Varsovie, Pologne depuis 2014. Cette année, deux membres de l&amp;#39;équipe ont eu le plaisir de participer à l&amp;#39;édition 2019 qui s&amp;#39;est déroulée sur deux journées, du 5 au 6 avril 2019.&lt;/p&gt;
&lt;!-- end paragraph 69e6ad3f-ee23-41c7-865d-bf418eb87023--&gt;
&lt;!-- begin paragraph 845a1d1d-3c5f-475a-a3ce-1d02cff9d8c9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-845a1d1d-3c5f-475a-a3ce-1d02cff9d8c9&quot;&gt;Les deux membres de l&amp;#39;équipe sont : &lt;/p&gt;
&lt;!-- end paragraph 845a1d1d-3c5f-475a-a3ce-1d02cff9d8c9--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Harrison Cheng, Data Engineer à Univalence depuis Mars 2019&lt;/li&gt;&lt;li&gt;Philippe Hong, Data Engineer à Univalence depuis Juillet 2017&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 8c90af02-8084-4fac-b6e2-4334f4c76ba7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c90af02-8084-4fac-b6e2-4334f4c76ba7&quot;&gt;Sur ces deux journées de conférences, il n&amp;#39;y a eu pas moins de 22 talks de 30 minutes (à l&amp;#39;exception du premier talk de Jon De Goes qui a duré un peu plus longtemps) sur des sujets variant du challenge de construire une architecture d&amp;#39;event sourcing purement fonctionnelle à une présentation de Kotlin pour les développeurs Scala. &lt;/p&gt;
&lt;!-- end paragraph 8c90af02-8084-4fac-b6e2-4334f4c76ba7--&gt;
&lt;!-- begin paragraph bc68b6f5-3a3a-4a1f-9f4c-eb7ae9d865a2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bc68b6f5-3a3a-4a1f-9f4c-eb7ae9d865a2&quot;&gt;Nous allons, dans cet article, survoler différents talks intéressants ayant pris place pendant la conférence.&lt;/p&gt;
&lt;!-- end paragraph bc68b6f5-3a3a-4a1f-9f4c-eb7ae9d865a2--&gt;
&lt;!-- begin heading_1 70c1d698-a594-43e9-8260-11bae1c37c68--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-70c1d698-a594-43e9-8260-11bae1c37c68&quot;&gt;Jour 1&lt;/h2&gt;
&lt;!-- end heading_1 70c1d698-a594-43e9-8260-11bae1c37c68--&gt;
&lt;!-- begin paragraph 8f3df049-0d71-48e4-bd12-9457f53d9450--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8f3df049-0d71-48e4-bd12-9457f53d9450&quot;&gt;Scalar s&amp;#39;est déroulé au POLIN Muzeum Historii Żydów Polskich (Musée de l&amp;#39;Histoire des Juifs polonais) non loin de la vieille ville de Varsovie.&lt;/p&gt;
&lt;!-- end paragraph 8f3df049-0d71-48e4-bd12-9457f53d9450--&gt;
&lt;!-- begin paragraph cdaed672-58cc-413d-9fb5-8d517a1e0d5c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cdaed672-58cc-413d-9fb5-8d517a1e0d5c&quot;&gt;Après avoir passé la sécurité, nous sommes accueilli par des membres du staff qui nous dirigent vers la salle de conférence. Scalar étant une conférence n&amp;#39;ayant qu&amp;#39;une seule track, cette salle de conférence a été utilisée pour toute la conférence.&lt;/p&gt;
&lt;!-- end paragraph cdaed672-58cc-413d-9fb5-8d517a1e0d5c--&gt;
&lt;!-- begin heading_2 d6b4a7ab-fc5a-452f-9efd-ed9d2d4b769a--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d6b4a7ab-fc5a-452f-9efd-ed9d2d4b769a&quot;&gt;atomically { &lt;span class=&quot;text-color-red&quot;&gt;delete&lt;/span&gt; your actors } - John A. De Goes (ft. Wiem Zine Elabadine)&lt;/h3&gt;
&lt;!-- end heading_2 d6b4a7ab-fc5a-452f-9efd-ed9d2d4b769a--&gt;
&lt;!-- begin paragraph 85aa7c7e-c11f-4f23-a947-eddc31d72dee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-85aa7c7e-c11f-4f23-a947-eddc31d72dee&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 85aa7c7e-c11f-4f23-a947-eddc31d72dee--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/delete_actors.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 8e280ded-c02f-48b7-a1c5-353ae8b6399d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e280ded-c02f-48b7-a1c5-353ae8b6399d&quot;&gt;Talk d&amp;#39;ouverture de la conférence, John de Goes accompagné de Wiem Zine présentent une nouvelle fonctionnalité pour ZIO, une librairie permettant de gérer votre code à effet, celle de pouvoir faire des transactions i.e. STM (Software Transactional Memory). Cela permet de résoudre le problème de concurrence en Scala de manière alternative aux autres méthodes (lock, actors,...).&lt;br /&gt;
La présentation a pris le parti de raconter une histoire humouristique, celle d&amp;#39;un voleur parvenant à voler des millions avec des exploits informatique basés sur la concurrence, d&amp;#39;un héros qui est le STM et de nous même, supposés devenir des héros grâce à ZIO.&lt;/p&gt;
&lt;!-- end paragraph 8e280ded-c02f-48b7-a1c5-353ae8b6399d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 7777c3de-dbc5-4737-9f6b-98fde14cbd10--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7777c3de-dbc5-4737-9f6b-98fde14cbd10&quot;&gt;S&amp;#39;ensuit une présentation des fonctionnalités de ZIO STM avec la promesse que faire de la programmation concurrente avec ZIO STM est très accessible.&lt;/p&gt;
&lt;!-- end paragraph 7777c3de-dbc5-4737-9f6b-98fde14cbd10--&gt;
&lt;!-- begin heading_2 9ac30954-e146-47dd-b13b-12f81ee23d44--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-9ac30954-e146-47dd-b13b-12f81ee23d44&quot;&gt;FS2 - Crash course - Lukasz Byczynski&lt;/h3&gt;
&lt;!-- end heading_2 9ac30954-e146-47dd-b13b-12f81ee23d44--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/fs2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph c617b62e-f908-4c69-9369-edf95284ff75--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c617b62e-f908-4c69-9369-edf95284ff75&quot;&gt;Cette présentation de 10 minutes nous décrit &lt;a href=&quot;https://github.com/functional-streams-for-scala/fs2&quot;&gt;FS2&lt;/a&gt;, une bibliothèque de streaming I/O. FS2 vient de FSS ⇒ &lt;b&gt;F&lt;/b&gt;unctional &lt;b&gt;S&lt;/b&gt;treams for &lt;b&gt;S&lt;/b&gt;cala. Lukasz nous décrit le stream comme étant des itérateurs dopés aux stéroïdes.&lt;/p&gt;
&lt;!-- end paragraph c617b62e-f908-4c69-9369-edf95284ff75--&gt;
&lt;!-- begin paragraph 74ef4ea2-508c-4c28-9858-445e86429740--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-74ef4ea2-508c-4c28-9858-445e86429740&quot;&gt;Comment commencer avec FS2 ? &lt;/p&gt;
&lt;!-- end paragraph 74ef4ea2-508c-4c28-9858-445e86429740--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Devenez familier avec la classe Stream&lt;/li&gt;&lt;li&gt;Essayez les méthodes covary et eval&lt;/li&gt;&lt;li&gt;Jouez avec la classe Pull&lt;/li&gt;&lt;li&gt;Regardez le package concurrent&lt;/li&gt;&lt;li&gt;Utilisez FS2 avec d&amp;#39;autres librairies comme Akka, Monix, etc...&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 4e934be0-585c-44b5-b250-249653bfb5e9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4e934be0-585c-44b5-b250-249653bfb5e9&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4e934be0-585c-44b5-b250-249653bfb5e9--&gt;
&lt;!-- begin heading_2 b6b20e17-9a05-4a80-b3a3-cbd7882b1b0c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b6b20e17-9a05-4a80-b3a3-cbd7882b1b0c&quot;&gt;The last frontier and beyond - Valentin Kasas&lt;/h3&gt;
&lt;!-- end heading_2 b6b20e17-9a05-4a80-b3a3-cbd7882b1b0c--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/last_frontier.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 91ccea83-bb20-4ce1-b3a9-c1e22c1c7b15--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-91ccea83-bb20-4ce1-b3a9-c1e22c1c7b15&quot;&gt;Nous commenceons ce talk avec les bienfaits de la programmation fonctionnelle, à savoir la possibilité d&amp;#39;abstraire du code afin d&amp;#39;éviter la répétition inutile, de passer moins de temps à chasser des bugs, etc. La programmation fonctionnelle est aussi très utile pour modéliser la donnée et les effets.&lt;/p&gt;
&lt;!-- end paragraph 91ccea83-bb20-4ce1-b3a9-c1e22c1c7b15--&gt;
&lt;!-- begin paragraph da937d82-56bf-4f7e-99fc-68fd337b9714--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-da937d82-56bf-4f7e-99fc-68fd337b9714&quot;&gt;Nous utilisons dans tous les projets du même code pour sérialiser de la donnée, lire des fichiers de configuration, pretty print, etc... et tout ce code partage une caractéristique : le code dépend de la structure de la donnée.&lt;/p&gt;
&lt;!-- end paragraph da937d82-56bf-4f7e-99fc-68fd337b9714--&gt;
&lt;!-- begin paragraph b331d496-5c15-40bb-9733-21f50860d084--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b331d496-5c15-40bb-9733-21f50860d084&quot;&gt;Donc si nous pouvons abstraire sur cette structure nous pouvons alors bénéficier des avantages évoqués précedemmment.&lt;/p&gt;
&lt;!-- end paragraph b331d496-5c15-40bb-9733-21f50860d084--&gt;
&lt;!-- begin paragraph 386fb721-d95b-4f7c-b6d1-50495e40c9b6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-386fb721-d95b-4f7c-b6d1-50495e40c9b6&quot;&gt;Une manière de faire cela est d&amp;#39;utiliser des macros.&lt;/p&gt;
&lt;!-- end paragraph 386fb721-d95b-4f7c-b6d1-50495e40c9b6--&gt;
&lt;!-- begin paragraph b8ee9480-dd83-4351-a67e-3921ce550d0a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b8ee9480-dd83-4351-a67e-3921ce550d0a&quot;&gt;Le problème survient lorsque nous devons faire évoluer des ADTs et même si nous pouvons garder des anciennes versions de nos ADTs lorsque nos données évoluent, ce n&amp;#39;est pas très réaliste sur des projets trop larges ou qui évoluent beaucoup.&lt;/p&gt;
&lt;!-- end paragraph b8ee9480-dd83-4351-a67e-3921ce550d0a--&gt;
&lt;!-- begin paragraph f27b4a09-807e-4ce1-af5e-3877cc726e93--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f27b4a09-807e-4ce1-af5e-3877cc726e93&quot;&gt;La solution proposée ici est tout simplement le schéma. Nous pouvons en résumé pourquoi pas nous débarasser de nos ADTs et d&amp;#39;utiliser simplement le concept de schéma pour gérer les évolutions.&lt;/p&gt;
&lt;!-- end paragraph f27b4a09-807e-4ce1-af5e-3877cc726e93--&gt;
&lt;!-- begin paragraph 0cd8ee7a-8a36-4c23-a6c8-8f3ca3e66566--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0cd8ee7a-8a36-4c23-a6c8-8f3ca3e66566&quot;&gt;Ainsi nous devons constituer notre propre version de schéma et pour cela nous avons besoin de &lt;/p&gt;
&lt;!-- end paragraph 0cd8ee7a-8a36-4c23-a6c8-8f3ca3e66566--&gt;
&lt;!-- begin paragraph a7ed90f8-2cfe-42c1-9f6f-7eb411dca6a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a7ed90f8-2cfe-42c1-9f6f-7eb411dca6a7&quot;&gt;TODO IN ARTICLE&lt;/p&gt;
&lt;!-- end paragraph a7ed90f8-2cfe-42c1-9f6f-7eb411dca6a7--&gt;
&lt;!-- begin paragraph 4c2885f7-2d59-481a-a95c-cb13804a6438--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4c2885f7-2d59-481a-a95c-cb13804a6438&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4c2885f7-2d59-481a-a95c-cb13804a6438--&gt;
&lt;!-- begin heading_2 b196e17f-3ccf-406d-b1c7-d2009306123e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b196e17f-3ccf-406d-b1c7-d2009306123e&quot;&gt;Scalatest : you&amp;#39;re asserting it wrong&lt;/h3&gt;
&lt;!-- end heading_2 b196e17f-3ccf-406d-b1c7-d2009306123e--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/scalatest.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 4c44e84f-b6e0-4846-8ccc-16b904e8915a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4c44e84f-b6e0-4846-8ccc-16b904e8915a&quot;&gt;Dans ce talk, Jacek Kunicki nous explique comment bien faire ses assert avec la bibliothèque ScalaTest. En effet, il n&amp;#39;est pas bien intéressant d&amp;#39;avoir par exemple &lt;code&gt;true was not false&lt;/code&gt; comme retour lorsqu&amp;#39;un test échoue.&lt;/p&gt;
&lt;!-- end paragraph 4c44e84f-b6e0-4846-8ccc-16b904e8915a--&gt;
&lt;!-- begin paragraph 08fd8e28-31fd-4dfd-98a0-330fce3682ab--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-08fd8e28-31fd-4dfd-98a0-330fce3682ab&quot;&gt;Par exemple si on a  &lt;code&gt;val option = None&lt;/code&gt;:&lt;/p&gt;
&lt;!-- end paragraph 08fd8e28-31fd-4dfd-98a0-330fce3682ab--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph de8eaa3a-5d3e-4362-8b64-bfeb36666f0b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-de8eaa3a-5d3e-4362-8b64-bfeb36666f0b&quot;&gt;Il vaut mieux utiliser &lt;code&gt;option should be (&amp;#39;defined)&lt;/code&gt;qui renvoit &lt;code&gt;None was not defined&lt;/code&gt; plutôt que &lt;code&gt;option.isDefined should be (true)&lt;/code&gt; qui renvoit &lt;code&gt;false was not true&lt;/code&gt;, ce qui est moins explicite.&lt;/p&gt;
&lt;!-- end paragraph de8eaa3a-5d3e-4362-8b64-bfeb36666f0b--&gt;
&lt;!-- begin heading_1 00cd534e-3f25-4fa4-a310-9203bcaa6dfb--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-00cd534e-3f25-4fa4-a310-9203bcaa6dfb&quot;&gt;Jour 2&lt;/h2&gt;
&lt;!-- end heading_1 00cd534e-3f25-4fa4-a310-9203bcaa6dfb--&gt;
&lt;!-- begin paragraph b610aac0-adf7-4ff6-9706-5610fdcebd9a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b610aac0-adf7-4ff6-9706-5610fdcebd9a&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b610aac0-adf7-4ff6-9706-5610fdcebd9a--&gt;
&lt;!-- begin heading_2 18281ea6-e84a-441e-a1d1-fdac3fbadea1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-18281ea6-e84a-441e-a1d1-fdac3fbadea1&quot;&gt;Cellular Automata: How to become an artist with a few lines of code&lt;/h3&gt;
&lt;!-- end heading_2 18281ea6-e84a-441e-a1d1-fdac3fbadea1--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/automata.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph ad48698a-3095-44e7-a0eb-553d35ee7e47--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ad48698a-3095-44e7-a0eb-553d35ee7e47&quot;&gt;Un talk un peu différent des autres. Maciej Gorywoda nous explique comment coder des automates cellulaires en Scala avec comme exemple le jeu de la vie et la fourmi de langton.&lt;/p&gt;
&lt;!-- end paragraph ad48698a-3095-44e7-a0eb-553d35ee7e47--&gt;
&lt;!-- begin paragraph f9f45c2c-56e6-43aa-9923-3e458ce87e18--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9f45c2c-56e6-43aa-9923-3e458ce87e18&quot;&gt;&lt;a href=&quot;https://github.com/makingthematrix/ca_art&quot;&gt;https://github.com/makingthematrix/ca_art&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph f9f45c2c-56e6-43aa-9923-3e458ce87e18--&gt;
&lt;!-- begin heading_2 273b804e-b737-4fde-a726-aa27f1d7c99a--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-273b804e-b737-4fde-a726-aa27f1d7c99a&quot;&gt;Functional Concurrency in Scala 101&lt;/h3&gt;
&lt;!-- end heading_2 273b804e-b737-4fde-a726-aa27f1d7c99a--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/functional_concurrency_101.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph a717f69a-6c5e-4610-a570-593bf60e388c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a717f69a-6c5e-4610-a570-593bf60e388c&quot;&gt;Dans ce talk, Piotr Gawryś nous défini plusieurs concepts de la concurrence en programmation fonctionnelle dans Scala (principalement avec cats-effect, fs2, monix, zio).&lt;/p&gt;
&lt;!-- end paragraph a717f69a-6c5e-4610-a570-593bf60e388c--&gt;
&lt;!-- begin paragraph 55bbd9ec-62c7-4864-bf45-13daa4cfcc29--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-55bbd9ec-62c7-4864-bf45-13daa4cfcc29&quot;&gt;Il commence par expliquer comment fonctionne la concurrence en général dans la jvm (définition concurrence et parallélisme, threads dans la jvm, context switch, threads pools, asynchronous boundary, task scheduling), puis enchaîne sur les différentes notions introduites par la programmation fonctionnelle (light asynchronous boundaries dans monix et cats-effects IO, fairness qui est la probabilité que différentes tasks peuvent s&amp;#39;exécuter, les green threads qui sont des threads programmés par la jvm plutot que par l&amp;#39;OS, les fibers qui sont comme des threads plus légers). &lt;/p&gt;
&lt;!-- end paragraph 55bbd9ec-62c7-4864-bf45-13daa4cfcc29--&gt;
&lt;!-- begin paragraph 85ef18b9-399c-4392-9749-170a8d17848d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-85ef18b9-399c-4392-9749-170a8d17848d&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 85ef18b9-399c-4392-9749-170a8d17848d--&gt;
&lt;!-- begin heading_2 91f98cc5-82da-4fcd-a1b3-25c4b1f0ea2e--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-91f98cc5-82da-4fcd-a1b3-25c4b1f0ea2e&quot;&gt;Taming your herd of cats&lt;/h3&gt;
&lt;!-- end heading_2 91f98cc5-82da-4fcd-a1b3-25c4b1f0ea2e--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/shjhj3q_1.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 19865eae-fd33-49da-bf59-9566f347f1d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-19865eae-fd33-49da-bf59-9566f347f1d7&quot;&gt;Ce talk est une présentation pour les personnes ne connaissant pas ou peu Cats, une librairie permettant de faire de la programmation fonctionnelle avancée. Différentes parties de la librairie sont présentées avec des exemples d&amp;#39;utilisation accompagnés d&amp;#39;astuces.&lt;/p&gt;
&lt;!-- end paragraph 19865eae-fd33-49da-bf59-9566f347f1d7--&gt;
&lt;!-- begin paragraph 08da9998-9cd4-4a6e-9534-f5edbcd7f490--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-08da9998-9cd4-4a6e-9534-f5edbcd7f490&quot;&gt;L&amp;#39;intérêt de ce type de talk est de faire découvrir en relative douceur des concepts plus poussés en programmation fonctionnelle en montrant ce qu&amp;#39;il est possible de réaliser avec ces concepts.&lt;br /&gt;
Un exemple connu est le combineAll pour les monoïdes qui permet de réduire une collection.&lt;/p&gt;
&lt;!-- end paragraph 08da9998-9cd4-4a6e-9534-f5edbcd7f490--&gt;
&lt;!-- begin heading_2 91945a2e-6e86-4024-9a92-fe656d8bebc1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-91945a2e-6e86-4024-9a92-fe656d8bebc1&quot;&gt;11 Little life-enhancing libraries&lt;/h3&gt;
&lt;!-- end heading_2 91945a2e-6e86-4024-9a92-fe656d8bebc1--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/eleven.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 2a85a7d3-d299-4c2f-8066-6f83e52b8e9d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2a85a7d3-d299-4c2f-8066-6f83e52b8e9d&quot;&gt;Jon Pretty nous demande de l&amp;#39;aide, il a trop de bibliothèques à gérer ! Dans ce talk, 11 bibliothèques nous sont présentées allant du parsing CSV au lightweight lens syntax, en passant par de l&amp;#39;inline pattern matching.&lt;/p&gt;
&lt;!-- end paragraph 2a85a7d3-d299-4c2f-8066-6f83e52b8e9d--&gt;
&lt;!-- begin paragraph 56223f25-9714-495c-b3d3-b7291b6e965f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-56223f25-9714-495c-b3d3-b7291b6e965f&quot;&gt;Nous traiterons ce talk plus en détail dans un prochain article. Restez à l&amp;#39;écoute :)&lt;/p&gt;
&lt;!-- end paragraph 56223f25-9714-495c-b3d3-b7291b6e965f--&gt;
&lt;!-- begin heading_2 332984ba-450d-4964-ae9e-ebc7dd061387--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-332984ba-450d-4964-ae9e-ebc7dd061387&quot;&gt;Kotlin for Scala developers&lt;/h3&gt;
&lt;!-- end heading_2 332984ba-450d-4964-ae9e-ebc7dd061387--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/kotlin.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 1295df7b-e0a1-4bca-92ad-79e01975fe57--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1295df7b-e0a1-4bca-92ad-79e01975fe57&quot;&gt;Ce qu&amp;#39;il faut retenir de ce talk, c&amp;#39;est que John A. De Goes &amp;lt;3 Kotlin.&lt;/p&gt;
&lt;!-- end paragraph 1295df7b-e0a1-4bca-92ad-79e01975fe57--&gt;
&lt;!-- begin paragraph 531a3466-e7b8-410b-9991-7038a961efda--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-531a3466-e7b8-410b-9991-7038a961efda&quot;&gt;Blague à part, ce talk de Jarek Ratajski nous présente Kotlin comme un Java ++ plutôt qu&amp;#39;un Scala - -. Il nous expose les différences entre Scala et Kotlin, les avantages à utiliser Kotlin (immutabilité, décision pragmatique sur l&amp;#39;absence d&amp;#39;obligation de compiler check, compilateur rapide, lambas et tailrec,...) et les limitations (pas de for comprehension, pas de type classes (même s&amp;#39;il y a un idiome spécifique pour des type classes dans la bibliothèque &lt;a href=&quot;https://arrow-kt.io/&quot;&gt;arrow&lt;/a&gt;), pattern matching limité...). &lt;/p&gt;
&lt;!-- end paragraph 531a3466-e7b8-410b-9991-7038a961efda--&gt;
&lt;!-- begin heading_1 805f9627-fe11-4475-b80a-61d8886cd577--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-805f9627-fe11-4475-b80a-61d8886cd577&quot;&gt;Scalar pour l&amp;#39;équipe&lt;/h2&gt;
&lt;!-- end heading_1 805f9627-fe11-4475-b80a-61d8886cd577--&gt;
&lt;!-- begin paragraph e555025b-6102-4f72-94c2-e98a077a31d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e555025b-6102-4f72-94c2-e98a077a31d1&quot;&gt;Commentaire Philippe : Dans l&amp;#39;ensemble les talks étaient bien présentés et réussi. Il y a eu beaucoup de talks sur le sujet de la concurrence et des acteurs. Les trois talks que j&amp;#39;ai préféré sont :&lt;/p&gt;
&lt;!-- end paragraph e555025b-6102-4f72-94c2-e98a077a31d1--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;atomically { &lt;span class=&quot;text-color-red&quot;&gt;delete&lt;/span&gt; your actors }&lt;/li&gt;&lt;li&gt;11 Little life-enhancing libraries&lt;/li&gt;&lt;li&gt;The last frontier and beyond&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph cab540c2-07a8-491c-88c5-5c3efe148740--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cab540c2-07a8-491c-88c5-5c3efe148740&quot;&gt;Avec en mention honorable Kotlin for Scala developers qui m&amp;#39;a personnellement donné envie de regarder d&amp;#39;un peu plus près Kotlin.&lt;/p&gt;
&lt;!-- end paragraph cab540c2-07a8-491c-88c5-5c3efe148740--&gt;
&lt;!-- begin paragraph aac3f44b-907c-47c9-86ca-f97e7c310298--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aac3f44b-907c-47c9-86ca-f97e7c310298&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph aac3f44b-907c-47c9-86ca-f97e7c310298--&gt;
&lt;!-- begin paragraph a3149b5c-33b8-4baf-a17c-13164b8bee27--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a3149b5c-33b8-4baf-a17c-13164b8bee27&quot;&gt;Commentaire Harrison : Scalar a été ma première conférence sur Scala, et c&amp;#39;était très intéressant. En tant que néophyte dans le domaine de la programmation fonctionnelle, je n&amp;#39;ai pas pu comprendre tout ce qui était expliqué dans les différents talks, mais cela m&amp;#39;a permis de voir les différentes bibliothèques Scala à surveiller et voir jusqu&amp;#39;à quel point vont les experts en Scala. J&amp;#39;ai apprécié le fait qu&amp;#39;il n&amp;#39;y avait qu&amp;#39;une scène et donc ne pas avoir à choisir entre tel ou tel talk.&lt;/p&gt;
&lt;!-- end paragraph a3149b5c-33b8-4baf-a17c-13164b8bee27--&gt;
&lt;!-- begin paragraph 129cea35-d479-46db-a5fd-d308a5493961--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-129cea35-d479-46db-a5fd-d308a5493961&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 129cea35-d479-46db-a5fd-d308a5493961--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/31b81231-2623-4aa9-93c5-32a8693a0691/20190405_085459.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:a4e31fc83a624eada5c777a4023e3d6f</id>
    <title>Un aperçu d&#039;Apache Pulsar</title>
    <updated>2019-04-09T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/un-apercu-d-apache-pulsar.html"/>
    <!--summary -->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>        <category term="Apache Pulsar"></category>    <content type="html">
&lt;!-- begin paragraph 7b1b85cc-ff02-44d5-adcf-04486f323f01--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7b1b85cc-ff02-44d5-adcf-04486f323f01&quot;&gt;C&amp;#39;est quoi, ça marche comment ?&lt;/p&gt;
&lt;!-- end paragraph 7b1b85cc-ff02-44d5-adcf-04486f323f01--&gt;
&lt;!-- begin heading_1 f04bde3f-f284-4790-8537-308137c6fdc7--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-f04bde3f-f284-4790-8537-308137c6fdc7&quot;&gt;Un aperçu d’Apache Pulsar&lt;/h2&gt;
&lt;!-- end heading_1 f04bde3f-f284-4790-8537-308137c6fdc7--&gt;
&lt;!-- begin paragraph 9499df0b-bef8-4343-9c61-de67dee1dd7a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9499df0b-bef8-4343-9c61-de67dee1dd7a&quot;&gt;&lt;a href=&quot;https://pulsar.apache.org/&quot;&gt;Apache Pulsar &lt;/a&gt;est un système distribué open-source dédié à la messagerie et basé sur le modèle “publish-subscribe”. Il a été développé chez Yahoo et mis en production depuis trois ans pour des applications comme Yahoo Mail, Yahoo Finance, Yahoo Sports, Flickr, Sherpa ou encore Gemini Ads. Depuis fin 2016, le projet a été mis en open-source sous la fondation Apache.&lt;/p&gt;
&lt;!-- end paragraph 9499df0b-bef8-4343-9c61-de67dee1dd7a--&gt;
&lt;!-- begin paragraph 1a032483-64c4-48fa-85c4-5c9d727a3ec4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1a032483-64c4-48fa-85c4-5c9d727a3ec4&quot;&gt;Dans cet article, nous allons explorer les différents concepts constituant Pulsar ainsi que son architecture.&lt;/p&gt;
&lt;!-- end paragraph 1a032483-64c4-48fa-85c4-5c9d727a3ec4--&gt;
&lt;!-- begin heading_2 3c740e16-5275-48c7-b9a4-4179d9142c3c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3c740e16-5275-48c7-b9a4-4179d9142c3c&quot;&gt;&lt;b&gt;Publish-Subscribe (pub-sub) et la triade : producteur, topic et consommateur&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 3c740e16-5275-48c7-b9a4-4179d9142c3c--&gt;
&lt;!-- begin paragraph c0c4eb38-7191-4020-aa45-c76cd140dd03--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c0c4eb38-7191-4020-aa45-c76cd140dd03&quot;&gt;&lt;b&gt;&lt;/b&gt;Pulsar fait partie des systèmes de messagerie utilisant le modèle du &lt;b&gt;publish-subscribe&lt;/b&gt; : la publication et l’abonnement. Ce modèle est composé de trois différents éléments permettant la transmission des messages : les &lt;b&gt;producteurs&lt;/b&gt;, les &lt;b&gt;topics&lt;/b&gt; et les &lt;b&gt;consommateurs&lt;/b&gt;. Les producteurs et les consommateurs, comme leur nom l’indique, sont des applications produisant et consommant des messages. Dans le vide ? Non ! C’est là que le topic intervient. Le topic joue le rôle d’intermédiaire entre les producteurs et les consommateurs et stockent les messages. En résumé, les producteurs publient leur messages dans différents topics et les consommateurs s’abonnent à différents topics pour consommer les messages. Cette indirection permet de découpler les producteurs des consommateurs, ils ne se parlent pas directement.&lt;/p&gt;
&lt;!-- end paragraph c0c4eb38-7191-4020-aa45-c76cd140dd03--&gt;
&lt;!-- begin heading_2 e2071f33-3a43-4ead-a61d-082d84700305--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e2071f33-3a43-4ead-a61d-082d84700305&quot;&gt;&lt;b&gt;Topic, namespace, tenant&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 e2071f33-3a43-4ead-a61d-082d84700305--&gt;
&lt;!-- begin paragraph 838ceace-0daa-4156-b208-19ab1589c6e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-838ceace-0daa-4156-b208-19ab1589c6e1&quot;&gt;Le topic est la figure centrale de Pulsar puisque c’est par là que transitent et sont potentiellement stockés tous les messages. Les topics sont comme des stockages structurés pour l’écriture de logs (&lt;i&gt;log-structured storage&lt;/i&gt;), où chaque message possède un &lt;b&gt;offset&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 838ceace-0daa-4156-b208-19ab1589c6e1--&gt;
&lt;!-- begin paragraph 67227665-671d-4dcf-b3c4-60746f7226a4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67227665-671d-4dcf-b3c4-60746f7226a4&quot;&gt;Dans Pulsar, les topics ne sont pas indépendants, ni dispersés dans la nature. Un principe que Pulsar prône est d’être &lt;b&gt;multi-tenant&lt;/b&gt; (i.e. multi-entité). Pour ce faire, les topics doivent être organisés en &lt;b&gt;tenant&lt;/b&gt; et &lt;b&gt;namespace&lt;/b&gt;. Pour faire simple, un namespace contient plusieurs topics et un tenant contient plusieurs namespace. Cela permet d’avoir différents niveaux de permissions, réplications, d’expiration de messages et autres, entre les différents namespaces et tenant.&lt;/p&gt;
&lt;!-- end paragraph 67227665-671d-4dcf-b3c4-60746f7226a4--&gt;
&lt;!-- begin paragraph 1d0f728b-7cc6-48f3-8738-3a05b47444d1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d0f728b-7cc6-48f3-8738-3a05b47444d1&quot;&gt;Les topics dans Pulsar peuvent être &lt;b&gt;persistant&lt;/b&gt; ou &lt;b&gt;non-persistant&lt;/b&gt;. Persistant veut dire ici que les données sont stockées de manière durable dans un disque (ou plusieurs car nous sommes dans le monde distribué).&lt;/p&gt;
&lt;!-- end paragraph 1d0f728b-7cc6-48f3-8738-3a05b47444d1--&gt;
&lt;!-- begin paragraph 35593dab-f053-49ee-b597-5832fd58da24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35593dab-f053-49ee-b597-5832fd58da24&quot;&gt;Pour résumer, les noms des topics sont représentés par une URL de la forme suivante :&lt;/p&gt;
&lt;!-- end paragraph 35593dab-f053-49ee-b597-5832fd58da24--&gt;
&lt;!-- begin paragraph 2d973d28-0cce-4d21-9399-792f438acf76--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2d973d28-0cce-4d21-9399-792f438acf76&quot;&gt;&lt;code&gt;{persistent|non-persistent}://tenant/namespace/topic&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 2d973d28-0cce-4d21-9399-792f438acf76--&gt;
&lt;!-- begin heading_2 de4f18ae-b655-467e-b839-def26f2b5bbb--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-de4f18ae-b655-467e-b839-def26f2b5bbb&quot;&gt;&lt;b&gt;Subscription mode, multi-topic subscription&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 de4f18ae-b655-467e-b839-def26f2b5bbb--&gt;
&lt;!-- begin paragraph de1e2f2f-a57d-44a0-ac4e-40bf1680ed27--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-de1e2f2f-a57d-44a0-ac4e-40bf1680ed27&quot;&gt;Pour s’abonner à un topic, un consommateur ou un groupe de consommateurs peuvent établir un contrat appelé “&lt;b&gt;subscription&lt;/b&gt;”. Les “subscription” contiennent des métadonnées et un curseur. C’est ce curseur qui suit l’offset du topic courant pour chaque consommateur.&lt;/p&gt;
&lt;!-- end paragraph de1e2f2f-a57d-44a0-ac4e-40bf1680ed27--&gt;
&lt;!-- begin paragraph e5648064-1044-45a9-89b1-e1231e457e42--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5648064-1044-45a9-89b1-e1231e457e42&quot;&gt;Il existe trois différents “subscription” dans Pulsar :&lt;/p&gt;
&lt;!-- end paragraph e5648064-1044-45a9-89b1-e1231e457e42--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Exclusive subscription&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph cf1b94bf-25ad-4190-96bd-70251041327f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf1b94bf-25ad-4190-96bd-70251041327f&quot;&gt;Ce mode permet à un seul et uniquement consommateur de s’abonner à un topic.&lt;/p&gt;
&lt;!-- end paragraph cf1b94bf-25ad-4190-96bd-70251041327f--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh6.googleusercontent.com/rm5I5YCsl449GymVcYua9F40t_AqD0C5vXMeqvkVqJjFhawJL5SPUxaXlKaMPqDFHvXwRVTtsIipCIyxziJuRm8SmJN_JCwPQdAiuo3DMtILG2kjATa88Ji9DuL9Rt8v89qYWoDF&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Shared subscription&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f3569c02-beeb-4308-8495-e12f39e54dfa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f3569c02-beeb-4308-8495-e12f39e54dfa&quot;&gt;Ce mode permet à plusieurs consommateurs de s’abonner à un topic.&lt;/p&gt;
&lt;!-- end paragraph f3569c02-beeb-4308-8495-e12f39e54dfa--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/cmiBVvOzFOsQ8yq2oVidHnrRqxxGn9ak80MsvTVGIghZNsIjZ7vK_lG_mOWFs3-YaMhxDrENMeQDOPKC-DLyZP-o2TRSx6LWsFXI9mJ-UAoNv2NvlREaY7LZwz7wAbVKrrhjaNpb&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Failover subscription&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 1d2e3ebb-2be5-4c08-90d0-12fc56d13c3b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d2e3ebb-2be5-4c08-90d0-12fc56d13c3b&quot;&gt;Ce mode permet de définir plusieurs consommateurs pour un topic. Sauf qu’à la différence du &lt;i&gt;shared subscription&lt;/i&gt;, il n’y a qu’un seul consommateur abonné au topic. Les autres sont là au cas où le consommateur abonné se déconnecte (d’une manière ou d’une autre).&lt;/p&gt;
&lt;!-- end paragraph 1d2e3ebb-2be5-4c08-90d0-12fc56d13c3b--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/5L3eMG23HxunFo7dxh61tmc5i6JkZgddJczkcqhe3rAZ0xASuwq33Uomr6j0gBtk8CW8tzqX4w8yzxItt4pNtxNSqCS9wX26SrPZ_QkNykprkGXD4R7IJkANVCkQ81xLHBxB3iv3&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph a950111f-c659-4305-b7a5-190b80b3c89c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a950111f-c659-4305-b7a5-190b80b3c89c&quot;&gt;Cela permet de choisir la meilleure façon de consommer les données. Si l’ordre des données est importante (chaque donnée dépend des données précédentes), on préférera utiliser soit le mode “exclusive”, soit le mode “failover”. Sinon, on utilisera le mode “shared” où l’ordre des messages n’est pas garanti, mais qui permet de consommer plus rapidement avec plus de consommateurs.&lt;/p&gt;
&lt;!-- end paragraph a950111f-c659-4305-b7a5-190b80b3c89c--&gt;
&lt;!-- begin paragraph cba2a4a0-d580-4c59-baca-e87b3d3c0b81--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cba2a4a0-d580-4c59-baca-e87b3d3c0b81&quot;&gt;Les consommateurs peuvent également s’abonner à différents topics (à la condition que ceux-ci soient dans le même namespace). Il y a deux façons pour le faire :&lt;/p&gt;
&lt;!-- end paragraph cba2a4a0-d580-4c59-baca-e87b3d3c0b81--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Établir explicitement la liste des topics à lire.&lt;/li&gt;&lt;li&gt;Définir par regex la liste des topics (ex : &lt;code&gt;persistent://tenant/namespace/topic-*&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 4d63ae7b-a044-4d85-a358-53d2a830f47f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4d63ae7b-a044-4d85-a358-53d2a830f47f&quot;&gt;Dans ce cas-là, l’ordre des messages consommés n’est pas garanti (comme pour le shared subscription).&lt;/p&gt;
&lt;!-- end paragraph 4d63ae7b-a044-4d85-a358-53d2a830f47f--&gt;
&lt;!-- begin heading_2 01ff9fd0-0e25-4415-85d5-620a6a42d726--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-01ff9fd0-0e25-4415-85d5-620a6a42d726&quot;&gt;&lt;b&gt;Acknowledgement&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 01ff9fd0-0e25-4415-85d5-620a6a42d726--&gt;
&lt;!-- begin paragraph 2d202b10-33f4-477c-8bd4-7dfb7c8c7725--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2d202b10-33f4-477c-8bd4-7dfb7c8c7725&quot;&gt;Dans Pulsar, il est important pour un serveur de savoir si un consommateur a fini de récupérer un message afin de décider s’il peut le supprimer ou pas. Le consommateur doit donc envoyer un “&lt;b&gt;ack&lt;/b&gt;” (&lt;i&gt;acknowledgement&lt;/i&gt;) permettant de faire savoir au serveur qu’il a bien reçu le message.&lt;/p&gt;
&lt;!-- end paragraph 2d202b10-33f4-477c-8bd4-7dfb7c8c7725--&gt;
&lt;!-- begin paragraph ba2d910f-3227-48f0-bfd1-832984f0ad8d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba2d910f-3227-48f0-bfd1-832984f0ad8d&quot;&gt;Ce “ack” est géré par les curseurs dans les subscriptions et permet à un même message de ne pas être consommé plusieurs fois. Le ack peut être fait de manière cumulative (tous les messages jusqu’à un certain point sont ack) ou individuel (un par un) :&lt;/p&gt;
&lt;!-- end paragraph ba2d910f-3227-48f0-bfd1-832984f0ad8d--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/F5PyFHBIFabDm59w-wAyiHXwrkLUi9airL7i2H7JsGd8BVQXsuzqgcD6oT85GUE99MGMCh2o_cAqtWTI63otEyTpuDQgr4uttAjDGzeaP6OQw1wS5o_irSDiDi2t6pkDtAieqKCb&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph e4dc840d-4c72-452c-8c2f-a3ea6cff0502--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e4dc840d-4c72-452c-8c2f-a3ea6cff0502&quot;&gt;Il permet également de définir les concepts d’expiration de message et rétention de message.&lt;/p&gt;
&lt;!-- end paragraph e4dc840d-4c72-452c-8c2f-a3ea6cff0502--&gt;
&lt;!-- begin paragraph 8cc163a1-9e52-48df-b3a5-456e805f224a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8cc163a1-9e52-48df-b3a5-456e805f224a&quot;&gt;&lt;i&gt;“Mais ce n&amp;#39;est pas la même chose ?”&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph 8cc163a1-9e52-48df-b3a5-456e805f224a--&gt;
&lt;!-- begin paragraph 7a106890-2754-4ab4-b47e-7fd860a36591--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7a106890-2754-4ab4-b47e-7fd860a36591&quot;&gt;Non ! Dans Pulsar par défaut, les messages non-ack ont un visa temporaire dans les serveurs et sont supprimés dès qu’ils sont ack. On peut prolonger ce visa lorsqu’ils sont ack en modifiant &lt;b&gt;la&lt;/b&gt; &lt;b&gt;rétention&lt;/b&gt; du message. À l’opposé, on peut diminuer le temps de ce visa lorsque les messages sont encore non-ack en modifiant &lt;b&gt;l’expiration&lt;/b&gt; du message (appelé aussi TTL = &lt;i&gt;Time To Live&lt;/i&gt;). Ces deux concepts permettent deux choses :&lt;/p&gt;
&lt;!-- end paragraph 7a106890-2754-4ab4-b47e-7fd860a36591--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Garder les données pendant un certain temps même s’ils ont été ack (rétention)&lt;/li&gt;&lt;li&gt;Supprimer les données après un certain temps même s’ils n’ont pas été ack (TTL)&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 662ce8ee-b23f-4267-9f96-4ddb9471de43--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-662ce8ee-b23f-4267-9f96-4ddb9471de43&quot;&gt;Cela permet également de voir Pulsar comme une queue si les messages ne sont pas gardés après ack, ou comme Apache Kafka où les messages sont persistés un certain temps après leur consommation.&lt;/p&gt;
&lt;!-- end paragraph 662ce8ee-b23f-4267-9f96-4ddb9471de43--&gt;
&lt;!-- begin heading_2 bee6477b-6f4f-421f-9e71-bcb1fffa4869--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-bee6477b-6f4f-421f-9e71-bcb1fffa4869&quot;&gt;&lt;b&gt;To sync or not to sync&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 bee6477b-6f4f-421f-9e71-bcb1fffa4869--&gt;
&lt;!-- begin paragraph 3aa0baa1-ed0a-496c-8a59-afa4a297cd01--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3aa0baa1-ed0a-496c-8a59-afa4a297cd01&quot;&gt;Les producteurs et consommateurs ont la possibilité d’envoyer ou de recevoir les messages de manière &lt;b&gt;synchrone&lt;/b&gt; ou &lt;b&gt;asynchrone&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 3aa0baa1-ed0a-496c-8a59-afa4a297cd01--&gt;
&lt;!-- begin paragraph cda3b9df-2256-40c5-966a-e401e2b69839--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cda3b9df-2256-40c5-966a-e401e2b69839&quot;&gt;Pour le producteur, un envoi synchrone correspond à une attente de confirmation de réception à chaque envoi de message et, dans le cas échéant, un échec d’envoi du message de la part du producteur. Un envoi asynchrone correspond à l’envoi direct d’un message dans une queue de messages, sans attente de confirmation de la part de Pulsar.&lt;/p&gt;
&lt;!-- end paragraph cda3b9df-2256-40c5-966a-e401e2b69839--&gt;
&lt;!-- begin paragraph de57b61d-2a22-4bee-a82d-600ea93b3251--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-de57b61d-2a22-4bee-a82d-600ea93b3251&quot;&gt;Pour le consommateur, une réception synchrone permet de bloquer l’exécution jusqu’à ce qu’un message arrive. Une réception asynchrone utilise une future value (CompletableFuture en Java par exemple).&lt;/p&gt;
&lt;!-- end paragraph de57b61d-2a22-4bee-a82d-600ea93b3251--&gt;
&lt;!-- begin paragraph 8d92a0b6-c23e-4a5e-8567-71028392a058--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d92a0b6-c23e-4a5e-8567-71028392a058&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8d92a0b6-c23e-4a5e-8567-71028392a058--&gt;
&lt;!-- begin heading_2 46798042-4fab-4d5a-b77f-fd104d3616f1--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-46798042-4fab-4d5a-b77f-fd104d3616f1&quot;&gt;&lt;b&gt;Architecture&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 46798042-4fab-4d5a-b77f-fd104d3616f1--&gt;
&lt;!-- begin paragraph abb15ac9-0d1a-476e-854a-970b06f36d92--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-abb15ac9-0d1a-476e-854a-970b06f36d92&quot;&gt;Maintenant que nous avons vu les différents concepts de Pulsar, regardons comment cela marche en vrai. Un cluster Pulsar est composé de trois principaux éléments :&lt;/p&gt;
&lt;!-- end paragraph abb15ac9-0d1a-476e-854a-970b06f36d92--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Des &lt;b&gt;brokers&lt;/b&gt; (serveurs) permettant d’acheminer les messages vers les différents consommateurs, communiquer avec la configuration Pulsar pour permettre la coordination de tâches, stocker des messages dans les “bookies” (instances BookKeeper), etc.&lt;/li&gt;&lt;li&gt;Un cluster &lt;b&gt;BookKeeper&lt;/b&gt; (bookies) permettant de persister les messages (c’est comme cela que les topics peuvent être persistants !).&lt;/li&gt;&lt;li&gt;Un cluster &lt;b&gt;ZooKeeper&lt;/b&gt; pour gérer les métadonnées des brokers et bookies et surveiller la santé de ceux-ci.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh6.googleusercontent.com/ZNNZyGOs8SgJpyzKAb2zBO0Sh9X6IqTK9yQJAuxR5rtaIGz1V1A3lpJ_OhyYyg8_6Y6gR_g7amL1CUnoerJvX9O9L2UWN1x3feNDjRJbhIqzjbVU5qeGRSLVkfelOjUshg7xw6SQ&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 741b335f-48a1-4e7f-aaa5-44bd13bc498a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-741b335f-48a1-4e7f-aaa5-44bd13bc498a&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 741b335f-48a1-4e7f-aaa5-44bd13bc498a--&gt;
&lt;!-- begin heading_2 b9c18fd1-ae99-47d4-882b-5e4c31258ed6--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b9c18fd1-ae99-47d4-882b-5e4c31258ed6&quot;&gt;&lt;i&gt;Divide ut regnes&lt;/i&gt;&lt;/h3&gt;
&lt;!-- end heading_2 b9c18fd1-ae99-47d4-882b-5e4c31258ed6--&gt;
&lt;!-- begin paragraph a47e66b2-6458-4b7f-9e66-2ff51ee789e6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a47e66b2-6458-4b7f-9e66-2ff51ee789e6&quot;&gt;Cette architecture en cluster permet d’établir les concepts vu précédemment de sorte que tout soit &lt;b&gt;distribué&lt;/b&gt;. En effet, chaque topic a la possibilité d’être divisé en partitions (appelés &lt;b&gt;partitioned topics&lt;/b&gt;). Ces partitions (représentant un morceau du topic) sont répartis automatiquement dans les différents broker Pulsar de manière la plus équitable possible (eg. s’il y a trois brokers et cinq partitions, deux brokers auront deux partitions chacun et la dernière partition sera affectée au troisième broker).&lt;/p&gt;
&lt;!-- end paragraph a47e66b2-6458-4b7f-9e66-2ff51ee789e6--&gt;
&lt;!-- begin paragraph 29755065-146e-4faa-847d-2ddea15ad7d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-29755065-146e-4faa-847d-2ddea15ad7d4&quot;&gt;Avec le mode &lt;i&gt;partitioned topic&lt;/i&gt;, les consommateurs et producteurs atteignent ainsi une plus grande capacité de traitement. Attention, cela est différent du mode &lt;i&gt;shared subscription&lt;/i&gt; où la consommation d’un topic est partagée entre plusieurs consommateurs !&lt;/p&gt;
&lt;!-- end paragraph 29755065-146e-4faa-847d-2ddea15ad7d4--&gt;
&lt;!-- begin heading_1 db1c8412-7609-4e2e-9579-22529c1de588--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-db1c8412-7609-4e2e-9579-22529c1de588&quot;&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/h2&gt;
&lt;!-- end heading_1 db1c8412-7609-4e2e-9579-22529c1de588--&gt;
&lt;!-- begin paragraph 4a0db4b6-702e-4c9d-a0e8-3834b80117f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a0db4b6-702e-4c9d-a0e8-3834b80117f3&quot;&gt;Pour conclure, Apache Pulsar est un système de messagerie qui se veut multi-fonction et multi-entité. L’architecture de Pulsar permet d’avoir un système de type publish-subscribe qui peut également prendre la fonction d’un système de type queue avec les bons paramètres. La différenciation de niveau pour les topics (tenant, namespace, topic) permet une séparation d’entité.&lt;/p&gt;
&lt;!-- end paragraph 4a0db4b6-702e-4c9d-a0e8-3834b80117f3--&gt;
&lt;!-- begin paragraph ee7eadf5-a82b-4610-8c68-40ed01a556e0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ee7eadf5-a82b-4610-8c68-40ed01a556e0&quot;&gt;Pour continuer sur le thème des systèmes de type publish-subscribe, vous pouvez consulter cet &lt;a href=&quot;https://blog.univalence.io/kafka-synthese/&quot;&gt;article&lt;/a&gt; sur notre blog décrivant Apache Kafka, un système similaire à Apache Pulsar avec néanmoins plusieurs différences au niveau de l’architecture et des concepts.&lt;/p&gt;
&lt;!-- end paragraph ee7eadf5-a82b-4610-8c68-40ed01a556e0--&gt;
&lt;!-- begin paragraph 6131ed5f-eee3-4cfd-aa90-25c2bad85c01--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6131ed5f-eee3-4cfd-aa90-25c2bad85c01&quot;&gt;Si vous voulez en savoir plus sur l’architecture d’Apache Pulsar et notamment à propos de la persistance des données avec Apache BookKeeper, je vous invite à consulter l&amp;#39;article de Jack Vanlightly : &lt;a href=&quot;https://jack-vanlightly.com/blog/2018/10/2/understanding-how-apache-pulsar-works&quot;&gt;&amp;quot;Understanding how ApachePulsar works&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6131ed5f-eee3-4cfd-aa90-25c2bad85c01--&gt;
&lt;!-- begin paragraph 51ab552d-b235-4bc9-9f18-dfd78f09fc2e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-51ab552d-b235-4bc9-9f18-dfd78f09fc2e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 51ab552d-b235-4bc9-9f18-dfd78f09fc2e--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:cddc5b65a23d475bbe03b91cb78722cb</id>
    <title>Typeclass derivation : faites éclore vos instances avec Magnolia</title>
    <updated>2019-04-02T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/typeclass-derivation-faites-eclore-vos-instances-avec-magnolia.html"/>
    <!--summary Magnolia est une libraire encore toute jeune et pourtant elle fait déjà ses preuves et sait se montrer indispensable lorsqu&#039;on parle de typeclass derivation.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="Typeclass"></category>    <category term="Magnolia"></category>    <category term="JSON"></category>    <content type="html">
&lt;!-- begin paragraph 3fa9f600-32be-4369-a4b8-3cad661337b7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3fa9f600-32be-4369-a4b8-3cad661337b7&quot;&gt;Vous faites du Scala ? Vous communiquez des données avec d&amp;#39;autres services (micro-services, big data, NoSQL...) ? Alors, vous aurez besoin de Magnolia.&lt;/p&gt;
&lt;!-- end paragraph 3fa9f600-32be-4369-a4b8-3cad661337b7--&gt;
&lt;!-- begin paragraph 7e729512-c073-4c6b-ad91-cfe88d16d2d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7e729512-c073-4c6b-ad91-cfe88d16d2d4&quot;&gt;Le format de donnée utilisé dans ce cadre est un point très sensible et correspond à des décisions qui vont être en partie contraintes par les performances, les frameworks utilisés... Vient alors la sérialisation et la désérialisation entre les données en mémoire et une représentation sous la forme d&amp;#39;une chaîne de caractères avec une syntaxe spécifique pour les formats lisibles (JSON, XML, CSV...) ou sous la forme d&amp;#39;une suite d&amp;#39;octets pour les formats binaires (Avro, Parquet, Protobuf...).&lt;/p&gt;
&lt;!-- end paragraph 7e729512-c073-4c6b-ad91-cfe88d16d2d4--&gt;
&lt;!-- begin heading_1 19605b2a-6823-4def-9b8a-ccc784f16c12--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-19605b2a-6823-4def-9b8a-ccc784f16c12&quot;&gt;Sérialisation&lt;/h2&gt;
&lt;!-- end heading_1 19605b2a-6823-4def-9b8a-ccc784f16c12--&gt;
&lt;!-- begin paragraph fe4d8ac7-47be-4154-bc04-a40f53eccc4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fe4d8ac7-47be-4154-bc04-a40f53eccc4d&quot;&gt;Lors de cette phase de sérialisation, il y a différentes approches mise en oeuvre. Supposons que nous avons les case classes ci-dessous, représentant des personnes. Nous cherchons ici à convertir ces case classes en JSON.&lt;/p&gt;
&lt;!-- end paragraph fe4d8ac7-47be-4154-bc04-a40f53eccc4d--&gt;
&lt;!-- begin code f005e11e-6fed-46b4-a6b7-16b45dcb6d54--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f005e11e-6fed-46b4-a6b7-16b45dcb6d54&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Address(id: String, city: String)
case class Person(id: String, name: String, age: Int, address: Address)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f005e11e-6fed-46b4-a6b7-16b45dcb6d54--&gt;
&lt;!-- begin paragraph ee1a6d4f-60c9-4d53-bf04-d1978b4f99a8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ee1a6d4f-60c9-4d53-bf04-d1978b4f99a8&quot;&gt;L&amp;#39;approche de sérialisation la plus simple consiste à faire la conversion à la main. On prend chaque champ de Person qu&amp;#39;on insère dans une chaîne de caractères : &lt;code&gt;s&amp;quot;&amp;quot;&amp;quot;{&amp;quot;id&amp;quot;: &amp;quot;${person.id}&amp;quot;, &amp;quot;name&amp;quot;:  &amp;quot;${person.name}&amp;quot;, ...}&amp;quot;&amp;quot;&amp;quot;&lt;/code&gt;. En terme de performance, c&amp;#39;est rapide. Mais, on a une potentielle répétition entre le nom du champ dans Person et son nom dans le JSON. De plus, avec une évolution du modèle, il faut se rappeler qu&amp;#39;il faut aussi modifier le template — ce qui n&amp;#39;est pas très fiable. Et puis, lorsque vous avez 200 champs par exemple dans votre entité, cette solution ne convient plus du tout. Vous avez enfin de quoi passer des heures de déboggage parce que vous avez oublié une &lt;code&gt;,&lt;/code&gt; ou un &lt;code&gt;&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph ee1a6d4f-60c9-4d53-bf04-d1978b4f99a8--&gt;
&lt;!-- begin paragraph 3ec76862-acdf-48fc-b624-9b52587aec48--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ec76862-acdf-48fc-b624-9b52587aec48&quot;&gt;Une seconde approche consiste à utiliser la réflexion pour explorer la structuration de la donnée et d&amp;#39;en déterminer le structuration et l&amp;#39;élaboration du JSON en sortie. Il n&amp;#39;y a plus, dans ce cas, de répétition et l&amp;#39;approche suit naturellement les évolutions du modèle de donnée. Par contre, cette approche est réalisée au runtime et se traduit pas une réduction des performances de votre application, puisque que la phase d&amp;#39;exploration de la structure de donnée est relancée à chaque sérialisation.&lt;/p&gt;
&lt;!-- end paragraph 3ec76862-acdf-48fc-b624-9b52587aec48--&gt;
&lt;!-- begin paragraph 39c0da6c-d5b4-4ccb-b44e-82e0c78e2e6f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-39c0da6c-d5b4-4ccb-b44e-82e0c78e2e6f&quot;&gt;Une troisième approche reprend l&amp;#39;approche précédente, seulement, l&amp;#39;exploration de la structure de donnée est faite une seule fois et va se traduire par la génération d&amp;#39;un code spécifique de conversion du modèle mémoire vers le format de sortie. Cette approche n&amp;#39;est coûteuse que lors de l&amp;#39;unique phase d&amp;#39;exploration et de génération de code. Donc en règle générale, dans un cadre statiquement typé, cette approche sera aussi performante voire plus performante que la version manuelle. Et si la phase d&amp;#39;exploration et de génération de code est faite à la compilation, typiquement avec un système de métaprogrammation avec des macros comme en Scala, cette phase ne vous coûtera plus rien au runtime.&lt;/p&gt;
&lt;!-- end paragraph 39c0da6c-d5b4-4ccb-b44e-82e0c78e2e6f--&gt;
&lt;!-- begin paragraph 7caf52e7-6c1b-4e95-9bc8-9299ec55543e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7caf52e7-6c1b-4e95-9bc8-9299ec55543e&quot;&gt;Magnolia propose une telle approche.&lt;/p&gt;
&lt;!-- end paragraph 7caf52e7-6c1b-4e95-9bc8-9299ec55543e--&gt;
&lt;!-- begin heading_1 596e8a71-cd08-4c8d-a84c-85a18f5459ba--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-596e8a71-cd08-4c8d-a84c-85a18f5459ba&quot;&gt;Magnolia&lt;/h2&gt;
&lt;!-- end heading_1 596e8a71-cd08-4c8d-a84c-85a18f5459ba--&gt;
&lt;!-- begin paragraph 54e03aa3-3fc0-4fff-a051-f6aafc071e16--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-54e03aa3-3fc0-4fff-a051-f6aafc071e16&quot;&gt;&lt;a href=&quot;https://propensive.com/opensource/magnolia/&quot;&gt;Magnolia&lt;/a&gt; est un framework léger (à peine 1000 lignes) développé initialement par Jon Pretty en Scala. Magnolia permet de réaliser de la dérivation de typeclasse...&lt;/p&gt;
&lt;!-- end paragraph 54e03aa3-3fc0-4fff-a051-f6aafc071e16--&gt;
&lt;!-- begin paragraph d4b9fe59-f927-4467-9113-3280df04a511--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d4b9fe59-f927-4467-9113-3280df04a511&quot;&gt;Wait! What?...&lt;/p&gt;
&lt;!-- end paragraph d4b9fe59-f927-4467-9113-3280df04a511--&gt;
&lt;!-- begin paragraph fb71e4e8-c2ef-40a9-9c42-3483c6ec7ede--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fb71e4e8-c2ef-40a9-9c42-3483c6ec7ede&quot;&gt;Nous avons vu la notion de typeclasse dans le &lt;a href=&quot;https://blog.univalence.io/pourquoi-les-typeclasses-cest-top/&quot;&gt;précédent article&lt;/a&gt;. Il s&amp;#39;agit d&amp;#39;une fonctionnalité permettant de catégoriser différents types de données et de caractériser un comportement commun (ie. des opérations communes / polymorphes). Les typeclasses ne se basent pas sur l&amp;#39;héritage, mais sur la délégation pour se brancher sur l&amp;#39;implémentation qui convient. Pour déclarer une typeclasse, il faut de déclarer un trait générique, puis instancier ce trait pour des types particuliers.&lt;/p&gt;
&lt;!-- end paragraph fb71e4e8-c2ef-40a9-9c42-3483c6ec7ede--&gt;
&lt;!-- begin paragraph fbecb85f-362e-4b3c-b032-27871a6fe4ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fbecb85f-362e-4b3c-b032-27871a6fe4ea&quot;&gt;Bien ! Mais si on crée des instances pour tous les types dont nous aurons besoin, on va vite se retrouver à ne faire que ça. On va aussi vite s&amp;#39;apercevoir qu&amp;#39;il y a une certaine répétition... C&amp;#39;est là qu&amp;#39;intervient la dérivation de typeclasse, qui consiste à générer automatiquement des instances pour des typeclasses données.&lt;/p&gt;
&lt;!-- end paragraph fbecb85f-362e-4b3c-b032-27871a6fe4ea--&gt;
&lt;!-- begin heading_1 8c4f41fc-d4f1-41e2-837b-d3e709b8f021--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-8c4f41fc-d4f1-41e2-837b-d3e709b8f021&quot;&gt;Cas d&amp;#39;utilisation : vers le JSON et au-delà&lt;/h2&gt;
&lt;!-- end heading_1 8c4f41fc-d4f1-41e2-837b-d3e709b8f021--&gt;
&lt;!-- begin paragraph 9021ac25-21e4-4572-90e1-a5667081b5ad--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9021ac25-21e4-4572-90e1-a5667081b5ad&quot;&gt;Partons de l&amp;#39;ADT ci-dessous (&lt;i&gt;Algebraic Data Type&lt;/i&gt; - type de données algébrique - comprenez un ensemble de trait, case class et case object pour former un type composite) pour représenter des documents &lt;a href=&quot;http://json.org/&quot;&gt;JSON&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 9021ac25-21e4-4572-90e1-a5667081b5ad--&gt;
&lt;!-- begin code 733df173-d560-46b2-90e7-c63185f6e91d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-733df173-d560-46b2-90e7-c63185f6e91d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;sealed trait JsonValue
case class   JsonNumber(value: Double)                        extends JsonValue
case class   JsonString(value: String)                        extends JsonValue
case class   JsonBoolean(value: Boolean)                      extends JsonValue
case object  JsonNull                                         extends JsonValue
case class   JsonArray(value: List[JsonValue])                extends JsonValue
case class   JsonObject(value: List[(JsonString, JsonValue)]) extends JsonValue&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 733df173-d560-46b2-90e7-c63185f6e91d--&gt;
&lt;!-- begin code 4b5bcbcd-11ef-44bb-8869-37c02ff5cd19--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4b5bcbcd-11ef-44bb-8869-37c02ff5cd19&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;sealed trait JsonValue
case class   JsonNumber(value: Double)                        extends JsonValue
case class   JsonString(value: String)                        extends JsonValue
case class   JsonBoolean(value: Boolean)                      extends JsonValue
case object  JsonNull                                         extends JsonValue
case class   JsonArray(value: List[JsonValue])                extends JsonValue
case class   JsonObject(value: List[JsonField])               extends JsonValue
case class   JsonField(name:String,value:JsonValue)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4b5bcbcd-11ef-44bb-8869-37c02ff5cd19--&gt;
&lt;!-- begin paragraph 8e9fac43-e080-44d5-8aaa-d8e8d4cb5e40--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e9fac43-e080-44d5-8aaa-d8e8d4cb5e40&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8e9fac43-e080-44d5-8aaa-d8e8d4cb5e40--&gt;
&lt;!-- begin paragraph b9d5cca4-7c2d-428f-9387-093734a24772--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b9d5cca4-7c2d-428f-9387-093734a24772&quot;&gt;Ce qui donne le schéma ci-dessous, où un JsonValue est soit un JsonNumber, un JsonString, un JsonNull, un JsonArray ou un JsonObject.&lt;/p&gt;
&lt;!-- end paragraph b9d5cca4-7c2d-428f-9387-093734a24772--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/cddc5b65-a23d-475b-be03-b91cb78722cb/nomnoml(2).png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 0af058ea-dfd5-4c85-9151-27da8aec5e45--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0af058ea-dfd5-4c85-9151-27da8aec5e45&quot;&gt;Et pour des soucis de lisibilité, voici une fonction permettant de convertir des JsonValue en chaîne de caractères JSON.&lt;/p&gt;
&lt;!-- end paragraph 0af058ea-dfd5-4c85-9151-27da8aec5e45--&gt;
&lt;!-- begin code 634da0b6-f6d2-4439-8dbf-ff4c81401d4a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-634da0b6-f6d2-4439-8dbf-ff4c81401d4a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def mkString(jsonValue: JsonValue): String =
  jsonValue match {
    case JsonNull        =&amp;gt; &amp;quot;null&amp;quot;
    case JsonString(v)   =&amp;gt; &amp;quot;\&amp;quot;&amp;quot; + v + &amp;quot;\&amp;quot;&amp;quot;
    case JsonBoolean(v)  =&amp;gt; s&amp;quot;$v&amp;quot;
    case JsonNumber(v)   =&amp;gt; s&amp;quot;$v&amp;quot;
    case JsonArray(vs)   =&amp;gt; vs.map(mkString).mkString(&amp;quot;[&amp;quot;, &amp;quot;, &amp;quot;, &amp;quot;]&amp;quot;)
    case JsonObject(kvs) =&amp;gt;
      kvs
        .map { case (k, v) =&amp;gt; s&amp;quot;${mkString(k)}: ${mkString(v)}&amp;quot; }
        .mkString(&amp;quot;{&amp;quot;, &amp;quot;, &amp;quot;, &amp;quot;}&amp;quot;)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 634da0b6-f6d2-4439-8dbf-ff4c81401d4a--&gt;
&lt;!-- begin paragraph c432b1d5-5e67-48cf-9d3a-934275fa3f45--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c432b1d5-5e67-48cf-9d3a-934275fa3f45&quot;&gt;Ce qui donne par exemple :&lt;/p&gt;
&lt;!-- end paragraph c432b1d5-5e67-48cf-9d3a-934275fa3f45--&gt;
&lt;!-- begin code 21551c3c-cbf1-4611-8a8b-98d79b75280b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-21551c3c-cbf1-4611-8a8b-98d79b75280b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&amp;gt; mkString(JsonArray(JsonNumber(42), JsonString(&amp;quot;all work and no play makes jack a dull boy&amp;quot;)))
res0: String = [42.0, all work and no play makes jack a dull boy]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 21551c3c-cbf1-4611-8a8b-98d79b75280b--&gt;
&lt;!-- begin paragraph 1a2a3236-8e4b-400a-a857-14cada83bde1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1a2a3236-8e4b-400a-a857-14cada83bde1&quot;&gt;Notre objectif est de convertir n&amp;#39;importe quel type A en JsonValue. Nous créons pour cela une typeclasse ToJson.&lt;/p&gt;
&lt;!-- end paragraph 1a2a3236-8e4b-400a-a857-14cada83bde1--&gt;
&lt;!-- begin code 068c138c-6cd2-4788-852d-1e7a100dca02--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-068c138c-6cd2-4788-852d-1e7a100dca02&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait ToJson[A] {
  def toJson(a: A): JsonValue
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 068c138c-6cd2-4788-852d-1e7a100dca02--&gt;
&lt;!-- begin paragraph faf7da53-6959-4bf0-9b67-519d40cc792e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-faf7da53-6959-4bf0-9b67-519d40cc792e&quot;&gt;Afin de facilter l&amp;#39;utilisation de ToJson, nous allons associer la méthode d&amp;#39;extension toJson à tous les types A de catégorie ToJson.&lt;/p&gt;
&lt;!-- end paragraph faf7da53-6959-4bf0-9b67-519d40cc792e--&gt;
&lt;!-- begin code cfc4742b-8c64-46a4-a371-527f5c84fcb9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-cfc4742b-8c64-46a4-a371-527f5c84fcb9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit class ToJsonOps[A: ToJson](a: A) {
  def toJson: JsonValue = implicitly[ToJson[A]].toJson(a)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code cfc4742b-8c64-46a4-a371-527f5c84fcb9--&gt;
&lt;!-- begin paragraph 3d9a049b-5e5c-45eb-9cd3-3d575c36bd78--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d9a049b-5e5c-45eb-9cd3-3d575c36bd78&quot;&gt;Ici, &lt;code&gt;implicitly&lt;/code&gt; permet de trouver une instance implicite dont le type correspond au type passé en paramètre.&lt;/p&gt;
&lt;!-- end paragraph 3d9a049b-5e5c-45eb-9cd3-3d575c36bd78--&gt;
&lt;!-- begin paragraph 26ab19ab-e9f9-480e-99e0-d1c4c5072bdc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26ab19ab-e9f9-480e-99e0-d1c4c5072bdc&quot;&gt;Nous créons à présent des instances de cette typeclasse pour des types de base.&lt;/p&gt;
&lt;!-- end paragraph 26ab19ab-e9f9-480e-99e0-d1c4c5072bdc--&gt;
&lt;!-- begin code a09579cc-dbb0-40dd-9eb0-c7dce9771ab1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a09579cc-dbb0-40dd-9eb0-c7dce9771ab1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit val intToJson:             ToJson[Int]     = a =&amp;gt; JsonNumber(a.toDouble)
implicit val doubleToJson:          ToJson[Double]  = a =&amp;gt; JsonNumber(a)
implicit val stringToJson:          ToJson[String]  = a =&amp;gt; JsonString(a)
implicit val booleanToJson:         ToJson[Boolean] = a =&amp;gt; JsonBooolean(a)
implicit def ListToJson[A: ToJson]: ToJson[List[A]] =
  a =&amp;gt; JsonArray(a.map(_.toJson))
implicit def mapToJson[A: ToJson]: ToJson[List[(String, A)]] =
  a =&amp;gt; JsonObject(a.map { case (k, v) =&amp;gt; (JsonString(k), v.toJson) })&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a09579cc-dbb0-40dd-9eb0-c7dce9771ab1--&gt;
&lt;!-- begin paragraph ddbc71cb-a7c9-445a-a8e0-dcec69b8b02a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ddbc71cb-a7c9-445a-a8e0-dcec69b8b02a&quot;&gt;Ce qui donne :&lt;/p&gt;
&lt;!-- end paragraph ddbc71cb-a7c9-445a-a8e0-dcec69b8b02a--&gt;
&lt;!-- begin code 71e40932-432e-4e14-a440-2fb0003876b1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-71e40932-432e-4e14-a440-2fb0003876b1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&amp;gt; 42.toJson
res0: JsonValue = JsonNumber(42.0)

&amp;gt; &amp;quot;all work and no play makes jack a dull boy&amp;quot;.toJson
res1: JsonValue = JsonString(all work and no play makes jack a dull boy)

&amp;gt; mkString(List(&amp;quot;a&amp;quot; -&amp;gt; 1, &amp;quot;b&amp;quot; -&amp;gt; 3).toJson)
res2: String = {&amp;quot;a&amp;quot;: 1.0, &amp;quot;b&amp;quot;: 3.0}

&amp;gt; implicitly[ToJson[Int]].toJson(42)
res3: JsonValue = JsonNumber(42.0)

&amp;gt; 42L.toJson
&amp;lt;console&amp;gt;:19: error: value toJson is not a member of Long

&amp;gt; implicitly[ToJson[Long]].toJson(42L)
&amp;lt;console&amp;gt;:19: error: could not find implicit value for parameter e: ToJson[Long]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 71e40932-432e-4e14-a440-2fb0003876b1--&gt;
&lt;!-- begin paragraph 9b56d4b2-b2e8-4629-aba0-d5913011a3fc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b56d4b2-b2e8-4629-aba0-d5913011a3fc&quot;&gt;(Les deux derniers cas génèrent une erreur puisque nous n&amp;#39;avons pas déclarer d&amp;#39;instance implicite de type &lt;code&gt;ToJson[Long]&lt;/code&gt; auparavant.)&lt;/p&gt;
&lt;!-- end paragraph 9b56d4b2-b2e8-4629-aba0-d5913011a3fc--&gt;
&lt;!-- begin paragraph 805f586c-3fcb-4379-bc34-2359f66c41d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-805f586c-3fcb-4379-bc34-2359f66c41d8&quot;&gt;Nous allons à présent dériver automatiquement ToJson pour toutes les case classes que nous pouvons créer. C&amp;#39;est là que nous allons utiliser Magnolia.&lt;/p&gt;
&lt;!-- end paragraph 805f586c-3fcb-4379-bc34-2359f66c41d8--&gt;
&lt;!-- begin paragraph 5ce84b10-ce2d-435b-ad80-7275ee71cbf0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ce84b10-ce2d-435b-ad80-7275ee71cbf0&quot;&gt;Magnolia se compose d&amp;#39;une macro et d&amp;#39;un ensemble d&amp;#39;interfaces pour réifier (ie. convertir en objet) les case classes et les sealed traits. Pour utiliser Magnolia, il faut bien évidemment importer le package correspondant et activer les macros (oui, les macros sont en Scala 2 et donc vouées à disparaître (d&amp;#39;où le &lt;code&gt;experimental&lt;/code&gt;) ; nous verrons comment est-ce ça se passe pour les futures versions). Il faut ensuite déclarer notre typeclasse de la manière suivante :&lt;/p&gt;
&lt;!-- end paragraph 5ce84b10-ce2d-435b-ad80-7275ee71cbf0--&gt;
&lt;!-- begin code d1cfc757-1a69-4c59-8e81-0db0cf0f91dc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d1cfc757-1a69-4c59-8e81-0db0cf0f91dc&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import magnolia._
import scala.language.experimental.macros

type Typeclass[A] = ToJson[A]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d1cfc757-1a69-4c59-8e81-0db0cf0f91dc--&gt;
&lt;!-- begin paragraph 616f814f-7f00-4ad2-9432-cb8c442d450c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-616f814f-7f00-4ad2-9432-cb8c442d450c&quot;&gt;Nous allons maintenant déclarer une fonction &lt;code&gt;combine&lt;/code&gt; qui va permettre de donner un comportement lié à notre typeclasse sur n&amp;#39;importe quel case classe. Ici nous allons chercher à convertir des case classes en objet JSON.&lt;/p&gt;
&lt;!-- end paragraph 616f814f-7f00-4ad2-9432-cb8c442d450c--&gt;
&lt;!-- begin code 952e7254-8203-4af0-88a4-4ba1e06bd771--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-952e7254-8203-4af0-88a4-4ba1e06bd771&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def combine[A](cc: CaseClass[Typeclass, A]): Typeclass[A] =
  a =&amp;gt; {
    val paramMap: List[(JsonString, JsonValue)] =
      cc.parameters
        .map(p =&amp;gt; JsonString(p.label) -&amp;gt; p.typeclass.toJson(p.dereference(a)))
        .toList

    JsonObject(paramMap)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 952e7254-8203-4af0-88a4-4ba1e06bd771--&gt;
&lt;!-- begin paragraph 83b9211f-91ed-44d7-8215-7e6b8c0f403f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-83b9211f-91ed-44d7-8215-7e6b8c0f403f&quot;&gt;À chaque fois que vous voyez Typeclass dans le code ci-dessus, il s&amp;#39;agit bien de ToJson derrière. Il est d&amp;#39;ailleurs possible de changer toutes les occurences de Typeclass par ToJson.&lt;/p&gt;
&lt;!-- end paragraph 83b9211f-91ed-44d7-8215-7e6b8c0f403f--&gt;
&lt;!-- begin paragraph 9509c7a7-f7ee-4510-a642-e8b73934c371--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9509c7a7-f7ee-4510-a642-e8b73934c371&quot;&gt;Dans notre case classe &lt;code&gt;cc&lt;/code&gt;, passée en entrée de combine, nous allons plus particulièrement nous intéresser aux champs (ou paramètres). Pour chaque paramètre de la case classe, nous récupérons le nom du paramètre (&lt;code&gt;p.label&lt;/code&gt;) converti en JsonString que nous associons à la valeur de ce paramètre, elle-même convertie en JSON. &lt;code&gt;p.dereference(a)&lt;/code&gt; permet de récupérer la valeur associée au paramètre &lt;code&gt;p&lt;/code&gt; dans l&amp;#39;instance &lt;code&gt;a&lt;/code&gt;. &lt;code&gt;p.typeclass&lt;/code&gt; permet de récupérer l&amp;#39;instance de la typeclasse associé au paramètre &lt;code&gt;p&lt;/code&gt;. De fait, &lt;code&gt;p.typeclass&lt;/code&gt; va retourner soit l&amp;#39;une des valeurs implicites déclarées plus haut si &lt;code&gt;p&lt;/code&gt; correspond à un type de base, soit réaliser un appel récursif sur &lt;code&gt;combine&lt;/code&gt; si &lt;code&gt;p&lt;/code&gt; correspond à une case classe.&lt;/p&gt;
&lt;!-- end paragraph 9509c7a7-f7ee-4510-a642-e8b73934c371--&gt;
&lt;!-- begin paragraph 30f45745-73cb-4961-bb00-a4d868c8a3af--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-30f45745-73cb-4961-bb00-a4d868c8a3af&quot;&gt;Pour rendre &lt;code&gt;combine&lt;/code&gt; accessible, nous utilisons la macro &lt;code&gt;gen&lt;/code&gt; de Magnolia de la manière suivante :&lt;/p&gt;
&lt;!-- end paragraph 30f45745-73cb-4961-bb00-a4d868c8a3af--&gt;
&lt;!-- begin code 58d50e36-0614-4c30-9171-398d564f7d53--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-58d50e36-0614-4c30-9171-398d564f7d53&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit def typeToJson[A]: Typeclass[A] = macro Magnolia.gen[A]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 58d50e36-0614-4c30-9171-398d564f7d53--&gt;
&lt;!-- begin paragraph ff26ccb9-b3a6-404e-93f9-bc250bf362fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff26ccb9-b3a6-404e-93f9-bc250bf362fb&quot;&gt;&lt;code&gt;typeToJson&lt;/code&gt; est déclarée en tant que fonction (&lt;code&gt;def&lt;/code&gt;). Ce qui permet d&amp;#39;utiliser la notation générique, où &lt;code&gt;A&lt;/code&gt; représente n&amp;#39;importe quel type. Le fait de mettre &lt;code&gt;implicit&lt;/code&gt; permet d&amp;#39;associer implicitement ToJson à toutes les case classes que nous rencontrons.&lt;/p&gt;
&lt;!-- end paragraph ff26ccb9-b3a6-404e-93f9-bc250bf362fb--&gt;
&lt;!-- begin paragraph eeaf3c16-7850-40f1-ba25-3f966baba890--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eeaf3c16-7850-40f1-ba25-3f966baba890&quot;&gt;Avec les exemples de case classes Person et Address vu au début de l&amp;#39;article, nous obtenons :&lt;/p&gt;
&lt;!-- end paragraph eeaf3c16-7850-40f1-ba25-3f966baba890--&gt;
&lt;!-- begin code fa566606-e63b-4f5e-ac00-fbf1a6cbe785--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fa566606-e63b-4f5e-ac00-fbf1a6cbe785&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&amp;gt; val toto =
  Person(
    id = &amp;quot;p1&amp;quot;,
    name = &amp;quot;Toto&amp;quot;,
    age = 32,
    address = Address(&amp;quot;c1&amp;quot;, &amp;quot;Paris&amp;quot;))
toto: Person = Person(p1, Toto, 32, Address(c1, Paris))

&amp;gt; mkString(toto.toJson)
res0: String = {&amp;quot;id&amp;quot;: &amp;quot;p1&amp;quot;, &amp;quot;name&amp;quot;: &amp;quot;Toto&amp;quot;, &amp;quot;age&amp;quot;: 32.0, &amp;quot;address&amp;quot;: {&amp;quot;id&amp;quot;: &amp;quot;c1&amp;quot;, &amp;quot;city&amp;quot;: &amp;quot;Paris&amp;quot;}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fa566606-e63b-4f5e-ac00-fbf1a6cbe785--&gt;
&lt;!-- begin heading_1 539443cd-27da-47e3-abea-ff2704c5e612--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-539443cd-27da-47e3-abea-ff2704c5e612&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 539443cd-27da-47e3-abea-ff2704c5e612--&gt;
&lt;!-- begin paragraph 7d8aa02d-14c1-4b8b-9aae-cea6dc1da4bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7d8aa02d-14c1-4b8b-9aae-cea6dc1da4bc&quot;&gt;Il existe des alternatives à Magnolia pour réaliser de la dérivation de typeclasse. Il est possible de passer tout d&amp;#39;abord par &lt;a href=&quot;https://github.com/milessabin/shapeless&quot;&gt;Shapeless&lt;/a&gt;. Shapeless est un framework léger dédié à la programmation générique. Comme il est plus généraliste la dérivation de typeclasse peut se montrer plus verbeuse qu&amp;#39;avec Magnolia. Il est possible aussi de passer directement par les macros. Néanmoins, comme ce système est de plus bas niveau, il sera au mieux plus difficile  de faire de la dérivation.&lt;/p&gt;
&lt;!-- end paragraph 7d8aa02d-14c1-4b8b-9aae-cea6dc1da4bc--&gt;
&lt;!-- begin paragraph bbff0878-a21a-481e-82b9-4414795393b6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bbff0878-a21a-481e-82b9-4414795393b6&quot;&gt;Avec la probable disparition des macros whitebox dans Scala 3 (type de macro intervenant avant la résolution des types), macros utilisées par Magnolia, la nouvelle version de Scala devrait adopter un outillage permettant de réaliser de la dérivation de typeclasse. Cet outillage tournera autour de nouveaux éléments de syntaxe et une partie spécifique dans la bibliothèque du SDK.&lt;/p&gt;
&lt;!-- end paragraph bbff0878-a21a-481e-82b9-4414795393b6--&gt;
&lt;!-- begin paragraph 77528365-a3e0-4029-ac94-477de661dc2d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77528365-a3e0-4029-ac94-477de661dc2d&quot;&gt;Pour terminer, Magnolia est notamment utilisé dans Scio, développé par Spotify. Scio est une API Scala pour Apache Beam et Google Cloud Dataflow. Julien Tournay a présenté une &lt;a href=&quot;https://www.youtube.com/watch?v=UN9-8MDTMiQ&quot;&gt;session&lt;/a&gt; ce sujet à ScalaIO 2018.&lt;/p&gt;
&lt;!-- end paragraph 77528365-a3e0-4029-ac94-477de661dc2d--&gt;
&lt;!-- begin paragraph 26610b03-57a4-4294-adc3-e5622fd5005d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26610b03-57a4-4294-adc3-e5622fd5005d&quot;&gt;Projet contenant le code de l&amp;#39;article : &lt;a href=&quot;https://github.com/univalence/blog-code/tree/master/typeclass_derivation&quot;&gt;https://github.com/univalence/blog-code/tree/master/typeclass_derivation&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 26610b03-57a4-4294-adc3-e5622fd5005d--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:855109e92043470c90a8ef9c03ad33d8</id>
    <title>Pourquoi les typeclasses c&#039;est top ?</title>
    <updated>2019-03-26T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/pourquoi-les-typeclasses-c-est-top.html"/>
    <!--summary Les typeclasses c&#039;est vraiment top, c&#039;est beau, c&#039;est fort et c&#039;est polyvalent. Si vous ne savez pas ce que c&#039;est alors ne ratez pas cet article !-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Typeclass"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin paragraph 55d8024e-913f-4297-88ae-094f693d02d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-55d8024e-913f-4297-88ae-094f693d02d4&quot;&gt;Le polymorphisme, qui permet à une opération d&amp;#39;être appelée sur des types différents, est l&amp;#39;une des bases de la programmation orientée objet. Et pourtant elle ne lui est pas exclusivement réservée. C&amp;#39;est en effet un terme que nous retrouvons en programmation fonctionnelle à travers la notion de typeclasse.&lt;/p&gt;
&lt;!-- end paragraph 55d8024e-913f-4297-88ae-094f693d02d4--&gt;
&lt;!-- begin paragraph 9e20655e-4915-4204-a64a-5479960c56c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e20655e-4915-4204-a64a-5479960c56c1&quot;&gt;La notion de typeclasse n&amp;#39;a en effet rien à voir avec les classes et les interfaces de la programmation orientée objet. Mais elle présente certains point communs. Bon, déjà, il y a classe dedans... Mais ça c&amp;#39;était facile !&lt;/p&gt;
&lt;!-- end paragraph 9e20655e-4915-4204-a64a-5479960c56c1--&gt;
&lt;!-- begin paragraph f235e4ea-7b19-4053-8dc0-00ce81e81f59--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f235e4ea-7b19-4053-8dc0-00ce81e81f59&quot;&gt;En fait, les typeclasses permettent de &amp;quot;catégoriser&amp;quot; des types divers et de leur associer une interface ou une API commune. Sauf que dans le cas des typeclasses, les possibilités d&amp;#39;extension et de mise en application sont beaucoup plus étendues que dans le cadre des classes, jusqu&amp;#39;à permettre de mieux s&amp;#39;en sortir dans un cadre legacy aussi fermé soit-il. Ceci est facilité parce que les typeclasses se basent plus sur la délégation que sur l&amp;#39;héritage.&lt;/p&gt;
&lt;!-- end paragraph f235e4ea-7b19-4053-8dc0-00ce81e81f59--&gt;
&lt;!-- begin heading_1 eb95f721-9f2a-467d-9ce0-c393dfeb5e8c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-eb95f721-9f2a-467d-9ce0-c393dfeb5e8c&quot;&gt;Du classique à l&amp;#39;héritage&lt;/h2&gt;
&lt;!-- end heading_1 eb95f721-9f2a-467d-9ce0-c393dfeb5e8c--&gt;
&lt;!-- begin paragraph b783a98d-5a41-45f2-9880-4397e724dadf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b783a98d-5a41-45f2-9880-4397e724dadf&quot;&gt;Prenons un exemple : j&amp;#39;ai des chiens et des dinosaures représentés par les types Dog et Dinosaur. Je devrais pouvoir utiliser la même opération &lt;code&gt;walk&lt;/code&gt; pour voir les chiens et les dinosaures marcher, pour laquelle seul va changer le comportement associé.&lt;/p&gt;
&lt;!-- end paragraph b783a98d-5a41-45f2-9880-4397e724dadf--&gt;
&lt;!-- begin paragraph df6f7f5f-dcb7-4262-9bc5-25ac3831f50f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-df6f7f5f-dcb7-4262-9bc5-25ac3831f50f&quot;&gt;En programmation plus ou moins classique, on partirait sur une approche utilisant la réflexion pour connaître le type de l&amp;#39;instance et une structure conditionnelle pour y associer un traitement spécifique.&lt;/p&gt;
&lt;!-- end paragraph df6f7f5f-dcb7-4262-9bc5-25ac3831f50f--&gt;
&lt;!-- begin code 6c507d92-e5f6-490e-b5de-6dd0f180b722--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6c507d92-e5f6-490e-b5de-6dd0f180b722&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;public String walk(Object animal) {
  if (animal.isInstanceOf[Dog])
    return &amp;quot;Tap tap tap tap tap tap tap tap&amp;quot;;
  else if (animal.isInstanceOf[Dinosaur])
    return &amp;quot;Boom boom acka-lacka lacka boom&amp;quot;;
  else
    throw new Exception(&amp;quot;your animal doesn&amp;#39;t walk. HAhaHAha!&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6c507d92-e5f6-490e-b5de-6dd0f180b722--&gt;
&lt;!-- begin paragraph 54761961-5c24-4a20-abd7-b5c8a0e2d930--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-54761961-5c24-4a20-abd7-b5c8a0e2d930&quot;&gt;(Depuis les &lt;a href=&quot;https://www.youtube.com/watch?v=vgiDcJi534Y&quot;&gt;années 80&lt;/a&gt;, on sait que les dinosaures font ce bruit lorsqu&amp;#39;ils se déplacent !)&lt;/p&gt;
&lt;!-- end paragraph 54761961-5c24-4a20-abd7-b5c8a0e2d930--&gt;
&lt;!-- begin paragraph 06f2931f-1302-4b44-9bdc-2cab49e49d72--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06f2931f-1302-4b44-9bdc-2cab49e49d72&quot;&gt;Le problème de cette approche est que le développeur doit penser au cas d&amp;#39;erreur. En cas d&amp;#39;oubli, seule l&amp;#39;exécution du programme avec la bonne donnée en entrée permet de rappeler que la gestion du cas exceptionnel est manquant. C&amp;#39;est une situation qui peut apparaître OÙ ELLE VEUT ET C&amp;#39;EST SOUVENT DANS LA PROD...&lt;/p&gt;
&lt;!-- end paragraph 06f2931f-1302-4b44-9bdc-2cab49e49d72--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/855109e9-2043-470c-90a8-ef9c03ad33d8/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph f6ec5034-4883-4a4a-a04a-51a63e0ffc73--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f6ec5034-4883-4a4a-a04a-51a63e0ffc73&quot;&gt;Il y a d&amp;#39;autres aspects qui sont relativement problématiques ici. Par exemple, ajouter un animal comme le canard, mais oublier de l&amp;#39;ajouter dans &lt;code&gt;walk&lt;/code&gt;. Ce qui va se traduire par une exception. De plus, si ce code vient du bibliothèque tierce scellée (ie. les modifications dans cette bibliothèque sont... c&amp;#39;est très compliqué), ajouter le comportement pour le canard... Et bien... on peut pas 😕. Par contre, si je veux ajouter une opération &lt;code&gt;dance&lt;/code&gt; pour mes animaux, ce ne sera pas un soucis.&lt;/p&gt;
&lt;!-- end paragraph f6ec5034-4883-4a4a-a04a-51a63e0ffc73--&gt;
&lt;!-- begin paragraph 79b55c26-8a41-46a5-804d-bbedc93168be--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79b55c26-8a41-46a5-804d-bbedc93168be&quot;&gt;La programmation orientée objet apporte une solution à ce problème en ne permettant pas au développeur de faire n&amp;#39;importe quoi, sauf lorsqu&amp;#39;il l&amp;#39;a clairement exprimé 🤔&lt;/p&gt;
&lt;!-- end paragraph 79b55c26-8a41-46a5-804d-bbedc93168be--&gt;
&lt;!-- begin code 90689d41-f2ae-44da-a38b-8e3a75c7c03d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-90689d41-f2ae-44da-a38b-8e3a75c7c03d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Animal { /* ... */ }

trait Walking {
  def walk: String
}

case class Dog(name: String) extends Animal with Walking {
  /* ... */
  def walk: String = &amp;quot;Tap tap tap tap tap tap tap tap&amp;quot;
}
case class Dinosaur(name: String) extends Animal with Walking {
  /* ... */
  def walk: String = &amp;quot;Boom boom acka-lacka lacka boom&amp;quot;
}

def walk(walking: Walking): String = walking.walk&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 90689d41-f2ae-44da-a38b-8e3a75c7c03d--&gt;
&lt;!-- begin paragraph e72c10c9-68f0-4968-b123-e706b2553f46--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e72c10c9-68f0-4968-b123-e706b2553f46&quot;&gt;Si je veux ajouter le canard, je dois alors étendre Animal et Walking. Ce qui m&amp;#39;oblige à définir la méthode &lt;code&gt;walk&lt;/code&gt; pour cet animal. Après quoi, je peux appeler la fonction &lt;code&gt;walk(Walking)&lt;/code&gt; pour les canards.&lt;/p&gt;
&lt;!-- end paragraph e72c10c9-68f0-4968-b123-e706b2553f46--&gt;
&lt;!-- begin paragraph 35010481-73f4-4eaa-82a6-a3f28f7982e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-35010481-73f4-4eaa-82a6-a3f28f7982e7&quot;&gt;L&amp;#39;approche orientée objet permet clairement de s&amp;#39;éviter des problèmes à la source et d&amp;#39;avoir moins de cas d&amp;#39;erreur à gérer. On gagne en flexibilité sur la mise en place de nouveaux comportements associés à une opération. Mais en contrepartie, on perd en flexibilité sur l&amp;#39;ajout de nouvelles opérations : si le code ci-dessus fait partie d&amp;#39;une bibliothèque scellée, je ne pourrais pas ajouter un comportement &lt;code&gt;dance&lt;/code&gt;, sauf à le faire en partant sur la solution précédente ou en utilisant d&amp;#39;autres approches basées sur la réflexion. Autre point : Dog et Dinosaur est toujours associé au mixin Walking quelque soit le contexte. On peut imaginer des contextes dans lequel &amp;quot;Dinosaur étend Walking&amp;quot; n&amp;#39;a aucune utilité, par exemple lorsque le dinosaure dort (somnambule), ou est perturbant, par exemple lorsqu&amp;#39;il est décédé (walking dead).&lt;/p&gt;
&lt;!-- end paragraph 35010481-73f4-4eaa-82a6-a3f28f7982e7--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;https://zamonthly.files.wordpress.com/2018/03/image-2018-03-21.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;C&amp;#39;est vraiment perturbant !&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 96e2a1df-57c1-4419-8736-80fb6390e5ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-96e2a1df-57c1-4419-8736-80fb6390e5ac&quot;&gt;Les typeclasses offrent une réponse à ces cas problématiques.&lt;/p&gt;
&lt;!-- end paragraph 96e2a1df-57c1-4419-8736-80fb6390e5ac--&gt;
&lt;!-- begin heading_1 ae9f466a-f2c5-4767-9cfa-bb6cf5ffee2a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ae9f466a-f2c5-4767-9cfa-bb6cf5ffee2a&quot;&gt;Typeclasse&lt;/h2&gt;
&lt;!-- end heading_1 ae9f466a-f2c5-4767-9cfa-bb6cf5ffee2a--&gt;
&lt;!-- begin paragraph 1fbf8f16-abc4-49b9-9192-d6a8dbac4154--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1fbf8f16-abc4-49b9-9192-d6a8dbac4154&quot;&gt;Les typeclasses permettent de catégoriser des types existant dans des contextes bien précis et d&amp;#39;associer à ces types des opérations communes.&lt;/p&gt;
&lt;!-- end paragraph 1fbf8f16-abc4-49b9-9192-d6a8dbac4154--&gt;
&lt;!-- begin paragraph c179fd10-b0c5-4d5b-91f0-d804825e3530--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c179fd10-b0c5-4d5b-91f0-d804825e3530&quot;&gt;La mise en place de typeclasses au sein de Scala passe par un idiome basé sur la notion de déclaration implicite. Comme le principe n&amp;#39;est pas très connu, nous allons procéder par étape.&lt;/p&gt;
&lt;!-- end paragraph c179fd10-b0c5-4d5b-91f0-d804825e3530--&gt;
&lt;!-- begin paragraph 6fb8b8d3-4186-47d0-81e0-2c9e7ba65ad8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6fb8b8d3-4186-47d0-81e0-2c9e7ba65ad8&quot;&gt;Nous déclarons d&amp;#39;abord quelques classes sans les lier directement à une classe ou une interface quelconque.&lt;/p&gt;
&lt;!-- end paragraph 6fb8b8d3-4186-47d0-81e0-2c9e7ba65ad8--&gt;
&lt;!-- begin code f7b13aee-5b20-42ab-92c6-dbb4bb0bf8b6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f7b13aee-5b20-42ab-92c6-dbb4bb0bf8b6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Dog(name: String)
case class Dinosaur(name: String)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f7b13aee-5b20-42ab-92c6-dbb4bb0bf8b6--&gt;
&lt;!-- begin paragraph 9b73fe92-a1c7-4d37-9eea-0b4cd8bb8e18--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b73fe92-a1c7-4d37-9eea-0b4cd8bb8e18&quot;&gt;Nous déclarons ensuite avec un &lt;code&gt;trait&lt;/code&gt; un type paramétré qui va permettre de catégoriser n&amp;#39;importe quel type A, sur lequel nous pourrons appeler la méthode &lt;code&gt;walk&lt;/code&gt;. C&amp;#39;est notre &lt;b&gt;typeclasse&lt;/b&gt; !&lt;/p&gt;
&lt;!-- end paragraph 9b73fe92-a1c7-4d37-9eea-0b4cd8bb8e18--&gt;
&lt;!-- begin code c2d643d8-db40-42ab-be53-6c85cde9af19--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c2d643d8-db40-42ab-be53-6c85cde9af19&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Walking[A] {
  def walk(a: A): String
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c2d643d8-db40-42ab-be53-6c85cde9af19--&gt;
&lt;!-- begin paragraph ea3f3f6d-51d8-486b-8fd9-8c84b582300b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ea3f3f6d-51d8-486b-8fd9-8c84b582300b&quot;&gt;Définissons une fonction &lt;code&gt;walk&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph ea3f3f6d-51d8-486b-8fd9-8c84b582300b--&gt;
&lt;!-- begin code b105a304-bb41-4e3d-9fdc-36ff2cbce2cf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b105a304-bb41-4e3d-9fdc-36ff2cbce2cf&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def walk[A: Walking](a: A): String =
  implicitly[Walking[A]].walk(a)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b105a304-bb41-4e3d-9fdc-36ff2cbce2cf--&gt;
&lt;!-- begin paragraph a581941e-8c07-4a5e-af98-601355a81451--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a581941e-8c07-4a5e-af98-601355a81451&quot;&gt;Cette fonction se base en entrée sur n&amp;#39;importe quel type A. Néanmoins, dans la signature, nous indiquons que ce type est contraint par le fait qu&amp;#39;il doit être de catégorie Walking (&lt;code&gt;A: Walking&lt;/code&gt;). Dans le corps de la fonction, nous indiquons que nous recherchons une instance de Walking déclarée implicitement pour le type A donnée (&lt;code&gt;implicitly[Walking[A]]&lt;/code&gt;). Une fois récupérée, nous pourrons appeler la méthode &lt;code&gt;walk&lt;/code&gt; dessus.&lt;/p&gt;
&lt;!-- end paragraph a581941e-8c07-4a5e-af98-601355a81451--&gt;
&lt;!-- begin paragraph 82eaba48-b3ae-4e6c-be1d-a3aa74ebfba7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-82eaba48-b3ae-4e6c-be1d-a3aa74ebfba7&quot;&gt;Nous déclarons à présent des instances implicites de Walking pour Dog et Dinosaur avec un comportement spécifique. Ce sont les &lt;b&gt;instances&lt;/b&gt; de la typeclasse.&lt;/p&gt;
&lt;!-- end paragraph 82eaba48-b3ae-4e6c-be1d-a3aa74ebfba7--&gt;
&lt;!-- begin code 4101d587-978b-4b68-8ed8-ff13edf1aedc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4101d587-978b-4b68-8ed8-ff13edf1aedc&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;implicit val dogWalking =
  new Walking[Dog] {
    def walk(d: Dog): String = &amp;quot;Tap tap tap tap tap tap tap tap&amp;quot;
  }
 
implicit val dinosaurWalking =
  new Walking[Dinosaur] {
    def walk(d: Dinosaur): String = &amp;quot;Boom boom acka-lacka lacka boom&amp;quot;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4101d587-978b-4b68-8ed8-ff13edf1aedc--&gt;
&lt;!-- begin paragraph 748973e3-3d2c-433d-a82d-779f473cddf4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-748973e3-3d2c-433d-a82d-779f473cddf4&quot;&gt;Et donc, à l&amp;#39;utilisation ça donne :&lt;/p&gt;
&lt;!-- end paragraph 748973e3-3d2c-433d-a82d-779f473cddf4--&gt;
&lt;!-- begin code c8783a00-6a74-4be9-a51b-ed24a4677c4e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c8783a00-6a74-4be9-a51b-ed24a4677c4e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;walk(Dog(&amp;quot;Rambo&amp;quot;))          // Tap tap tap tap tap tap tap tap
walk(Dinosaur(&amp;quot;P&amp;#39;tit Rex&amp;quot;)) // Boom boom acka-lacka lacka boom&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c8783a00-6a74-4be9-a51b-ed24a4677c4e--&gt;
&lt;!-- begin paragraph 4180ea0c-7be5-49f8-b463-abceb6db5c28--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4180ea0c-7be5-49f8-b463-abceb6db5c28&quot;&gt;Si la déclaration d&amp;#39;une instance implicite est manquante, nous avons alors une erreur de compilation.&lt;/p&gt;
&lt;!-- end paragraph 4180ea0c-7be5-49f8-b463-abceb6db5c28--&gt;
&lt;!-- begin code 0f9cc89d-c91d-41f7-9e3c-d41fd165b4f4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0f9cc89d-c91d-41f7-9e3c-d41fd165b4f4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Duck(name: String)

walk(Duck(&amp;quot;Donald&amp;quot;)) // KO 🙀 - compilation error
// could not find implicit value for evidence parameter of type Walking[Duck]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0f9cc89d-c91d-41f7-9e3c-d41fd165b4f4--&gt;
&lt;!-- begin paragraph 5f3a45c6-2b1a-4e37-a2b0-391c9dccc9cd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5f3a45c6-2b1a-4e37-a2b0-391c9dccc9cd&quot;&gt;Pour que le code ci-dessus fonctionne avec Duck, il faut alors déclarer une instance implicite de type Walking[Duck].&lt;/p&gt;
&lt;!-- end paragraph 5f3a45c6-2b1a-4e37-a2b0-391c9dccc9cd--&gt;
&lt;!-- begin paragraph b712f44f-91b0-4061-a91c-ee6a14a1cb9d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b712f44f-91b0-4061-a91c-ee6a14a1cb9d&quot;&gt;Quelques remarques ici :&lt;/p&gt;
&lt;!-- end paragraph b712f44f-91b0-4061-a91c-ee6a14a1cb9d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Dog et Dinosaur sont décorrélés de Walking. Les deux déclarations peuvent se faire dans des espaces de code différents.&lt;/li&gt;&lt;li&gt;Le lien entre Dog et Dinosaur d&amp;#39;un côté avec Walking est concrétisé par les instances implicites (&lt;code&gt;implicit val ...&lt;/code&gt;). La déclaration de ces instances peut se faire dans un autre espace de code.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph b0d42cc7-3d76-4c31-a7ed-d87bdebbb8d5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b0d42cc7-3d76-4c31-a7ed-d87bdebbb8d5&quot;&gt;Donc, même si Dog, Dinosaur, Walking et la fonction &lt;code&gt;walk&lt;/code&gt; sont dans des bibliothèques scellées, il est toujours possible :&lt;/p&gt;
&lt;!-- end paragraph b0d42cc7-3d76-4c31-a7ed-d87bdebbb8d5--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;de créer de nouveaux animaux avec le comportement Walking,&lt;/li&gt;&lt;li&gt;d&amp;#39;associer un comportement Dancing pour Dog et Dinosaur,&lt;/li&gt;&lt;li&gt;de ne pas importer le comportement Walking s&amp;#39;il n&amp;#39;est pas nécessaire.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 49f2d144-7baf-4de5-aedf-765387f173ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-49f2d144-7baf-4de5-aedf-765387f173ae&quot;&gt;Le tout est vérifié par le compilateur.&lt;/p&gt;
&lt;!-- end paragraph 49f2d144-7baf-4de5-aedf-765387f173ae--&gt;
&lt;!-- begin heading_1 bff07735-fe97-48b1-a6c6-03f95b7159de--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-bff07735-fe97-48b1-a6c6-03f95b7159de&quot;&gt;Pour terminer&lt;/h2&gt;
&lt;!-- end heading_1 bff07735-fe97-48b1-a6c6-03f95b7159de--&gt;
&lt;!-- begin paragraph 234b08d7-9ea9-4bab-98a5-567b83b70981--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-234b08d7-9ea9-4bab-98a5-567b83b70981&quot;&gt;Nous avons vu sans le nommer des exemples de typeclasses à la fin de &lt;a href=&quot;https://blog.univalence.io/covariance-contravariance-et-botanique/&quot;&gt;notre précédent article sur la covariance et la contravariance&lt;/a&gt;, avec les notions de Functor et CoFunctor.&lt;/p&gt;
&lt;!-- end paragraph 234b08d7-9ea9-4bab-98a5-567b83b70981--&gt;
&lt;!-- begin paragraph 36fcf287-fbeb-4acf-ae60-4b61a9a60129--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-36fcf287-fbeb-4acf-ae60-4b61a9a60129&quot;&gt;C&amp;#39;est en tout cas cette approche qui est employée par des bibliothèques comme Cats ou ScalaZ pour associer à des types un comportement de Functor et de Monad, même à des types déjà définit dans la bibliothèque standard de Scala.&lt;/p&gt;
&lt;!-- end paragraph 36fcf287-fbeb-4acf-ae60-4b61a9a60129--&gt;
&lt;!-- begin paragraph 3255ce2a-1564-4e55-9d08-51d38178864d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3255ce2a-1564-4e55-9d08-51d38178864d&quot;&gt;La notion de typeclasse provient du langage Haskell. Contrairement à Scala, les typeclasses ont une syntaxe spécifique dans Haskell. La notion a aussi été adaptée à Kotlin, comme on peut le voir avec la bibliothèque &lt;a href=&quot;https://arrow-kt.io/docs/typeclasses/intro/&quot;&gt;Arrow&lt;/a&gt;. Scala 3 devrait proposer une &lt;a href=&quot;https://dotty.epfl.ch/docs/reference/contextual/type-classes.html&quot;&gt;syntaxe spécifique&lt;/a&gt; pour les typeclasses, en particulier pour la déclaration des instances.&lt;/p&gt;
&lt;!-- end paragraph 3255ce2a-1564-4e55-9d08-51d38178864d--&gt;
&lt;!-- begin paragraph 2a764552-9766-4127-8357-47306dff8a84--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2a764552-9766-4127-8357-47306dff8a84&quot;&gt;Nous verrons dans un prochain article l&amp;#39;utilisation des typeclasses avec Magnolia, afin de faciliter la conversion de format de données, en incluant les case class.&lt;/p&gt;
&lt;!-- end paragraph 2a764552-9766-4127-8357-47306dff8a84--&gt;
&lt;!-- begin divider 58369a2c-3ff3-4b5d-9a5c-0de1daa76652--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-58369a2c-3ff3-4b5d-9a5c-0de1daa76652&quot;&gt;
&lt;!-- end divider 58369a2c-3ff3-4b5d-9a5c-0de1daa76652--&gt;
&lt;!-- begin paragraph 8559781f-ab58-43a8-9f7c-2ab3282f2c34--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8559781f-ab58-43a8-9f7c-2ab3282f2c34&quot;&gt;Référence&lt;/p&gt;
&lt;!-- end paragraph 8559781f-ab58-43a8-9f7c-2ab3282f2c34--&gt;
&lt;!-- begin paragraph 8859ec3d-82b7-4f33-a2b4-f7cce2f5dc0e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8859ec3d-82b7-4f33-a2b4-f7cce2f5dc0e&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 8859ec3d-82b7-4f33-a2b4-f7cce2f5dc0e--&gt;
&lt;!-- begin bookmark acf3c3e8-dd51-4810-970a-bd7fcd51964e--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-acf3c3e8-dd51-4810-970a-bd7fcd51964e&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://github.com/dotty-staging/dotty/blob/63b4ad7a28c92b32469b6ef997765336bc2274c2/docs/docs/reference/contextual/derivation.md&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/b78c67a6c94cd120be02ca79926080ec7bcf800b3af68ef53b74ca188cec3342/dotty-staging/dotty&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;dotty/docs/docs/reference/contextual/derivation.md at 63b4ad7a28c92b32469b6ef997765336bc2274c2 · dotty-staging/dotty&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Research platform for new language concepts and compiler technologies for Scala. - dotty/docs/docs/reference/contextual/derivation.md at 63b4ad7a28c92b32469b6ef997765336bc2274c2 · dotty-staging/dotty&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://github.com/dotty-staging/dotty/blob/63b4ad7a28c92b32469b6ef997765336bc2274c2/docs/docs/reference/contextual/derivation.md&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark acf3c3e8-dd51-4810-970a-bd7fcd51964e--&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/vgiDcJi534Y?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 7b82963b-a606-4f38-b93d-bd21766eee18--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7b82963b-a606-4f38-b93d-bd21766eee18&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7b82963b-a606-4f38-b93d-bd21766eee18--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:9c120529f0e547a98ff377c3e2eaaa25</id>
    <title>Covariance, contravariance et botanique</title>
    <updated>2019-03-20T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/covariance-contravariance-et-botanique.html"/>
    <!--summary La notion de variance est une notion souvent énigmatique pour les développeurs. Elles sont pourtant, en présence d&#039;un langage utilisant à la fois du sous-typage et des types paramétrés, des notions importantes.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scala"></category>    <category term="Variance"></category>    <category term="Covariance"></category>    <category term="Contravariance"></category>    <content type="html">
&lt;!-- begin paragraph 26b6b8a1-6307-4aa5-829e-e2d6e0ecba6b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26b6b8a1-6307-4aa5-829e-e2d6e0ecba6b&quot;&gt;La notion de variance est une notion souvent énigmatique pour les développeurs. On retrouve cette notion dès lors que nous sommes en présence d&amp;#39;un langage utilisant à la fois du sous-typage et des types paramétrés (ou type générique). Ce qui inclut une bonne partie des langages proposant un style de programmation orienté objet basé sur des classes et parmi eux, les langages qui utilisent les génériques (Java, C#, Scala...).&lt;/p&gt;
&lt;!-- end paragraph 26b6b8a1-6307-4aa5-829e-e2d6e0ecba6b--&gt;
&lt;!-- begin paragraph d5f55c63-3a65-426a-b6a9-71e24676d951--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d5f55c63-3a65-426a-b6a9-71e24676d951&quot;&gt;Si j&amp;#39;ai écrit énigmatique, c&amp;#39;est bien parce qu&amp;#39;à travers la notion de variance nous nous retrouvons parfois avec des erreurs de compilation difficiles à comprendre. Il faudra alors jouer avec &lt;code&gt;T extends U&lt;/code&gt; ou &lt;code&gt;? super T&lt;/code&gt; en Java. Et en Scala, ce sera avec les expressions &lt;code&gt;+A&lt;/code&gt;, &lt;code&gt;-A&lt;/code&gt;, &lt;code&gt;A &amp;lt;: B&lt;/code&gt; et &lt;code&gt;A &amp;gt;: B&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph d5f55c63-3a65-426a-b6a9-71e24676d951--&gt;
&lt;!-- begin paragraph a0310f1b-684a-413c-b945-fea68a8c9904--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a0310f1b-684a-413c-b945-fea68a8c9904&quot;&gt;Pourtant le choix est relativement simple : sommes-nous en position de producteur / fournisseur de données ou en position de consommateur ? De cette question, vous pourrez déterminer si un paramètre est &lt;b&gt;covariant&lt;/b&gt; ou &lt;b&gt;contravariant&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph a0310f1b-684a-413c-b945-fea68a8c9904--&gt;
&lt;!-- begin heading_1 67acfe89-8044-4ae1-babb-5fae41c0b00a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-67acfe89-8044-4ae1-babb-5fae41c0b00a&quot;&gt;Covariance&lt;/h2&gt;
&lt;!-- end heading_1 67acfe89-8044-4ae1-babb-5fae41c0b00a--&gt;
&lt;!-- begin paragraph 38f1c874-5aee-488e-b230-608d35037ed4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-38f1c874-5aee-488e-b230-608d35037ed4&quot;&gt;La cas le plus souvent rencontré et celui qui est le plus intuitif est la covariance. Ce cas apparaît avec les collections, avec le type Option, mais aussi avec le type IO et les types représentant des fonctions (le type de la sortie d&amp;#39;une fonction est covariant). La covariance s&amp;#39;applique dès qu&amp;#39;il est possible d&amp;#39;utiliser l&amp;#39;analogie avec une boîte, un conteneur, un burrito ou tout ce qui s&amp;#39;apparente à de la production ou à de la fourniture de données.&lt;/p&gt;
&lt;!-- end paragraph 38f1c874-5aee-488e-b230-608d35037ed4--&gt;
&lt;!-- begin paragraph 8e06bfd0-2a81-41c1-a3da-9bd53d6afddc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e06bfd0-2a81-41c1-a3da-9bd53d6afddc&quot;&gt;En exemple, nous allons créer une arborescence représentant des aliments, des fruits et des légumes (classification purement culinaire, tout en s&amp;#39;évitant le cas de la tomate).&lt;/p&gt;
&lt;!-- end paragraph 8e06bfd0-2a81-41c1-a3da-9bd53d6afddc--&gt;
&lt;!-- begin code f369416f-7df9-4793-b134-d5e3e3fb4113--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f369416f-7df9-4793-b134-d5e3e3fb4113&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Aliment {
  def name: String
}
case class Fruit(name: String) extends Aliment
case class Legume(name: String) extends Aliment

val fraise = Fruit(&amp;quot;fraise&amp;quot;)
val brocoli = Legume(&amp;quot;brocoli&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f369416f-7df9-4793-b134-d5e3e3fb4113--&gt;
&lt;!-- begin paragraph 57937173-76bd-4426-af41-65b8164bca4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-57937173-76bd-4426-af41-65b8164bca4d&quot;&gt;Nous avons maintenant deux gourmands : Mary qui mange plutôt varié et Johnny qui ne mange que des fruits. Représentons les par des fonctions.&lt;/p&gt;
&lt;!-- end paragraph 57937173-76bd-4426-af41-65b8164bca4d--&gt;
&lt;!-- begin code 2d96356f-393b-43ed-80d4-e2e681fc0282--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2d96356f-393b-43ed-80d4-e2e681fc0282&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def maryMange(l: List[Aliment]): Unit = ()
def johnnyMange(l: List[Fruit]): Unit = ()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2d96356f-393b-43ed-80d4-e2e681fc0282--&gt;
&lt;!-- begin paragraph 14e1750b-f0c9-4902-956e-e72875b19844--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-14e1750b-f0c9-4902-956e-e72875b19844&quot;&gt;Dans ce cas, passer une liste de fruits à ces deux fonctions ne posent pas problème. Le cas de Johnny est trivial. Pour Mary, il n&amp;#39;y a pas d&amp;#39;inconvénient puisque les fruits sont des aliments. &lt;/p&gt;
&lt;!-- end paragraph 14e1750b-f0c9-4902-956e-e72875b19844--&gt;
&lt;!-- begin code 2be4cdd7-1135-4bc0-9a20-9928b39c2d77--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2be4cdd7-1135-4bc0-9a20-9928b39c2d77&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val fruits: List[Fruit] = List(fraise, fraise)

maryMange(fruits)   // OK 😻 - grâce à la covariance, cela compile !
johnnyMange(fruits) // OK 👌 - ça compile !&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2be4cdd7-1135-4bc0-9a20-9928b39c2d77--&gt;
&lt;!-- begin paragraph 64e27f57-e8ce-47ba-8f51-18bb2e7cc3ec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-64e27f57-e8ce-47ba-8f51-18bb2e7cc3ec&quot;&gt;Par contre :&lt;/p&gt;
&lt;!-- end paragraph 64e27f57-e8ce-47ba-8f51-18bb2e7cc3ec--&gt;
&lt;!-- begin code 8a38f84f-a50e-436d-b308-fd379863c515--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8a38f84f-a50e-436d-b308-fd379863c515&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val aliments: List[Aliment] = List(brocoli, fraise)

maryMange(aliments)   // OK 👌 - ça compile !
johnnyMange(aliments) // NOK 🙀 - erreur de compilation&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8a38f84f-a50e-436d-b308-fd379863c515--&gt;
&lt;!-- begin paragraph ce9e6ce0-0fe4-47cd-a425-b7581b03d1e5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce9e6ce0-0fe4-47cd-a425-b7581b03d1e5&quot;&gt;Nous avons alors une erreur de compilation, puisque Johnny ne peut/veut pas manger n&amp;#39;importe quel aliment.&lt;/p&gt;
&lt;!-- end paragraph ce9e6ce0-0fe4-47cd-a425-b7581b03d1e5--&gt;
&lt;!-- begin paragraph c06e93b9-8586-4e4b-8f0d-16d0911a7721--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c06e93b9-8586-4e4b-8f0d-16d0911a7721&quot;&gt;&lt;code&gt;List[A]&lt;/code&gt; est covariant puisque le type de &lt;code&gt;List&lt;/code&gt; varie dans le même sens que le type de &lt;code&gt;A&lt;/code&gt;. Puisque &lt;code&gt;Fruit&lt;/code&gt; est un sous-type de &lt;code&gt;Aliment&lt;/code&gt;, alors &lt;code&gt;List[Fruit]&lt;/code&gt; est un sous-type de &lt;code&gt;List[Aliment]&lt;/code&gt;. Ça fonctionne avec les types &lt;code&gt;Stream[A]&lt;/code&gt;, &lt;code&gt;Option[A]&lt;/code&gt;, &lt;code&gt;RDD[A]&lt;/code&gt;, &lt;code&gt;Parser[A]&lt;/code&gt;, &lt;code&gt;X =&amp;gt; A&lt;/code&gt; (uniquement si nous nous intéressons à la variance de &lt;code&gt;A&lt;/code&gt;), &lt;code&gt;Either[E, A]&lt;/code&gt;...&lt;/p&gt;
&lt;!-- end paragraph c06e93b9-8586-4e4b-8f0d-16d0911a7721--&gt;
&lt;!-- begin paragraph fdb791bb-accc-4660-9db1-471c569711d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fdb791bb-accc-4660-9db1-471c569711d3&quot;&gt;De manière générale, nous pouvons imaginer un type &lt;code&gt;Producteur[A]&lt;/code&gt; sur lequel nous avons une méthode &lt;code&gt;get&lt;/code&gt; permettant de récupérer une valeur. Nous allons utiliser le symbole &lt;code&gt;+&lt;/code&gt; en prefix de la définition du paramètre de type pour indiquer que le paramètre de type est covariant.&lt;/p&gt;
&lt;!-- end paragraph fdb791bb-accc-4660-9db1-471c569711d3--&gt;
&lt;!-- begin code a6f3fc84-3331-4b9f-9c78-70c40d7221b4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a6f3fc84-3331-4b9f-9c78-70c40d7221b4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Producteur[+A](get: A)

val jardinFraise: Producteur[Fruit] = Producteur(fraise)
val jardinAliment: Producteur[Aliment] = Producteur(brocoli)

def recolte[A](producteur: Producteur[A]): A = producteur.get

recolte[Aliment](jardinFraise)  // OK 😻 - grâce à la covariance, cela compile !
recolte[Aliment](jardinAliment) // OK 👌 - ça compile !

recolte[Fruit](jardinFraise)    // OK 👌 - ça compile !
recolte[Fruit](jardinAliment)   // NOK 🙀 - erreur de compilation&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a6f3fc84-3331-4b9f-9c78-70c40d7221b4--&gt;
&lt;!-- begin heading_1 342a81f1-119a-4c63-abb4-cb80342eb475--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-342a81f1-119a-4c63-abb4-cb80342eb475&quot;&gt;Contravariance&lt;/h2&gt;
&lt;!-- end heading_1 342a81f1-119a-4c63-abb4-cb80342eb475--&gt;
&lt;!-- begin paragraph ac9cf43a-5381-49c7-a0a4-5f6e8bfaf1ce--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ac9cf43a-5381-49c7-a0a4-5f6e8bfaf1ce&quot;&gt;Alors là, accrochez-vous. La contravariance est pour le coup moins intuitif.&lt;/p&gt;
&lt;!-- end paragraph ac9cf43a-5381-49c7-a0a4-5f6e8bfaf1ce--&gt;
&lt;!-- begin paragraph d47d577b-92a5-488d-a4f9-e830dccccb3f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d47d577b-92a5-488d-a4f9-e830dccccb3f&quot;&gt;Nous avons vu que la covariance apparaît lorsqu&amp;#39;un type générique varie dans le même sens que son type sous-jacent. Et bien, il y a des cas où le type générique varie dans le sens inverse par rapport à son type sous-jacent. Autrement dit, on a bien &lt;code&gt;Fruit&lt;/code&gt; qui est sous-type de &lt;code&gt;Aliment&lt;/code&gt;, mais &lt;code&gt;DownUnder[Aliment]&lt;/code&gt; est un sous-type &lt;code&gt;DownUnder[Fruit]&lt;/code&gt;, si &lt;code&gt;DownUnder&lt;/code&gt; est un tel type.&lt;/p&gt;
&lt;!-- end paragraph d47d577b-92a5-488d-a4f9-e830dccccb3f--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://media.giphy.com/media/l3V0o7QyRb08irLag/giphy.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 708226c8-9a53-4553-97a3-ae239f5cfb6a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-708226c8-9a53-4553-97a3-ae239f5cfb6a&quot;&gt;Cette situation arrive dans un cadre de consommation de données. Par exemple, lorsque vous fournissez des données en entrée d&amp;#39;une fonction, lorsque vous envoyez des messages à Kafka, lorsque vous avez un &lt;i&gt;setter&lt;/i&gt; ou lorsque vous exécutez des &lt;code&gt;INSERT&lt;/code&gt; dans une base.&lt;/p&gt;
&lt;!-- end paragraph 708226c8-9a53-4553-97a3-ae239f5cfb6a--&gt;
&lt;!-- begin paragraph fef38747-0c4d-4351-bb04-fe2a0a9bdce4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fef38747-0c4d-4351-bb04-fe2a0a9bdce4&quot;&gt;Imaginons un type &lt;code&gt;Consommateur[A]&lt;/code&gt; qui possède une méthode &lt;code&gt;mange&lt;/code&gt; qui absorbe dans élément de type &lt;code&gt;A&lt;/code&gt;. Nous allons utiliser le symbole &lt;code&gt;-&lt;/code&gt; pour indiquer que le type est contravariant. Nous allons aussi reprendre nos personnages : Mary qui mange plutôt varié et Johnny qui ne mange que des fruits.&lt;/p&gt;
&lt;!-- end paragraph fef38747-0c4d-4351-bb04-fe2a0a9bdce4--&gt;
&lt;!-- begin code fc7ce716-9314-4cf6-a5d4-4629c173198b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fc7ce716-9314-4cf6-a5d4-4629c173198b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Consommateur[-A](mange: A =&amp;gt; Unit)

val mary = Consommateur[Aliment](mange = aliment =&amp;gt; ())
val johnny = Consommateur[Fruit](mange = fruit =&amp;gt; ())&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fc7ce716-9314-4cf6-a5d4-4629c173198b--&gt;
&lt;!-- begin paragraph 4ada7061-91d3-49b4-86b0-c2dcb2b04d4f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4ada7061-91d3-49b4-86b0-c2dcb2b04d4f&quot;&gt;Créons une fonction liée au service dans un restaurant.&lt;/p&gt;
&lt;!-- end paragraph 4ada7061-91d3-49b4-86b0-c2dcb2b04d4f--&gt;
&lt;!-- begin code edceba31-3b89-4990-a967-2c3c66322e70--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-edceba31-3b89-4990-a967-2c3c66322e70&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def serviceRestaurant[A](consommateur: Consommateur[A]): A =&amp;gt; Unit =
  a =&amp;gt; consommateur.mange(a)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code edceba31-3b89-4990-a967-2c3c66322e70--&gt;
&lt;!-- begin paragraph 00395beb-c967-4eb8-87ed-9f18d6e656f6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-00395beb-c967-4eb8-87ed-9f18d6e656f6&quot;&gt;Testons avec Mary :&lt;/p&gt;
&lt;!-- end paragraph 00395beb-c967-4eb8-87ed-9f18d6e656f6--&gt;
&lt;!-- begin code add8d81f-bb21-48bc-89fe-fd9be89c33aa--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-add8d81f-bb21-48bc-89fe-fd9be89c33aa&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;serviceRestaurant[Fruit](mary)   // OK 😻 - grâce à la contravariance, ça compile !
serviceRestaurant[Aliment](mary) // OK 👌 - ça compile !&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code add8d81f-bb21-48bc-89fe-fd9be89c33aa--&gt;
&lt;!-- begin paragraph da455345-7c12-42bd-b736-c29e99c83445--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-da455345-7c12-42bd-b736-c29e99c83445&quot;&gt;Puisque Mary mange toute sorte d&amp;#39;aliments, elle peut aussi manger des fruits.&lt;/p&gt;
&lt;!-- end paragraph da455345-7c12-42bd-b736-c29e99c83445--&gt;
&lt;!-- begin paragraph 27fa5a90-5470-4767-9f9d-c48f57b44c0e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27fa5a90-5470-4767-9f9d-c48f57b44c0e&quot;&gt;Par contre, avec Johnny :&lt;/p&gt;
&lt;!-- end paragraph 27fa5a90-5470-4767-9f9d-c48f57b44c0e--&gt;
&lt;!-- begin code bec1414c-caea-4e81-8c42-2286022a9532--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bec1414c-caea-4e81-8c42-2286022a9532&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;serviceRestaurant[Fruit](johnny)   // OK 👌 - ça compile !
serviceRestaurant[Aliment](johnny) // NOK 🙀 - erreur de compilation&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bec1414c-caea-4e81-8c42-2286022a9532--&gt;
&lt;!-- begin paragraph b2ddf7b8-b361-4266-96a7-44652d6a9a15--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b2ddf7b8-b361-4266-96a7-44652d6a9a15&quot;&gt;Johnny ne mange que des fruits. Du coup, il ne peut pas manger des aliments quelconques tant qu&amp;#39;il n&amp;#39;a pas la garanti qu&amp;#39;il s&amp;#39;agit de fruits.&lt;/p&gt;
&lt;!-- end paragraph b2ddf7b8-b361-4266-96a7-44652d6a9a15--&gt;
&lt;!-- begin paragraph 409f19fc-3319-47df-8f8a-aa71194dbc60--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-409f19fc-3319-47df-8f8a-aa71194dbc60&quot;&gt;Ainsi, &lt;code&gt;Fruit&lt;/code&gt; a beau être un sous-type de &lt;code&gt;Aliment&lt;/code&gt;, &lt;code&gt;Consommateur[Aliment]&lt;/code&gt; est vu comme un sous-type de &lt;code&gt;Consommateur[Fruit]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 409f19fc-3319-47df-8f8a-aa71194dbc60--&gt;
&lt;!-- begin heading_1 544aaae2-72d7-4599-b2f1-1a2ca44e9bf3--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-544aaae2-72d7-4599-b2f1-1a2ca44e9bf3&quot;&gt;Autres cas d&amp;#39;application&lt;/h2&gt;
&lt;!-- end heading_1 544aaae2-72d7-4599-b2f1-1a2ca44e9bf3--&gt;
&lt;!-- begin paragraph cf28aedf-4712-4a6f-bd01-3e75ba195dcf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf28aedf-4712-4a6f-bd01-3e75ba195dcf&quot;&gt;Les notions de covariance et contravariance ne s&amp;#39;appliquent pas seulement aux types. Elles vont aussi s&amp;#39;appliquer aux catégories de types. C&amp;#39;est le cas avec les functors.&lt;/p&gt;
&lt;!-- end paragraph cf28aedf-4712-4a6f-bd01-3e75ba195dcf--&gt;
&lt;!-- begin paragraph 714524a1-4690-4cfa-b928-cef3bf3dca6a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-714524a1-4690-4cfa-b928-cef3bf3dca6a&quot;&gt;En règle générale, quand on imagine la notion de Functor et l&amp;#39;opération &lt;code&gt;map&lt;/code&gt;, on l&amp;#39;imagine dans un cadre covariant :&lt;/p&gt;
&lt;!-- end paragraph 714524a1-4690-4cfa-b928-cef3bf3dca6a--&gt;
&lt;!-- begin code 217c54de-a16e-404c-87cb-f77a8935307e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-217c54de-a16e-404c-87cb-f77a8935307e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A =&amp;gt; B): F[B]
}

def map[A, B, F[_]: Functor](fa:F[A])(f: A =&amp;gt; B): F[B]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 217c54de-a16e-404c-87cb-f77a8935307e--&gt;
&lt;!-- begin paragraph 84aef39d-043e-4794-a4a8-3d212aa87a4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-84aef39d-043e-4794-a4a8-3d212aa87a4d&quot;&gt;Il en existe une version contravariante :&lt;/p&gt;
&lt;!-- end paragraph 84aef39d-043e-4794-a4a8-3d212aa87a4d--&gt;
&lt;!-- begin code 0253051b-c9a3-4f46-9d5d-e8099cfd66e6--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0253051b-c9a3-4f46-9d5d-e8099cfd66e6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait CoFunctor[F[_]] {
  def comap[A, B](f: B =&amp;gt; A)(fa:F[A]): F[B]
}

def comap[A, B, F[_]: CoFunctor](f: B =&amp;gt; A)(fa: F[A]): F[B]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0253051b-c9a3-4f46-9d5d-e8099cfd66e6--&gt;
&lt;!-- begin paragraph d28b5b6c-3748-42a9-a98a-71203a419912--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d28b5b6c-3748-42a9-a98a-71203a419912&quot;&gt;Reprenons le type &lt;code&gt;Consommateur[A]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph d28b5b6c-3748-42a9-a98a-71203a419912--&gt;
&lt;!-- begin paragraph 63748f88-0a5f-4e8a-b19a-df2752630654--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-63748f88-0a5f-4e8a-b19a-df2752630654&quot;&gt;Imaginons que nous avons un &lt;code&gt;Consommateur[String]&lt;/code&gt;. Mais nous avons en entrée des données de type &lt;code&gt;Int&lt;/code&gt;. Pour convertir notre &lt;code&gt;Consommateur[String]&lt;/code&gt; en &lt;code&gt;Consommateur[Int]&lt;/code&gt;, il suffit de fournir une fonction qui convertit des &lt;code&gt;Int&lt;/code&gt; en &lt;code&gt;String&lt;/code&gt; et de l&amp;#39;appliquer à notre &lt;code&gt;Consommateur[String]&lt;/code&gt; en utilisant &lt;code&gt;comap&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 63748f88-0a5f-4e8a-b19a-df2752630654--&gt;
&lt;!-- begin code 49002612-2b8e-4b18-901b-57ff13bc4625--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-49002612-2b8e-4b18-901b-57ff13bc4625&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// create CoFunctor instance for Consommateur
implicit val consommateurCoFunctor =
  new CoFunctor[Consommateur] {
    def comap[A, B](f: B =&amp;gt; A)(fa: Consommateur[A]):Consommateur[B] = 
        Consommateur(b =&amp;gt; fa.mange(f(b)))
  }

val stringConsommateur: Consommateur[String] = ???

val intConsommateur: Consommateur[Int] =
  comap((i: Int) =&amp;gt; i.toString)(stringConsommateur)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 49002612-2b8e-4b18-901b-57ff13bc4625--&gt;
&lt;!-- begin paragraph e388a8d7-0b1e-41eb-8471-b21339b7de73--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e388a8d7-0b1e-41eb-8471-b21339b7de73&quot;&gt;Nous retrouvons aussi les notions de covariance et de contravariance dans le cadre de &lt;a href=&quot;https://propensive.com/opensource/magnolia/tutorial#the-combine-method&quot;&gt;Magnolia&lt;/a&gt;. Magnolia est un petit framework permettant d&amp;#39;associer des typeclasses à des case class et des sealed traits.&lt;/p&gt;
&lt;!-- end paragraph e388a8d7-0b1e-41eb-8471-b21339b7de73--&gt;
&lt;!-- begin paragraph 497ba268-911e-4a5b-9537-ac8531121e39--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-497ba268-911e-4a5b-9537-ac8531121e39&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 497ba268-911e-4a5b-9537-ac8531121e39--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:350710f3c09f497daa72621d8b3abfe5</id>
    <title>Shuffle dans Spark, reduceByKey vs groupByKey</title>
    <updated>2019-03-14T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/shuffle-dans-spark-reducebykey-vs-groupbykey.html"/>
    <!--summary -->    <author>
      <name>Harrison Cheng</name>
      <uri>https://univalence.io/blog/auteurs/harrison-cheng.html</uri>
    </author>        <category term="Spark"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin heading_1 93054d74-6753-4912-ba3f-503c5dd90ee4--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-93054d74-6753-4912-ba3f-503c5dd90ee4&quot;&gt;Introduction&lt;/h2&gt;
&lt;!-- end heading_1 93054d74-6753-4912-ba3f-503c5dd90ee4--&gt;
&lt;!-- begin paragraph c72c2c11-2fed-4398-9fb1-b5cc1fad6d90--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c72c2c11-2fed-4398-9fb1-b5cc1fad6d90&quot;&gt;Lors de mes débuts dans Spark, un collègue m’a dit la chose suivante : &amp;quot;N’utilise pas groupByKey, utilise plutôt reduceByKey&amp;quot;. Je lui demande alors pourquoi l’un plutôt que l’autre et il me répond tout simplement : “à cause du shuffle”. Hein ? Shuffle ?&lt;/p&gt;
&lt;!-- end paragraph c72c2c11-2fed-4398-9fb1-b5cc1fad6d90--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;https://media.giphy.com/media/q0KrtRcr10Bhu/giphy.gif&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph b33fa095-7da8-4ea1-a015-8dc8ab37b684--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-b33fa095-7da8-4ea1-a015-8dc8ab37b684&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b33fa095-7da8-4ea1-a015-8dc8ab37b684--&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph c12d4ece-da3d-45f6-829e-ef773c66ad1a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c12d4ece-da3d-45f6-829e-ef773c66ad1a&quot;&gt;Effectivement, je n’avais encore jamais entendu ce terme dans ma vie, alors qu’il se révèle d’une importance extrême dans le traitement des données en parallèle, surtout si vous voulez avoir une performance élevée dans vos temps de traitement.&lt;/p&gt;
&lt;!-- end paragraph c12d4ece-da3d-45f6-829e-ef773c66ad1a--&gt;
&lt;!-- begin heading_1 ca7b635f-cc57-426f-9772-4a9a13e839ad--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ca7b635f-cc57-426f-9772-4a9a13e839ad&quot;&gt;Qu’est-ce que le shuffle dans Spark ?&lt;/h2&gt;
&lt;!-- end heading_1 ca7b635f-cc57-426f-9772-4a9a13e839ad--&gt;
&lt;!-- begin paragraph 03ca50a0-fee1-47bd-874e-eb611740dc17--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-03ca50a0-fee1-47bd-874e-eb611740dc17&quot;&gt;Shuffle (anglais) : remanier, mélanger, battre (des cartes)...&lt;/p&gt;
&lt;!-- end paragraph 03ca50a0-fee1-47bd-874e-eb611740dc17--&gt;
&lt;!-- begin paragraph 539d8a55-7f00-4606-8831-5ddc64289b58--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-539d8a55-7f00-4606-8831-5ddc64289b58&quot;&gt;Dans Spark, on parle de &lt;b&gt;shuffle&lt;/b&gt; lorsqu’on envoie des données à travers le cluster, d’une machine vers une autre. Par exemple :&lt;/p&gt;
&lt;!-- end paragraph 539d8a55-7f00-4606-8831-5ddc64289b58--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/_jiht1oFQhsKnoWXxglhABPGhpJYfo0bLOtFYT_t_LIMQTFgBCu1bLOohg-M5z0-Kcb7BygqxQNjyFKFdeEzD3fB2JJpZ3D7_cu2GyK9JcSSS0vFHuUoPPZTKCjQXyDXTrMI6nVN&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 2a4adba9-7fb7-406a-8227-d7dcd172984b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2a4adba9-7fb7-406a-8227-d7dcd172984b&quot;&gt;On effectue un shuffle de la donnée A et on l&amp;#39;envoie de la machine A vers la machine B. Simple non ?&lt;/p&gt;
&lt;!-- end paragraph 2a4adba9-7fb7-406a-8227-d7dcd172984b--&gt;
&lt;!-- begin paragraph 0b7b2fb2-78f9-4915-9612-3610a9fb8da7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0b7b2fb2-78f9-4915-9612-3610a9fb8da7&quot;&gt;La chose importante à retenir est que ce transfert de données requiert une &lt;b&gt;connexion réseau&lt;/b&gt; entre les machines, et que plus il y aura de machines et de données à transférer, plus il y aura de &lt;b&gt;trafic réseau&lt;/b&gt;.&lt;/p&gt;
&lt;!-- end paragraph 0b7b2fb2-78f9-4915-9612-3610a9fb8da7--&gt;
&lt;!-- begin heading_1 581aaf5e-1584-471c-aa63-fd88043e244c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-581aaf5e-1584-471c-aa63-fd88043e244c&quot;&gt;Et donc c’est quoi la différence entre groupByKey et reduceByKey ?&lt;/h2&gt;
&lt;!-- end heading_1 581aaf5e-1584-471c-aa63-fd88043e244c--&gt;
&lt;!-- begin paragraph 0e0b0c81-506b-4f24-8a91-80a71ebfbed0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e0b0c81-506b-4f24-8a91-80a71ebfbed0&quot;&gt;Tout d’abord, rafraîchissons nous la mémoire sur les “resilient distributed dataset”, plus communément appelés &lt;b&gt;RDD&lt;/b&gt;. Spark Core travaille avec ces objets, qui sont des collections de records/observations distribuées dans un cluster de machines. Chaque “morceau” de RDD réparti est appelé &lt;b&gt;partition&lt;/b&gt;. Dans Spark, chacune des partitions appartient à un exécuteur.&lt;/p&gt;
&lt;!-- end paragraph 0e0b0c81-506b-4f24-8a91-80a71ebfbed0--&gt;
&lt;!-- begin paragraph 2823618e-ecbb-4566-ad01-fe90b2a6571a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2823618e-ecbb-4566-ad01-fe90b2a6571a&quot;&gt;Exemple de construction d’un RDD :&lt;/p&gt;
&lt;!-- end paragraph 2823618e-ecbb-4566-ad01-fe90b2a6571a--&gt;
&lt;!-- begin code e6411dfd-f5e8-4f7f-ab67-286def041e40--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e6411dfd-f5e8-4f7f-ab67-286def041e40&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val words =
  List(&amp;quot;univalence&amp;quot;, &amp;quot;scala&amp;quot;, &amp;quot;scala&amp;quot;, &amp;quot;univalence&amp;quot;, &amp;quot;univalence&amp;quot;, &amp;quot;scala&amp;quot;)
val wordsRDD = sc.parallelize(words).map(word =&amp;gt; (word, 1))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e6411dfd-f5e8-4f7f-ab67-286def041e40--&gt;
&lt;!-- begin paragraph f51c3ae9-5c37-4491-b70b-38d227c1d3a6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f51c3ae9-5c37-4491-b70b-38d227c1d3a6&quot;&gt;Afin de pouvoir observer la différence entre groupByKey et reduceByKey, essayons de les appliquer au RDD du dessus pour faire un simple word count (occurrence de chaque string d’une liste).&lt;/p&gt;
&lt;!-- end paragraph f51c3ae9-5c37-4491-b70b-38d227c1d3a6--&gt;
&lt;!-- begin code f9aaccb7-9285-49bf-9692-1f8956050a5f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f9aaccb7-9285-49bf-9692-1f8956050a5f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// Code groupByKey
val wordCountGroupByKey = wordsRDD
	.groupByKey() // On groupe chaque paire par leur clé (ici le mot)
	.map((word, list) =&amp;gt; (word, list.sum)) // On fait la somme&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f9aaccb7-9285-49bf-9692-1f8956050a5f--&gt;
&lt;!-- begin code c9c73841-04a7-4e49-98e4-6e3ce6786030--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c9c73841-04a7-4e49-98e4-6e3ce6786030&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// Code reducebykey
val wordCountReduceByKey = wordsRDD
	.reduceByKey(_ + _) // On calcule l’occurrence et groupe chaque paire 
			    // par leur clé&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c9c73841-04a7-4e49-98e4-6e3ce6786030--&gt;
&lt;!-- begin paragraph f45d8a3d-2f77-4442-8185-fd33541a2e5a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f45d8a3d-2f77-4442-8185-fd33541a2e5a&quot;&gt;Les deux fonctions ont à priori l’air de faire le même travail, bien qu’ils s’appliquent différemment. Pourtant, reduceByKey est en fait plus performant que groupByKey lorsqu’il y a beaucoup de données (et ça c’est utile quand on travaille dans le Big Data).&lt;/p&gt;
&lt;!-- end paragraph f45d8a3d-2f77-4442-8185-fd33541a2e5a--&gt;
&lt;!-- begin heading_2 35bd3c64-54bf-434a-9fc2-0efec5f77446--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-35bd3c64-54bf-434a-9fc2-0efec5f77446&quot;&gt;But why ?&lt;/h3&gt;
&lt;!-- end heading_2 35bd3c64-54bf-434a-9fc2-0efec5f77446--&gt;
&lt;!-- begin paragraph 5aef68d3-2a94-4a2b-9126-6b7cb2b8e447--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5aef68d3-2a94-4a2b-9126-6b7cb2b8e447&quot;&gt;Imaginons que le RDD wordsRDD est réparti en deux partitions.&lt;/p&gt;
&lt;!-- end paragraph 5aef68d3-2a94-4a2b-9126-6b7cb2b8e447--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/KwYnpgF6Ldm4UbdQh66Kvfr_j90iJN4kkqQZ4oW0TL73becvCpP8ppi49cyqWoLO2gvUKHPRbOSdWI2j0U0BGGLC94FCQlUjNRatwDB_Yvwjs_x-m2A5H2jypAiCIJy_BFnrHhJd&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 18ef8c41-d3da-4f4e-9e2e-c9c433357c6f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-18ef8c41-d3da-4f4e-9e2e-c9c433357c6f&quot;&gt;En réalité, ce qui se passe pour &lt;code&gt;groupByKey&lt;/code&gt;, c’est qu’il va d’abord chercher à déplacer les clés identiques dans une même partition (1), afin de pouvoir appliquer le traitement (2) (ici faire la somme pour chaque mot) :&lt;/p&gt;
&lt;!-- end paragraph 18ef8c41-d3da-4f4e-9e2e-c9c433357c6f--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh6.googleusercontent.com/IXWQm8LRTXI5R9F7OHgPWvjvhgbvKi8CuxAtIRbNCDaqsc19MDo90EfBV3p-pY9HoEoIY0lORIn3Aeb_KjbMmNqHbbqSg9bRuyhoojUi48BR3AfM3HInsWRg63CWTVv-T7JlMmDa&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 1c15b1e1-f109-4a85-a7a5-bbc52a2ce8f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1c15b1e1-f109-4a85-a7a5-bbc52a2ce8f9&quot;&gt;Il y a deux conséquences à cela :&lt;/p&gt;
&lt;!-- end paragraph 1c15b1e1-f109-4a85-a7a5-bbc52a2ce8f9--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Cela produit un shuffle assez important des données car il doit le faire pour chaque donnée de chaque partition du RDD. Donc, si les partitions ne sont pas sur une même machine, cela provoque un gros trafic réseau.&lt;/li&gt;&lt;li&gt;Si dans notre exemple un mot composait 99% de la liste, groupByKey rassemblera toutes les données ayant comme clé ce mot dans une seule machine, ce qui peut causer des problèmes de mémoire.&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph 40c4efeb-9084-4209-94c7-92940e2071d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-40c4efeb-9084-4209-94c7-92940e2071d4&quot;&gt;Pour &lt;code&gt;reduceByKey&lt;/code&gt;, les choses se passent différemment. Il y a d’abord un “pré-traitement” (1) dans chacune des partitions, puis les données sont déplacées selon leur clé (2), pour enfin avoir un traitement final (3) sur les partitions :&lt;/p&gt;
&lt;!-- end paragraph 40c4efeb-9084-4209-94c7-92940e2071d4--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;https://lh6.googleusercontent.com/c07ur4X5vHjPpEOOmHDgpwWNlfj8X5oMMa0BejZYYxoqjYyeSAS9CCRl13Ea5L1ffr1VqTpzgHLdsUCVTVnu_44q7SevynkZUVJ_LLmRYzDQT8sinM9BaTMBPARdSs67FXwK9xJL&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph ab2a421d-7bab-46b7-b5af-eee13738ec53--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ab2a421d-7bab-46b7-b5af-eee13738ec53&quot;&gt;On n’évite donc pas le shuffle de données avec reduceByKey. Néanmoins la quantité de données à déplacer est amoindrie par le pré-traitement dans chaque partition. Il en résulte un trafic réseau potentiellement moins important (donc amélioration du temps de traitement) et également moins de risque de problème de mémoire.&lt;/p&gt;
&lt;!-- end paragraph ab2a421d-7bab-46b7-b5af-eee13738ec53--&gt;
&lt;!-- begin heading_1 ac2236f8-dc60-4c8b-8650-d2cea1337728--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ac2236f8-dc60-4c8b-8650-d2cea1337728&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 ac2236f8-dc60-4c8b-8650-d2cea1337728--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Prendre en compte le shuffle lorsque des traitements parallèles sont mis en place, notamment les opérations comme &lt;code&gt;reduceByKey&lt;/code&gt; et &lt;code&gt;groupByKey&lt;/code&gt; qui en produisent.&lt;/li&gt;&lt;li&gt;Préférer &lt;code&gt;reduceByKey&lt;/code&gt; à &lt;code&gt;groupByKey&lt;/code&gt; pour avoir une meilleure performance dans le temps de traitement et moins de problèmes de mémoire (en particulier si le nombre moyen d&amp;#39;éléments par clé par partition est élevé).&lt;/li&gt;&lt;li&gt;Si vous voulez aller plus loin il y a &lt;code&gt;combineByKey&lt;/code&gt; qui offre encore plus d&amp;#39;options (&lt;code&gt;reduceByKey&lt;/code&gt; est implémenté en utilisant &lt;code&gt;combineByKey&lt;/code&gt;)&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph e3ffa8b9-470a-41c7-8f9b-d305145e1693--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e3ffa8b9-470a-41c7-8f9b-d305145e1693&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph e3ffa8b9-470a-41c7-8f9b-d305145e1693--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:e2b58f1822db407f879f224332a1c0f7</id>
    <title>Kafka et les groupes de consommateurs</title>
    <updated>2019-03-13T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/kafka-et-les-groupes-de-consommateurs.html"/>
    <!--summary Les groupes de consommateurs de Kafka ne vous disent rien ? Ils vont pourtant devenir inévitables à l&#039;avenir alors autant les découvrir dès maintenant !-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Kafka"></category>    <category term="Confluent"></category>    <category term="Consumer"></category>    <category term="Group"></category>    <category term="GroupID"></category>    <content type="html">
&lt;!-- begin paragraph 4a86511f-cc7e-4e05-8ba6-ffd9043513b5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a86511f-cc7e-4e05-8ba6-ffd9043513b5&quot;&gt;Kafka est l&amp;#39;un des systèmes orientés messages les plus performants existant actuellement. Il se base sur une approche producteurs-consommateurs communiquant à travers des topics. Afin d&amp;#39;aider à mettre en place diverses stratégies d&amp;#39;ingestion de données, Kafka donne la possibilité de regrouper des consommateurs qui vont se partager les messages de topics auxquels ils ont souscript.&lt;/p&gt;
&lt;!-- end paragraph 4a86511f-cc7e-4e05-8ba6-ffd9043513b5--&gt;
&lt;!-- begin paragraph 0bf3d7f8-b712-4186-961d-43974431f0a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0bf3d7f8-b712-4186-961d-43974431f0a7&quot;&gt;La notion de groupe de consommateurs est apparue avec la version 0.9 de Kafka. D&amp;#39;abord optionnelle, cette fonctionnalité va devenir obligatoire. En effet, il y a dans le code KafkaConsumer ce warning qui apparaît, si vous oubliez de fournir le paramètre &lt;code&gt;group.id&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 0bf3d7f8-b712-4186-961d-43974431f0a7--&gt;
&lt;!-- begin paragraph 8d2aca70-2363-4282-ab12-c123248fd22b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d2aca70-2363-4282-ab12-c123248fd22b&quot;&gt;&lt;code&gt;&amp;quot;Support for using the empty group id by consumers is deprecated and will be removed in the next major release.&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 8d2aca70-2363-4282-ab12-c123248fd22b--&gt;
&lt;!-- begin paragraph cb26f0d9-01e2-40a8-9b8d-4d1d5fff53be--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cb26f0d9-01e2-40a8-9b8d-4d1d5fff53be&quot;&gt;Ça vaut le coup d&amp;#39;en savoir plus sur la notion de groupe de consommateurs dans Kafka.&lt;/p&gt;
&lt;!-- end paragraph cb26f0d9-01e2-40a8-9b8d-4d1d5fff53be--&gt;
&lt;!-- begin heading_1 5df04d15-dd43-4908-8b69-bc09d43932a9--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5df04d15-dd43-4908-8b69-bc09d43932a9&quot;&gt;Généralité&lt;/h2&gt;
&lt;!-- end heading_1 5df04d15-dd43-4908-8b69-bc09d43932a9--&gt;
&lt;!-- begin paragraph c194fbe7-0e81-43be-981f-e91b441228e4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c194fbe7-0e81-43be-981f-e91b441228e4&quot;&gt;Dans Kafka, un &lt;b&gt;groupe&lt;/b&gt; représente un &lt;b&gt;ensemble de consommateurs partageant les mêmes offsets,&lt;/b&gt; et donc aussi les mêmes partitions et les mêmes topics.&lt;/p&gt;
&lt;!-- end paragraph c194fbe7-0e81-43be-981f-e91b441228e4--&gt;
&lt;!-- begin paragraph 040dcdfe-7e8a-4ad8-a18d-291900053b36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-040dcdfe-7e8a-4ad8-a18d-291900053b36&quot;&gt;Néanmoins, à un instant donné les différentes partitions d&amp;#39;un topic vont être réparties sur les différents consommateurs du groupe sans qu&amp;#39;il ait d&amp;#39;accès concurrent (ie. il ne peut pas y avoir deux consommateurs du même groupe sur une même partition). Si le groupe contient plus de consommateurs que de partitions, certains consommateurs ne seront pas utilisés.&lt;/p&gt;
&lt;!-- end paragraph 040dcdfe-7e8a-4ad8-a18d-291900053b36--&gt;
&lt;!-- begin paragraph b0621038-7df5-4be2-b15f-d42c4879062e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b0621038-7df5-4be2-b15f-d42c4879062e&quot;&gt;Un groupe est identifié par un groupId, qui est une simple chaîne de caractères. Il est déclaré au niveau des consommateurs et il est géré au niveau brokers.&lt;/p&gt;
&lt;!-- end paragraph b0621038-7df5-4be2-b15f-d42c4879062e--&gt;
&lt;!-- begin paragraph 2ad4fa41-3799-435a-bcbd-94a025a9a429--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2ad4fa41-3799-435a-bcbd-94a025a9a429&quot;&gt;On peut utiliser la notion de groupe pour coordonner des consommateurs de différentes manières :&lt;/p&gt;
&lt;!-- end paragraph 2ad4fa41-3799-435a-bcbd-94a025a9a429--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;avec des groupes différents pour des consommateurs, pour effectuer des traitements différents sur les données apparaissant au niveau d&amp;#39;un même topic. C&amp;#39;est par exemple le cas dans le cadre des analytics lorsqu&amp;#39;on a besoin de sortir différentes mesures ou différents KPI.&lt;/li&gt;&lt;li&gt;avec un groupe unique pour un ensemble de consommateurs, pour répartir la charge d&amp;#39;ingestion des données entre différents consommateurs. Cette approche permet de scaler un service.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph acdfd4c2-cb1a-4365-8bb5-7c5102f8a308--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-acdfd4c2-cb1a-4365-8bb5-7c5102f8a308&quot;&gt;Si vous utilisez la plateforme Confluent, vous verrez l&amp;#39;ensemble des groupes disponibles en cliquant sur &amp;quot;Consumer lag&amp;quot;. Sinon, vous pouvez utiliser la commande &lt;code&gt;kafka-consumer-groups&lt;/code&gt; avec l&amp;#39;option &lt;code&gt;--list&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph acdfd4c2-cb1a-4365-8bb5-7c5102f8a308--&gt;
&lt;!-- begin code 2fbe83f4-8f15-45b1-9e66-6346ac620af5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2fbe83f4-8f15-45b1-9e66-6346ac620af5&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ ./bin/kafka-consumer-groups \
  --bootstrap-server localhost:9092 \
  --list&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2fbe83f4-8f15-45b1-9e66-6346ac620af5--&gt;
&lt;!-- begin paragraph 0df5ea89-01c1-471c-bb78-bca4476d23f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0df5ea89-01c1-471c-bb78-bca4476d23f2&quot;&gt;Ce qui donne, par exemple :&lt;/p&gt;
&lt;!-- end paragraph 0df5ea89-01c1-471c-bb78-bca4476d23f2--&gt;
&lt;!-- begin code 3e0721bf-2907-4257-865d-289647180c29--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3e0721bf-2907-4257-865d-289647180c29&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;my_group
_confluent-controlcenter-5-1-2-1-command
_confluent-controlcenter-5-1-2-1
group_641bd1a6-c275-42a8-b90b-26f92f25cb38&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3e0721bf-2907-4257-865d-289647180c29--&gt;
&lt;!-- begin paragraph e65c3e6d-3f09-4818-a85b-d2dad05c2ef2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e65c3e6d-3f09-4818-a85b-d2dad05c2ef2&quot;&gt;Vous pouvez aussi utiliser la classe &lt;code&gt;AdminClient&lt;/code&gt; dans l&amp;#39;API kafka-client.&lt;/p&gt;
&lt;!-- end paragraph e65c3e6d-3f09-4818-a85b-d2dad05c2ef2--&gt;
&lt;!-- begin code 998748d0-5b8e-4909-bdf6-89ea9e0d4539--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-998748d0-5b8e-4909-bdf6-89ea9e0d4539&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import scala.collection.JavaConverters._

val brokers: String             = &amp;quot;localhost:9092&amp;quot;
val config: Map[String, AnyRef] =
  Map(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -&amp;gt; brokers)
val adminClient: AdminClient    = AdminClient.create(mapAsJavaMap(config))

for (group &amp;lt;- adminClient.listConsumerGroups().all().get().asScala) {
  println(s&amp;quot;${group.groupId()}&amp;quot;)
}

adminClient.close()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 998748d0-5b8e-4909-bdf6-89ea9e0d4539--&gt;
&lt;!-- begin heading_1 28ae830b-229d-4a03-b37f-f43fb44dd09c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-28ae830b-229d-4a03-b37f-f43fb44dd09c&quot;&gt;Création d&amp;#39;un groupe&lt;/h2&gt;
&lt;!-- end heading_1 28ae830b-229d-4a03-b37f-f43fb44dd09c--&gt;
&lt;!-- begin paragraph 9288af0b-a619-470b-9820-b13a7414d76e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9288af0b-a619-470b-9820-b13a7414d76e&quot;&gt;La création d&amp;#39;un groupe est déclenchée par l&amp;#39;opération &lt;code&gt;poll()&lt;/code&gt; au niveau du consommateur. Il faut donc créer un consommateur, le faire souscrire à un topic et tenter une récupération de message pour pouvoir créer un groupe. Si le groupId n&amp;#39;existe pas dans Kafka, il y aura une sélection au niveau des brokers à partir du &lt;i&gt;hashcode&lt;/i&gt; du group ID, pour décider du broker qui deviendra le &lt;b&gt;coordinateur&lt;/b&gt; du groupe. Le coordinateur est responsable de la gestion du groupe, de ses membres, du suivi de son cycle de vie et d&amp;#39;assigner les partitions.&lt;/p&gt;
&lt;!-- end paragraph 9288af0b-a619-470b-9820-b13a7414d76e--&gt;
&lt;!-- begin paragraph a607a58d-b47b-4851-afb9-ccd3d65bde46--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a607a58d-b47b-4851-afb9-ccd3d65bde46&quot;&gt;Par contre, il n&amp;#39;y a pas d&amp;#39;autres possibilités pour créer un groupe, ni en ligne de commande, ni par l&amp;#39;API Kafka, ni par le Control Center de Confluent.&lt;/p&gt;
&lt;!-- end paragraph a607a58d-b47b-4851-afb9-ccd3d65bde46--&gt;
&lt;!-- begin heading_1 ae168c54-ff15-47c8-82c1-61a6aa37b938--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ae168c54-ff15-47c8-82c1-61a6aa37b938&quot;&gt;Cycle de vie&lt;/h2&gt;
&lt;!-- end heading_1 ae168c54-ff15-47c8-82c1-61a6aa37b938--&gt;
&lt;!-- begin paragraph 3d12978f-43d4-4811-a2f6-c85c765f9c99--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3d12978f-43d4-4811-a2f6-c85c765f9c99&quot;&gt;Les groupes ont 4 états de fonctionnement possibles :&lt;/p&gt;
&lt;!-- end paragraph 3d12978f-43d4-4811-a2f6-c85c765f9c99--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;Empty&lt;/b&gt; : le groupe n&amp;#39;a plus de membre associé. Il est éligible à la suppression si aucun commit n&amp;#39;a été fait dans ce groupe. Il s&amp;#39;agit aussi de l&amp;#39;état initial d&amp;#39;un groupe lors de sa création, qui passe alors rapidement à l&amp;#39;état PreparingRebalance, puisque le membre responsable de la création du groupe va par suite demander de rejoindre le groupe.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Stable&lt;/b&gt; : le groupe comporte un ou plusieurs membres et il n&amp;#39;y a pas de changement dans sa configuration.&lt;/li&gt;&lt;li&gt;&lt;b&gt;PreparingRebalance&lt;/b&gt; : un ou plusieurs membres rejoingnent le groupe ou sont retirés du groupe. Le groupe se prépare à un rééquilibrage des charges.&lt;/li&gt;&lt;li&gt;&lt;b&gt;CompletingRebalance&lt;/b&gt; (anciennement &lt;b&gt;AwaitingSync&lt;/b&gt;) : le groupe est en attente de l&amp;#39;attribution d&amp;#39;un état de la part du leader du groupe.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 5315234c-42ca-4201-abbb-cd167764bf69--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5315234c-42ca-4201-abbb-cd167764bf69&quot;&gt;En cas de problème rencontré avec un groupe, celui-ci passe alors à l&amp;#39;état &lt;b&gt;Dead&lt;/b&gt;. Cet état est aussi atteint si un groupe est vide et si ses anciens membres n&amp;#39;ont pas fait de commit. Cet état apparaît enfin dans le cas où une partition associée au groupe disparait.&lt;/p&gt;
&lt;!-- end paragraph 5315234c-42ca-4201-abbb-cd167764bf69--&gt;
&lt;!-- begin paragraph 71677f41-ed30-4b6b-a90e-99620ad9a9fe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71677f41-ed30-4b6b-a90e-99620ad9a9fe&quot;&gt;Voici le diagramme d&amp;#39;états-transitions que révèle le code source sur les états d&amp;#39;un groupe.&lt;/p&gt;
&lt;!-- end paragraph 71677f41-ed30-4b6b-a90e-99620ad9a9fe--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/e2b58f18-22db-407f-879f-224332a1c0f7/kafka-group-groupstate.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph bd98db9f-5a5d-47fb-9a6a-b91775e31b2c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bd98db9f-5a5d-47fb-9a6a-b91775e31b2c&quot;&gt;La notion de rééquilibrage ici sous-entend une rerépartition de la charge au sein d&amp;#39;un groupe et donc une rerépartition des partitions. Ce rééquilibrage a lieu lorsqu&amp;#39;un consommateur rejoint le groupe ou lorsqu&amp;#39;il le quitte. Il a aussi pour effet d&amp;#39;incrémenter le numéro &lt;code&gt;generationId&lt;/code&gt;, qui représente un numéro de configuration (en terme de membres) du groupe. Ce numéro est très présent lorsqu&amp;#39;on regarde les logs des brokers Kafka.&lt;/p&gt;
&lt;!-- end paragraph bd98db9f-5a5d-47fb-9a6a-b91775e31b2c--&gt;
&lt;!-- begin code a3702d23-ca9a-4391-8cca-53d6634110d4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a3702d23-ca9a-4391-8cca-53d6634110d4&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;INFO [GroupCoordinator 0]: Stabilized group my_group generation 2 (__consumer_offsets-14) (kafka.coordinator.group.GroupCoordinator)
INFO [GroupCoordinator 0]: Preparing to rebalance group my_group in state PreparingRebalance with old generation 2 (__consumer_offsets-14) (reason: removing member consumer-2-dc8d50e1-aabc-411d-a91e-aa906ef0b15f on heartbeat expiration) (kafka.coordinator.group.GroupCoordinator)
INFO [GroupCoordinator 0]: Group my_group with generation 3 is now empty (__consumer_offsets-14) (kafka.coordinator.group.GroupCoordinator)
INFO [GroupMetadataManager brokerId=0] Group my_group transitioned to Dead in generation 3 (kafka.coordinator.group.GroupMetadataManager)
INFO [GroupCoordinator 0]: Removed 1 offsets associated with deleted partitions: webclick-0. (kafka.coordinator.group.GroupCoordinator)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a3702d23-ca9a-4391-8cca-53d6634110d4--&gt;
&lt;!-- begin paragraph 1d6f81b9-b5b2-419e-b2a9-1f0aacbff71a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1d6f81b9-b5b2-419e-b2a9-1f0aacbff71a&quot;&gt;Pour pouvoir rester dans un groupe, les membres doivent envoyer un signal de &lt;i&gt;heartbeat&lt;/i&gt; au coordinateur (paramètre : &lt;code&gt;heartbeat.interval.ms&lt;/code&gt; - 3 secondes par défaut d&amp;#39;après le code source de Kafka). S&amp;#39;il n&amp;#39;y a pas de signal envoyé, le coordinateur va attendre un certain temps (paramètre : &lt;code&gt;session.timeout.ms&lt;/code&gt; - 10 secondes par défaut d&amp;#39;après le code source de Kafka). Si un membre n&amp;#39;a toujours rien envoyé malgré ce délai, il est viré du groupe et un rééquilibrage se met en place.&lt;/p&gt;
&lt;!-- end paragraph 1d6f81b9-b5b2-419e-b2a9-1f0aacbff71a--&gt;
&lt;!-- begin paragraph adebe68a-4abb-421d-88ed-d44f05a97909--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-adebe68a-4abb-421d-88ed-d44f05a97909&quot;&gt;À tout moment, vous pouvez utiliser AdminClient de l&amp;#39;API Kafka, le Control Center (menu &amp;quot;Consumer lag&amp;quot;) ou &lt;code&gt;kafka-consumer-groups&lt;/code&gt; pour obtenir des informations sur un groupe. Par exemple :&lt;/p&gt;
&lt;!-- end paragraph adebe68a-4abb-421d-88ed-d44f05a97909--&gt;
&lt;!-- begin code 970d2589-c781-4488-8c6f-a69e4b95cf3f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-970d2589-c781-4488-8c6f-a69e4b95cf3f&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ ./bin/kafka-consumer-groups \
  --bootstrap-server localhost:9092 \
  --describe --group my_group&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 970d2589-c781-4488-8c6f-a69e4b95cf3f--&gt;
&lt;!-- begin paragraph 81a62d8f-fd0c-452a-825f-85b33ecee155--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-81a62d8f-fd0c-452a-825f-85b33ecee155&quot;&gt;Ce qui donne, par exemple :&lt;/p&gt;
&lt;!-- end paragraph 81a62d8f-fd0c-452a-825f-85b33ecee155--&gt;
&lt;!-- begin code e4ca1d82-e932-44af-8e55-d60f3d42c0b8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e4ca1d82-e932-44af-8e55-d60f3d42c0b8&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Consumer group &amp;#39;my_group&amp;#39; has no active members.

TOPIC           PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             CONSUMER-ID     HOST            CLIENT-ID
webclick        0          20              50              30              -               -               -
webclick        1          0               30              30              -               -               -&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e4ca1d82-e932-44af-8e55-d60f3d42c0b8--&gt;
&lt;!-- begin paragraph 5a7c3ff9-68b4-4de0-9095-44838a542594--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a7c3ff9-68b4-4de0-9095-44838a542594&quot;&gt;La notion de &lt;i&gt;lag&lt;/i&gt; correspond à la différence entre le dernier offset de message traité par le groupe pour une partition donnée et l&amp;#39;offset du message le plus récent. Dans l&amp;#39;exemple ci-dessus, nous voyons qu&amp;#39;il reste pour le groupe &lt;code&gt;my_group&lt;/code&gt; 30 messages à traiter sur chaque partition du topic &lt;code&gt;webclick&lt;/code&gt;, mais qu&amp;#39;il n&amp;#39;y a aucun consommateur associé au groupe.&lt;/p&gt;
&lt;!-- end paragraph 5a7c3ff9-68b4-4de0-9095-44838a542594--&gt;
&lt;!-- begin paragraph 77a76228-b82e-4a1f-82bc-dc935b556bea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77a76228-b82e-4a1f-82bc-dc935b556bea&quot;&gt;Avec l&amp;#39;option &lt;code&gt;--state&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 77a76228-b82e-4a1f-82bc-dc935b556bea--&gt;
&lt;!-- begin code de2136c5-5743-49fc-a6c2-a8185d3f2c11--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-de2136c5-5743-49fc-a6c2-a8185d3f2c11&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./bin/kafka-consumer-groups \
  --bootstrap-server localhost:9092 \
  --describe --group my_group \
  --state&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code de2136c5-5743-49fc-a6c2-a8185d3f2c11--&gt;
&lt;!-- begin paragraph 58e6a2c4-9e0b-449d-8530-5df0a42af0cd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-58e6a2c4-9e0b-449d-8530-5df0a42af0cd&quot;&gt;Nous avons à la fois l&amp;#39;état du groupe (&lt;code&gt;Empty&lt;/code&gt; ici), le nombre de membres et la référence du broker qui sert de coordinateur.&lt;/p&gt;
&lt;!-- end paragraph 58e6a2c4-9e0b-449d-8530-5df0a42af0cd--&gt;
&lt;!-- begin code 75ad1bd3-b97e-43ff-8b67-8d7cac0e0df8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-75ad1bd3-b97e-43ff-8b67-8d7cac0e0df8&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Consumer group &amp;#39;my_group&amp;#39; has no active members.

COORDINATOR (ID)          ASSIGNMENT-STRATEGY       STATE                #MEMBERS
192.168.0.40:9092 (0)                               Empty                0&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 75ad1bd3-b97e-43ff-8b67-8d7cac0e0df8--&gt;
&lt;!-- begin heading_1 ce0593eb-e9fd-4201-b744-cab41d10a459--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ce0593eb-e9fd-4201-b744-cab41d10a459&quot;&gt;Suppression d&amp;#39;un groupe&lt;/h2&gt;
&lt;!-- end heading_1 ce0593eb-e9fd-4201-b744-cab41d10a459--&gt;
&lt;!-- begin paragraph 8dbd11c8-dd7d-4d1d-b4e7-7c6cb3bf3e36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8dbd11c8-dd7d-4d1d-b4e7-7c6cb3bf3e36&quot;&gt;La suppression d&amp;#39;un groupe peut être manuelle, en utilisant à nouveau la commande &lt;code&gt;kafka-consumer-groups&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 8dbd11c8-dd7d-4d1d-b4e7-7c6cb3bf3e36--&gt;
&lt;!-- begin code fad5de4e-a19c-41cf-a14b-d2bda5f525e5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fad5de4e-a19c-41cf-a14b-d2bda5f525e5&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ ./bin/kafka-consumer-groups \
  --bootstrap-server localhost:9092 \
  --delete --group my_group&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fad5de4e-a19c-41cf-a14b-d2bda5f525e5--&gt;
&lt;!-- begin paragraph dd8dfcc2-11eb-4322-b3c1-03acc187d357--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dd8dfcc2-11eb-4322-b3c1-03acc187d357&quot;&gt;Elle peut se faire avec AdminClient de l&amp;#39;API Kafka. Par contre, le Control Center ne semble pas offrir cette possibilité.&lt;/p&gt;
&lt;!-- end paragraph dd8dfcc2-11eb-4322-b3c1-03acc187d357--&gt;
&lt;!-- begin paragraph 9000c5ff-17f0-41a3-87de-2194efa430b4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9000c5ff-17f0-41a3-87de-2194efa430b4&quot;&gt;Sinon, la suppression des groupes vides sans commit associé est orchestré par un scheduler interne. Les intervalles de vérification de ce scheduler dépendent du paramètre côté broker &lt;code&gt;offsets.retention.check.interval.ms&lt;/code&gt; (par défaut 10 minutes d&amp;#39;après le code source de Kafka).&lt;/p&gt;
&lt;!-- end paragraph 9000c5ff-17f0-41a3-87de-2194efa430b4--&gt;
&lt;!-- begin heading_1 a2d8f677-cd15-4a9b-b76b-6edbd9349d3b--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a2d8f677-cd15-4a9b-b76b-6edbd9349d3b&quot;&gt;Doc&lt;/h2&gt;
&lt;!-- end heading_1 a2d8f677-cd15-4a9b-b76b-6edbd9349d3b--&gt;
&lt;!-- begin paragraph 7c9bc684-7da6-458a-935d-93654d055269--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7c9bc684-7da6-458a-935d-93654d055269&quot;&gt;Pour plus d&amp;#39;information, vous pouvez aller sur cette page de Confluent qui décrit le fonctionnement des consommateurs et des groupes de consommateurs.&lt;/p&gt;
&lt;!-- end paragraph 7c9bc684-7da6-458a-935d-93654d055269--&gt;
&lt;!-- begin bookmark 0398dfbd-ab5a-4bc9-9519-6ca0fc39b64d--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-0398dfbd-ab5a-4bc9-9519-6ca0fc39b64d&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://docs.confluent.io/platform/current/clients/consumer.html&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Kafka Consumer for Confluent Platform | Confluent Documentation&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;An Apache Kafka consumer group is a set of consumers which cooperate to consume data from some topics.&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://docs.confluent.io/platform/current/clients/consumer.html&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 0398dfbd-ab5a-4bc9-9519-6ca0fc39b64d--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:587ac57567d041cab66c6eab5da3261c</id>
    <title>Spark : Déduplication dans le schéma</title>
    <updated>2019-03-07T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/spark-deduplication-dans-le-schema.html"/>
    <!--summary -->    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="Spark"></category>    <category term="Catalyst"></category>    <category term="SparkSQL"></category>    <content type="html">
&lt;!-- begin paragraph 8d3fffef-7d4f-4a3a-a633-f963cc4993b1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d3fffef-7d4f-4a3a-a633-f963cc4993b1&quot;&gt;Avez-vous déjà vu... un DataFrame Spark ayant deux colonnes ayant le même nom ? Et avez-vous essayé de lancer une requête SQL dessus ?&lt;/p&gt;
&lt;!-- end paragraph 8d3fffef-7d4f-4a3a-a633-f963cc4993b1--&gt;
&lt;!-- begin paragraph d9b834da-9a11-4d08-8316-1a06c43277be--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d9b834da-9a11-4d08-8316-1a06c43277be&quot;&gt;Il est possible que ça soit le cas si vous utilisez Spark, même de manière sporadique. Lorsque vous faites une jointure entre deux dataframes, vous allez rencontrer rapidement ce cas de figure : &lt;/p&gt;
&lt;!-- end paragraph d9b834da-9a11-4d08-8316-1a06c43277be--&gt;
&lt;!-- begin code 5d57e01b-e514-4084-8683-54b856a4bf9b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5d57e01b-e514-4084-8683-54b856a4bf9b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Person(id: String, name: String, age: Int)
case class Status(id: String, status: String)

val statusDF: DataFrame = ss.createDataFrame(Seq(Person(id = &amp;quot;id1&amp;quot;, name = &amp;quot;toto&amp;quot;, age = 13)))
val personDF: DataFrame = ss.createDataFrame(Seq(Status(id = &amp;quot;id1&amp;quot;, status = &amp;quot;active&amp;quot;)))

val dataframe: DataFrame =
  personDF.join(statusDF, personDF(&amp;quot;id&amp;quot;) === statusDF(&amp;quot;id&amp;quot;))

dataframe.select(&amp;quot;id&amp;quot;, &amp;quot;name&amp;quot;, &amp;quot;status&amp;quot;).show()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5d57e01b-e514-4084-8683-54b856a4bf9b--&gt;
&lt;!-- begin paragraph 6fa2a2fa-3f65-4058-a14e-30c943fcd72b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6fa2a2fa-3f65-4058-a14e-30c943fcd72b&quot;&gt;Lors du &lt;code&gt;select&lt;/code&gt;, Spark va vous rappeler gentiment à l&amp;#39;ordre avec l&amp;#39;exception suivante : &lt;/p&gt;
&lt;!-- end paragraph 6fa2a2fa-3f65-4058-a14e-30c943fcd72b--&gt;
&lt;!-- begin code 0424c0a4-4e71-4672-8b83-90e8b9736f62--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0424c0a4-4e71-4672-8b83-90e8b9736f62&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;org.apache.spark.sql.AnalysisException: Reference &amp;#39;id&amp;#39; is ambiguous, could be: id, id.;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0424c0a4-4e71-4672-8b83-90e8b9736f62--&gt;
&lt;!-- begin paragraph 7b34013c-239c-4a4d-8ea1-b4774e831587--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7b34013c-239c-4a4d-8ea1-b4774e831587&quot;&gt;Mais, vous pouvez aussi avoir la maladresse d&amp;#39;enregistrer un tel DataFrame sur disque (en utilisant le format Parquet, par exemple). Sans le savoir, vous allez vous retrouver avec une colonne illisible. Vous allez du coup être accueillis par une autre exception lors de la lecture :&lt;/p&gt;
&lt;!-- end paragraph 7b34013c-239c-4a4d-8ea1-b4774e831587--&gt;
&lt;!-- begin code 7c9daf38-a862-43eb-b447-f0aa5ea51a03--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7c9daf38-a862-43eb-b447-f0aa5ea51a03&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;java.lang.RuntimeException: [id] BINARY was added twice&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7c9daf38-a862-43eb-b447-f0aa5ea51a03--&gt;
&lt;!-- begin paragraph 89562035-b4b3-425c-bcca-67b061afc37f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-89562035-b4b3-425c-bcca-67b061afc37f&quot;&gt;Alors que faire dans ce cas-là ? Sommes-nous condamnés à maudire la personne qui a osé mettre deux noms de colonne identiques ? 🤬&lt;/p&gt;
&lt;!-- end paragraph 89562035-b4b3-425c-bcca-67b061afc37f--&gt;
&lt;!-- begin paragraph 0d5a3c22-d60b-4f58-9818-c29205b971bd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0d5a3c22-d60b-4f58-9818-c29205b971bd&quot;&gt;Vous pouvez vous en sortir avec trois solutions :&lt;/p&gt;
&lt;!-- end paragraph 0d5a3c22-d60b-4f58-9818-c29205b971bd--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Il existe une solution très facile à ce problème, en modifiant le nom de la colonne responsable (ici &lt;code&gt;id&lt;/code&gt;) en amont de la jointure :&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code a8e584e1-568f-4047-adb6-76856aa01a23--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a8e584e1-568f-4047-adb6-76856aa01a23&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val statusDFRenamed: DataFrame = statusDF.withColumnRenamed(&amp;quot;id&amp;quot;, &amp;quot;idStatus&amp;quot;)
//ou bien
val personDFRenamed: DataFrame = personDF.withColumnRenamed(&amp;quot;id&amp;quot;, &amp;quot;idPerson&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a8e584e1-568f-4047-adb6-76856aa01a23--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Si vous souhaitez travailler directement sur la dataframe en sortie de jointure, vous pouvez utiliser une fonction que nous avons mis dans &lt;a href=&quot;https://github.com/UNIVALENCE/spark-plumbus&quot;&gt;Plumbus&lt;/a&gt;, qui réalise un &lt;code&gt;coalesce&lt;/code&gt; entre des colonnes de même nom. Pour réussir à définir cette transformation, on passe par la structure de la requête (Catalyst), afin d&amp;#39;adresser individuellement les colonnes qui ont le même nom (NamedExpression).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 20c376e9-7eb3-46db-91a6-4b3b7096e0de--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-20c376e9-7eb3-46db-91a6-4b3b7096e0de&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def coalesceColWithSameName(df: DataFrame): DataFrame = {
    //pour être sûr que c&amp;#39;est un Project
    val frame: DataFrame = df.select(&amp;quot;*&amp;quot;)
    //récupération de toutes les expressions : id, name, age, id, status
    val cols: Seq[NamedExpression] = frame.queryExecution.analyzed.asInstanceOf[Project].projectList
    //colonnes en sortie, dans le même ordre, sans les doublons
    val outputCols: Array[String] = frame.columns.distinct

    import org.apache.spark.sql.functions.coalesce
    
    val colMap: Map[String, Column] = {
      val nameExpressionsByName: Map[String, Seq[NamedExpression]] = cols.groupBy(_.name)

      nameExpressionsByName.map({
        //seulement une expression pour ce nom
        case (name, Seq(expr)) =&amp;gt; (name, new Column(expr).as(name))
        //plusieurs expressions pour ce nom =&amp;gt; fusion avec coalesce
        case (name, nameExpressions) =&amp;gt; (name, coalesce(nameExpressions.map(x =&amp;gt; new Column(x)): _*).as(name))
      })
    }

    frame.select(outputCols.map(colMap): _*)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 20c376e9-7eb3-46db-91a6-4b3b7096e0de--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Vous pouvez utiliser une autre solution que nous avons aussi ajouté à &lt;a href=&quot;https://github.com/UNIVALENCE/spark-plumbus&quot;&gt;Plumbus&lt;/a&gt;, avec un comportement différent. Ici, les colonnes en doublon vont être renommées séparément avec un suffixe généré automatiquement (&lt;code&gt;_1, _2, _3, ..., _n&lt;/code&gt;). Même technique, nous utilisons la structure de la requête pour adresser individuellement les colonnes.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin code 1c8472a8-0499-4b5d-9106-a25095aec604--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1c8472a8-0499-4b5d-9106-a25095aec604&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def renameColumnsWithSameName(df: DataFrame): DataFrame = {
    val frame: DataFrame = df.select(&amp;quot;*&amp;quot;)
    val namedExpressions: Seq[NamedExpression] = frame.queryExecution.analyzed.asInstanceOf[Project].projectList
    val duplicateNames: Set[String] = namedExpressions.map(_.name).groupBy(identity).filter(_._2.size &amp;gt; 1).keys.toSet

    if (duplicateNames.isEmpty) {
      df
    } else {
      type NameCount = Map[String, Int]
      type Res = (NameCount, Seq[Column])

      val zero: Res = (Map.empty, Nil)

      val res = namedExpressions.foldLeft(zero)({
        case ((counts, cols), exp) =&amp;gt;
          if (duplicateNames(exp.name)) {
            val i = counts.getOrElse(exp.name, 0)
            val col = new Column(exp).as(exp.name + &amp;quot;_&amp;quot; + i)

            (counts + (exp.name -&amp;gt; (i + 1)), cols :+ col)
          } else {
            (counts, cols :+ new Column(exp))
          }
      })

      df.select(res._2: _*)
    }

  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1c8472a8-0499-4b5d-9106-a25095aec604--&gt;
&lt;!-- begin paragraph 19bbb615-cace-483d-95d8-c64def97a554--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-19bbb615-cace-483d-95d8-c64def97a554&quot;&gt;Voici un exemple :&lt;/p&gt;
&lt;!-- end paragraph 19bbb615-cace-483d-95d8-c64def97a554--&gt;
&lt;!-- begin code 77b99259-2e0f-4783-a53f-3f34d512db1c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-77b99259-2e0f-4783-a53f-3f34d512db1c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import ss.implicits._

val person: Person = Person(id = &amp;quot;1&amp;quot;, name = &amp;quot;toto&amp;quot;, age = 13)
val status: Status = Status(id = &amp;quot;1&amp;quot;, status = &amp;quot;active&amp;quot;)

val df1: Dataset[Person] = ss.createDataset(Seq(person))
val df2: Dataset[Status] = ss.createDataset(Seq(status))

val frame: DataFrame = df1.join(df2, df1(&amp;quot;id&amp;quot;) === df2(&amp;quot;id&amp;quot;))

println(&amp;quot;initial DataFrame&amp;quot;)
frame.show()

//clean auto1
println(&amp;quot;coalesceColWithSameName&amp;quot;)
coalesceColWithSameName(frame).show()

//clean auto2
println(&amp;quot;renameColWithSameName&amp;quot;)
renameColumnsWithSameName(frame).show()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 77b99259-2e0f-4783-a53f-3f34d512db1c--&gt;
&lt;!-- begin paragraph f0add2c4-8eb4-4f5e-b366-76a4ea5372c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f0add2c4-8eb4-4f5e-b366-76a4ea5372c0&quot;&gt;Nous obtenons en sortie :&lt;/p&gt;
&lt;!-- end paragraph f0add2c4-8eb4-4f5e-b366-76a4ea5372c0--&gt;
&lt;!-- begin code 7e18d1c9-b09e-40ce-924f-a853570e1231--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7e18d1c9-b09e-40ce-924f-a853570e1231&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;initial DataFrame
+---+----+---+---+------+
| id|name|age| id|status|
+---+----+---+---+------+
|  1|toto| 13|  1|active|
+---+----+---+---+------+

coalesceColWithSameName
+---+----+---+------+
| id|name|age|status|
+---+----+---+------+
|  1|toto| 13|active|
+---+----+---+------+

renameColWithSameName
+----+----+---+----+------+
|id_0|name|age|id_1|status|
+----+----+---+----+------+
|   1|toto| 13|   1|active|
+----+----+---+----+------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7e18d1c9-b09e-40ce-924f-a853570e1231--&gt;
&lt;!-- begin heading_1 61030657-9276-4c2b-a4d3-1bed48471c0b--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-61030657-9276-4c2b-a4d3-1bed48471c0b&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 61030657-9276-4c2b-a4d3-1bed48471c0b--&gt;
&lt;!-- begin paragraph bb82d2fb-9168-4d51-8d9b-362f5ef9226e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bb82d2fb-9168-4d51-8d9b-362f5ef9226e&quot;&gt;&lt;del&gt;Nous avons vu comment gérer ces dataframes assez embêtant et nous avons vu trois manières de répondre à ce problème, chacune ayant un niveau de difficulté variable. Évidemment le but de cet article était d&amp;#39;en apprendre plus sur Catalyst et sur les différentes opérations possibles faisable avec celui-ci. Il peut être très bénéfique d&amp;#39;utiliser Catalyst dans le cadre d&amp;#39;opérations manipulant le schéma d&amp;#39;un dataframe en Spark.&lt;/del&gt;&lt;/p&gt;
&lt;!-- end paragraph bb82d2fb-9168-4d51-8d9b-362f5ef9226e--&gt;
&lt;!-- begin paragraph 20ad8627-8496-4e72-b6f3-fc9ab54788d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-20ad8627-8496-4e72-b6f3-fc9ab54788d7&quot;&gt;Nous avons vu rapidement comment résoudre le problème des doublons de colonnes de manière générique, en allant chercher un peu d&amp;#39;info dans les représentations internes de Spark SQL (ie. avec Catalyst). Ces APIs, assez méconnues, peuvent être très intéressantes si nous devons aller plus loin que la manipulation générique de schéma (avec &lt;code&gt;df.schema&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph 20ad8627-8496-4e72-b6f3-fc9ab54788d7--&gt;
&lt;!-- begin divider 0ebda390-a2fb-4377-840d-320d50e55070--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-0ebda390-a2fb-4377-840d-320d50e55070&quot;&gt;
&lt;!-- end divider 0ebda390-a2fb-4377-840d-320d50e55070--&gt;
&lt;!-- begin heading_1 221dc849-0c9f-4601-90ae-31147423cc2d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-221dc849-0c9f-4601-90ae-31147423cc2d&quot;&gt;Notes&lt;/h2&gt;
&lt;!-- end heading_1 221dc849-0c9f-4601-90ae-31147423cc2d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;lorsque l&amp;#39;on manipule les dataframes  :org.apache.spark.sql.AnalysisException: Reference &amp;#39;id&amp;#39; is ambiguous, could be: id#3, id#7.;&lt;/li&gt;&lt;li&gt;si on sauvegarde en parquet et que l&amp;#39;on relit ça après : Caused by: java.lang.RuntimeException: [id] BINARY was added twice&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 37da80dc-7ff6-491e-9c2e-b8c670abc544--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-37da80dc-7ff6-491e-9c2e-b8c670abc544&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 37da80dc-7ff6-491e-9c2e-b8c670abc544--&gt;
&lt;!-- begin paragraph be38d121-8a3f-48d4-8797-95a821b6e657--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be38d121-8a3f-48d4-8797-95a821b6e657&quot;&gt;Solutions :&lt;/p&gt;
&lt;!-- end paragraph be38d121-8a3f-48d4-8797-95a821b6e657--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;à la main&lt;/li&gt;&lt;li&gt;avec &lt;code&gt;def coalesceColwithSameName(df: DataFrame):DataFrame&lt;/code&gt;&lt;/li&gt;&lt;li&gt;avec &lt;code&gt;def renameColumnsWithSameName(df:DataFrame):DataFrame&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph c978be07-2137-4be2-a04d-f6eaea8edbfd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c978be07-2137-4be2-a04d-f6eaea8edbfd&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c978be07-2137-4be2-a04d-f6eaea8edbfd--&gt;&lt;div class=&quot;block-toggle level-0&quot;&gt;&lt;a class=&quot;block-toggle-button&quot; href=&quot;#&quot;&gt;Code&lt;/a&gt;&lt;div class=&quot;block-toggle-content&quot;&gt;
&lt;!-- begin code ad31f472-7096-451f-8ee9-18e3572fbf9f--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-ad31f472-7096-451f-8ee9-18e3572fbf9f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.spark.sql.catalyst.expressions.NamedExpression
import org.apache.spark.sql.catalyst.plans.logical.Project
import org.apache.spark.sql.{Column, DataFrame, SparkSession}
import org.scalatest.FunSuite

case class Person(id:String,name:String,age:Int)

class TestDuplicateCols extends FunSuite with TestUtils {

  val ss: SparkSession = TestSparkSession.create()

  def coalesceColwithSameName(df: DataFrame):DataFrame = {
    //pour être sûr que c&amp;#39;est un Projet
    val frame = df.select(&amp;quot;*&amp;quot;)
    //récupération de toutes les expressions : id#3, name#4, age#5,id#7, name#8, age#9
    val cols: Seq[NamedExpression] = frame.queryExecution.analyzed.asInstanceOf[Project].projectList
    //colonnes en sortie
    val outpuCols = frame.columns.distinct

    import org.apache.spark.sql.functions.coalesce
    //
    val colMap: Map[String, Column] = {
      val nameExpressionsByName: Map[String, Seq[NamedExpression]] = cols.groupBy(_.name)

      nameExpressionsByName.map({
        //seulement une expression pour ce nom
        case (name, Seq(expr))       =&amp;gt; (name, new Column(expr).as(name))
        //plusieurs expressions pour ce nom =&amp;gt; fusion avec coalesce
        case (name, nameExpressions) =&amp;gt; (name, coalesce(nameExpressions.map(x =&amp;gt; new Column(x)): _*).as(name))
      })
    }
    frame.select(outpuCols.map(colMap):_*)
  }

  def renameColumnsWithSameName(df:DataFrame):DataFrame = {
    val frame = df.select(&amp;quot;*&amp;quot;)

    val namedExpressions = frame.queryExecution.analyzed.asInstanceOf[Project].projectList

    val duplicateNames: Set[String] = namedExpressions.map(_.name).groupBy(identity).filter(_._2.size &amp;gt; 1).keys.toSet

    if(duplicateNames.isEmpty) df else {

      type NameCount = Map[String,Int]
      type Res = (NameCount,Seq[Column])

      val zero:Res = (Map.empty,Nil)

      val res = namedExpressions.foldLeft(zero)({
        case ((counts,cols),exp) =&amp;gt;
        if(duplicateNames(exp.name)) {
          val i = counts.getOrElse(exp.name,0)
          val col = new Column(exp).as(exp.name + &amp;quot;_&amp;quot; + i)

          (counts + (exp.name  -&amp;gt; (i + 1)),cols :+ col)
        } else {
          (counts, cols :+ new Column(exp))
        }
      })

      df.select(res._2:_*)
    }

  }


  test(&amp;quot;toto&amp;quot;) {

    import ss.implicits._

    val person = Person(id = &amp;quot;1&amp;quot;, name = &amp;quot;toto&amp;quot;, age = 13)

    val df1 = ss.createDataset(Seq(person))

    val df2 = df1

    val frame = df1.join(df2, df1(&amp;quot;id&amp;quot;) === df2(&amp;quot;id&amp;quot;))



    //frame.apply(&amp;quot;xyz&amp;quot;)

    frame.show()

    val plan = frame.select(&amp;quot;*&amp;quot;).queryExecution.analyzed

    //bug
    //frame.select(&amp;quot;id&amp;quot;,&amp;quot;age&amp;quot;,&amp;quot;name&amp;quot;)

    //work
    frame.select(df1(&amp;quot;id&amp;quot;),df1(&amp;quot;age&amp;quot;),df1(&amp;quot;name&amp;quot;)).show()


    //clean auto1
    coalesceColwithSameName(frame).show()

    //clean auto2
    renameColumnsWithSameName(frame).show()
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ad31f472-7096-451f-8ee9-18e3572fbf9f--&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;block-toggle level-0&quot;&gt;&lt;a class=&quot;block-toggle-button&quot; href=&quot;#&quot;&gt;Output&lt;/a&gt;&lt;div class=&quot;block-toggle-content&quot;&gt;
&lt;!-- begin code 44efaae3-0936-479a-98eb-b6ff55f34651--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-44efaae3-0936-479a-98eb-b6ff55f34651&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+----+---+---+----+---+
| id|name|age| id|name|age|
+---+----+---+---+----+---+
|  1|toto| 13|  1|toto| 13|
+---+----+---+---+----+---+

+---+---+----+
| id|age|name|
+---+---+----+
|  1| 13|toto|
+---+---+----+

+---+----+---+
| id|name|age|
+---+----+---+
|  1|toto| 13|
+---+----+---+

+----+------+-----+----+------+-----+
|id_0|name_0|age_0|id_1|name_1|age_1|
+----+------+-----+----+------+-----+
|   1|  toto|   13|   1|  toto|   13|
+----+------+-----+----+------+-----+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 44efaae3-0936-479a-98eb-b6ff55f34651--&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;block-toggle level-0&quot;&gt;&lt;a class=&quot;block-toggle-button&quot; href=&quot;#&quot;&gt;Code formaté&lt;/a&gt;&lt;div class=&quot;block-toggle-content&quot;&gt;
&lt;!-- begin code fcb3b948-6b01-4427-997a-c1b6b5fe00de--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-fcb3b948-6b01-4427-997a-c1b6b5fe00de&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.spark.sql.catalyst.expressions.NamedExpression
import org.apache.spark.sql.catalyst.plans.logical.Project
import org.apache.spark.sql.{Column, DataFrame, SparkSession}
import org.scalatest.FunSuite

case class Person(id: String, name: String, age: Int)

class TestDuplicateCols extends FunSuite {

  val ss: SparkSession = SparkSession.builder().master(&amp;quot;local[*]&amp;quot;).getOrCreate()

  def coalesceColwithSameName(df: DataFrame): DataFrame = {
    //pour être sûr que c&amp;#39;est un Project
    val frame = df.select(&amp;quot;*&amp;quot;)
    //récupération de toutes les expressions : id#3, name#4, age#5,id#7, name#8, age#9
    val cols: Seq[NamedExpression] = frame.queryExecution.analyzed.asInstanceOf[Project].projectList
    //colonnes en sortie
    val outpuCols = frame.columns.distinct

    import org.apache.spark.sql.functions.coalesce
    
    val colMap: Map[String, Column] = {
      val nameExpressionsByName: Map[String, Seq[NamedExpression]] = cols.groupBy(_.name)

      nameExpressionsByName.map({
        //seulement une expression pour ce nom
        case (name, Seq(expr)) =&amp;gt; (name, new Column(expr).as(name))
        //plusieurs expressions pour ce nom =&amp;gt; fusion avec coalesce
        case (name, nameExpressions) =&amp;gt; (name, coalesce(nameExpressions.map(x =&amp;gt; new Column(x)): _*).as(name))
      })
    }
    frame.select(outpuCols.map(colMap): _*)
  }

  def renameColumnsWithSameName(df: DataFrame): DataFrame = {
    val frame = df.select(&amp;quot;*&amp;quot;)

    val namedExpressions = frame.queryExecution.analyzed.asInstanceOf[Project].projectList

    val duplicateNames: Set[String] = namedExpressions.map(_.name).groupBy(identity).filter(_._2.size &amp;gt; 1).keys.toSet

    if (duplicateNames.isEmpty) df else {

      type NameCount = Map[String, Int]
      type Res = (NameCount, Seq[Column])

      val zero: Res = (Map.empty, Nil)

      val res = namedExpressions.foldLeft(zero)({
        case ((counts, cols), exp) =&amp;gt;
          if (duplicateNames(exp.name)) {
            val i = counts.getOrElse(exp.name, 0)
            val col = new Column(exp).as(exp.name + &amp;quot;_&amp;quot; + i)

            (counts + (exp.name -&amp;gt; (i + 1)), cols :+ col)
          } else {
            (counts, cols :+ new Column(exp))
          }
      })

      df.select(res._2: _*)
    }

  }


  test(&amp;quot;toto&amp;quot;) {

    import ss.implicits._

    val person = Person(id = &amp;quot;1&amp;quot;, name = &amp;quot;toto&amp;quot;, age = 13)

    val df1 = ss.createDataset(Seq(person))

    val df2 = df1

    val frame = df1.join(df2, df1(&amp;quot;id&amp;quot;) === df2(&amp;quot;id&amp;quot;))

    frame.show()

    val plan = frame.select(&amp;quot;*&amp;quot;).queryExecution.analyzed

    //bug
    //frame.select(&amp;quot;id&amp;quot;,&amp;quot;age&amp;quot;,&amp;quot;name&amp;quot;)

    //work
    frame.select(df1(&amp;quot;id&amp;quot;), df1(&amp;quot;age&amp;quot;), df1(&amp;quot;name&amp;quot;)).show()


    //clean auto1
    coalesceColwithSameName(frame).show()

    //clean auto2
    renameColumnsWithSameName(frame).show()
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fcb3b948-6b01-4427-997a-c1b6b5fe00de--&gt;&lt;/div&gt;&lt;/div&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:53a30aab05f049ec92417f9ab6c41f01</id>
    <title>En finir avec les problèmes de case class dans Spark</title>
    <updated>2019-02-25T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/en-finir-avec-les-problemes-de-case-class-dans-spark.html"/>
    <!--summary Dans un article précédent on montrait comment utiliser des fonctions d&#039;ordre supérieur avec spark &lt;2.4 mais la solution souffrait d&#039;un problème majeur, le mélange avec les case class...-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Spark"></category>    <category term="Scala"></category>    <category term="Magnolia"></category>    <category term="Programmation fonctionnelle"></category>    <category term="Twitter Chill"></category>    <content type="html">
&lt;!-- begin paragraph 30e66e3a-71cb-4f25-a36a-bdb4a3c9d219--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-30e66e3a-71cb-4f25-a36a-bdb4a3c9d219&quot;&gt;Dans un &lt;a href=&quot;https://blog.univalence.io/fonctions-dordre-superieur-dans-spark-2-pour-traiter-des-structures-imbriquees/&quot;&gt;précédent article&lt;/a&gt;, nous avons vu qu&amp;#39;il est possible d&amp;#39;avoir des fonctions d&amp;#39;ordre supérieur avec des versions anciennes de Spark. Sauf que notre implémentation souffre d&amp;#39;un défaut que nous retrouvons assez souvent dès lors que nous commençons à mélanger case class et dataset. Voyons ensemble comment nous allons y remédier.&lt;/p&gt;
&lt;!-- end paragraph 30e66e3a-71cb-4f25-a36a-bdb4a3c9d219--&gt;
&lt;!-- begin paragraph 3ca7a7a8-c8b0-4bd7-b38d-178eb42ef960--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ca7a7a8-c8b0-4bd7-b38d-178eb42ef960&quot;&gt;Nous allons créer un dataset représentant des familles. Pour cela, créons une case class représentant des personnes.&lt;/p&gt;
&lt;!-- end paragraph 3ca7a7a8-c8b0-4bd7-b38d-178eb42ef960--&gt;
&lt;!-- begin code 611c690a-896d-49d7-9f1b-710abd31d039--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-611c690a-896d-49d7-9f1b-710abd31d039&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class Person(name: String, age: Int)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 611c690a-896d-49d7-9f1b-710abd31d039--&gt;
&lt;!-- begin paragraph 64ca9a97-04dc-42fe-bd3f-e41487f7489a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-64ca9a97-04dc-42fe-bd3f-e41487f7489a&quot;&gt;Créons maintenant notre dataset.&lt;/p&gt;
&lt;!-- end paragraph 64ca9a97-04dc-42fe-bd3f-e41487f7489a--&gt;
&lt;!-- begin code 7afce9c7-5552-4c79-813f-4582f4912291--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7afce9c7-5552-4c79-813f-4582f4912291&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val df =
  ss.createDataset(Seq(
      (&amp;quot;1&amp;quot;, Seq(
        Person(&amp;quot;John&amp;quot;, 32),
        Person(&amp;quot;Mary&amp;quot;, 31)
      )),
      (&amp;quot;2&amp;quot;, Seq(
        Person(&amp;quot;Fred&amp;quot;, 42),
        Person(&amp;quot;Elsa&amp;quot;, 40),
        Person(&amp;quot;Daryll&amp;quot;, 10)
      ))
  )).toDF(&amp;quot;id&amp;quot;, &amp;quot;family&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7afce9c7-5552-4c79-813f-4582f4912291--&gt;
&lt;!-- begin code 11906c4f-bc28-4d4c-be1f-0edd1b3403e2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-11906c4f-bc28-4d4c-be1f-0edd1b3403e2&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+--------------------------------------+
|id |family                                |
+---+--------------------------------------+
|1  |[[John, 32], [Mary, 31]]              |
|2  |[[Fred, 42], [Elsa, 40], [Daryll, 10]]|
+---+--------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 11906c4f-bc28-4d4c-be1f-0edd1b3403e2--&gt;
&lt;!-- begin paragraph bb8b6af5-d091-4e68-9394-129a78bee14b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bb8b6af5-d091-4e68-9394-129a78bee14b&quot;&gt;Formalisons une requête qui ne conserve que les noms des personnes du dataset précédent. Pour cela nous allons utiliser l&amp;#39;une des fonctions d&amp;#39;ordre supérieur que nous avons vu dans l&amp;#39;article précédent sur le sujet.&lt;/p&gt;
&lt;!-- end paragraph bb8b6af5-d091-4e68-9394-129a78bee14b--&gt;
&lt;!-- begin code 2edc2420-abe1-4165-821e-75b88393e086--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2edc2420-abe1-4165-821e-75b88393e086&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import Implicits._

df.select(
  $&amp;quot;id&amp;quot;,
  $&amp;quot;family&amp;quot;.map((p: Person) =&amp;gt; p.name).as(&amp;quot;family&amp;quot;)
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2edc2420-abe1-4165-821e-75b88393e086--&gt;
&lt;!-- begin paragraph f4146eb3-1d3d-4cc1-8a29-8707f9434945--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f4146eb3-1d3d-4cc1-8a29-8707f9434945&quot;&gt;Nous obtenons alors une exception.&lt;/p&gt;
&lt;!-- end paragraph f4146eb3-1d3d-4cc1-8a29-8707f9434945--&gt;
&lt;!-- begin code 12808b53-6687-4a8d-b505-76c1224f9c90--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-12808b53-6687-4a8d-b505-76c1224f9c90&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Exception in thread &amp;quot;main&amp;quot; org.apache.spark.SparkException: Failed to execute user defined function($anonfun$4: (array&amp;lt;struct&amp;lt;name:string,age:int&amp;gt;&amp;gt;) =&amp;gt; array&amp;lt;string&amp;gt;)
	at org.apache.spark.sql.catalyst.expressions.ScalaUDF.eval(ScalaUDF.scala:1066)
	at org.apache.spark.sql.catalyst.expressions.Alias.eval(namedExpressions.scala:151)
	at org.apache.spark.sql.catalyst.expressions.InterpretedProjection.apply(Projection.scala:50)
	...
Caused by: java.lang.ClassCastException: org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema cannot be cast to xag.Person
	at Example$$anonfun$main$2.apply(TestMain.scala:33)
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
	...&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 12808b53-6687-4a8d-b505-76c1224f9c90--&gt;
&lt;!-- begin paragraph 6135f92e-8238-47c5-a92b-92a8f43eb30c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6135f92e-8238-47c5-a92b-92a8f43eb30c&quot;&gt;L&amp;#39;exception d&amp;#39;origine indique : &lt;code&gt;java.lang.ClassCastException: org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema cannot be cast to Person&lt;/code&gt;. Catalyst est incapable de reconstituer l&amp;#39;entité &lt;code&gt;Person&lt;/code&gt; à partir du résultat de la version sérialisée de cette entité contenue dans le dataset.&lt;/p&gt;
&lt;!-- end paragraph 6135f92e-8238-47c5-a92b-92a8f43eb30c--&gt;
&lt;!-- begin paragraph a9dc84ac-5ed7-4b22-aacc-29985db49c77--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a9dc84ac-5ed7-4b22-aacc-29985db49c77&quot;&gt;Pour résoudre cette exception, nous allons utiliser &lt;code&gt;com.twitter.chill.Externalizer&lt;/code&gt;. Twitter Chill est une bibliothèque d&amp;#39;extension pour Kryo et Kryo est une bibliothèque de sérialisation Java utilisé par Spark. &lt;code&gt;Externalizer&lt;/code&gt; permet de mettre en place une sorte de &amp;quot;boîte&amp;quot; pour sérialiser et désérialiser n&amp;#39;importe quel type, même non sérialisable au sens Java.&lt;/p&gt;
&lt;!-- end paragraph a9dc84ac-5ed7-4b22-aacc-29985db49c77--&gt;
&lt;!-- begin paragraph d8cf28fc-2641-46a4-8ea2-ffff17a9bb3b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d8cf28fc-2641-46a4-8ea2-ffff17a9bb3b&quot;&gt;Nous allons aussi utiliser une opération permettant d&amp;#39;effectuer un &amp;quot;nettoyage&amp;quot; sur les données rencontrées, en récupérant notamment les types sous-jacent cachés derrière &lt;code&gt;Any&lt;/code&gt;, monnaie courante avec Spark. Nous allons utiliser une approche (ou plutôt idiome) &lt;i&gt;typeclass&lt;/i&gt; pour associer cette opération à l&amp;#39;ensemble des types utilisés dans les données (en attendant la nouvelle syntaxe sensée arriver avec Scala 3 !). Voici l&amp;#39;interface de cette opération.&lt;/p&gt;
&lt;!-- end paragraph d8cf28fc-2641-46a4-8ea2-ffff17a9bb3b--&gt;
&lt;!-- begin code d681b7c6-9896-49f5-b1f2-72a321409301--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d681b7c6-9896-49f5-b1f2-72a321409301&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// The typeclass with the operation to clean data for Spark
trait CleanFromRow[A] extends Serializable {
  def clean(a: Any): A
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d681b7c6-9896-49f5-b1f2-72a321409301--&gt;
&lt;!-- begin paragraph 337d9b3b-dd4a-43fc-9b0e-3e69a7378dc7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-337d9b3b-dd4a-43fc-9b0e-3e69a7378dc7&quot;&gt;Ci-dessous, nous retrouvons la classe implicite avec l&amp;#39;opération &lt;code&gt;map&lt;/code&gt;, que nous avons définie dans l&amp;#39;article précédent avec un léger refactoring : 1/ on s&amp;#39;assure que l&amp;#39;opération &lt;code&gt;clean&lt;/code&gt; s&amp;#39;applique bien au type &lt;code&gt;A&lt;/code&gt; donné en entrée, 2/ on s&amp;#39;assure que le &amp;quot;nettoyage&amp;quot; et la sérialisation seront réalisés (&lt;code&gt;serializeAndClean&lt;/code&gt;). Le refactoring ne s&amp;#39;applique qu&amp;#39;à l&amp;#39;opération &lt;code&gt;map&lt;/code&gt;, mais nous pouvons aussi l&amp;#39;appliquer sur les autres opérations : &lt;code&gt;flatMap&lt;/code&gt; et &lt;code&gt;foldLeft&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 337d9b3b-dd4a-43fc-9b0e-3e69a7378dc7--&gt;
&lt;!-- begin code c83751c3-146d-401a-8191-f8907be13537--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c83751c3-146d-401a-8191-f8907be13537&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Implicits2 {

  implicit class RicherColumn2(column: Column) {

    def map[A : CleanFromRow: TypeTag, B : TypeTag](f: A =&amp;gt; B): Column = {
      val f0 = udf[Seq[B], Seq[A]](serializeAndClean(s =&amp;gt; s.map(f)))

      f0(column)
    }

  }

  import com.twitter.chill.Externalizer

  private def serializeAndClean[A: CleanFromRow, B](f: Seq[A] =&amp;gt; B): Seq[A] =&amp;gt; B = {
    val cleaner: Externalizer[A =&amp;gt; A] =
      Externalizer(implicitly[CleanFromRow[A]].clean _)
    val fExt: Externalizer[Seq[A] =&amp;gt; B] =
      Externalizer(f)

    values =&amp;gt;
      if (values == null) {
        null.asInstanceOf[B]
      } else {
        val fExt0: Seq[A] =&amp;gt; B = fExt.get
        val cleaner0: A =&amp;gt; A = cleaner.get

        fExt0(values.map(cleaner0))
      }
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c83751c3-146d-401a-8191-f8907be13537--&gt;
&lt;!-- begin paragraph 9990d291-5931-4286-a151-6969fb87ca36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9990d291-5931-4286-a151-6969fb87ca36&quot;&gt;Le comportement de l&amp;#39;opération de &amp;quot;nettoyage&amp;quot; va dépendre du type rencontré. Nous définissons donc ci-dessous différentes instances du type &lt;code&gt;CleanFromRow&lt;/code&gt; pour cela (Int, String, Seq...). Nous utilisons aussi &lt;a href=&quot;https://propensive.com/opensource/magnolia/&quot;&gt;Magnolia&lt;/a&gt; de &lt;a href=&quot;https://twitter.com/propensive&quot;&gt;Jon Pretty&lt;/a&gt; pour associer l&amp;#39;opération &lt;code&gt;clean&lt;/code&gt; à des case class. L&amp;#39;avantage de Magnolia, c&amp;#39;est qu&amp;#39;il est récursif. Du coup, ça fonctionne aussi avec des structures imbriquées de différentes profondeurs.&lt;/p&gt;
&lt;!-- end paragraph 9990d291-5931-4286-a151-6969fb87ca36--&gt;
&lt;!-- begin code 10675e14-c303-4165-871c-a390c820784d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-10675e14-c303-4165-871c-a390c820784d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object CleanFromRow {
  import magnolia._
  import scala.reflect.ClassTag
  import language.experimental.macros

  type Typeclass[T] = CleanFromRow[T]

  private def instance[A]: Typeclass[A] =
    new Typeclass[A] {
      override def clean(a: Any): A = a.asInstanceOf[A]
    }

  // Instances to associate clean operation to basic types
  implicit val double: Typeclass[Double] = instance
  implicit val boolean: Typeclass[Boolean] = instance
  implicit val strCFR: Typeclass[String] = instance
  implicit val intCFR: Typeclass[Int] = instance
  implicit val longCFR: Typeclass[Long] = instance
  // add other typeclass instances for basic types...

  // Instance for Option type
  implicit def opt[T: Typeclass: Manifest]: Typeclass[Option[T]] =
    new Typeclass[Option[T]] {
      // this helps to avoid type erasure warning
      private val rc = implicitly[Manifest[T]].runtimeClass

      override def clean(a: Any): Option[T] =
        a match {
          case ox: Option[_]
              if ox.forall(x =&amp;gt; rc.isAssignableFrom(x.getClass)) =&amp;gt;
            ox.asInstanceOf[Option[T]]
          case null =&amp;gt; None
          case x    =&amp;gt; Option(implicitly[Typeclass[T]].clean(x))
        }
    }

  // Instance for Seq type
  implicit def seq[T:Typeclass:Manifest]: Typeclass[Seq[T]] = {
    new Typeclass[Seq[T]] {
      // this helps to avoid type erasure warning
      private val rc = implicitly[Manifest[T]].runtimeClass

      override def clean(a: Any): Seq[T] =
        a match {
          case Nil =&amp;gt; Nil
          case xs: Seq[_]
              if xs.forall(x =&amp;gt; rc.isAssignableFrom(x.getClass)) =&amp;gt;
            xs.asInstanceOf[Seq[T]]
          case x: Seq[_] =&amp;gt; x.map(implicitly[Typeclass[T]].clean)
        }
    }
  }

  // Instance generator for case classes
  def combine[T: ClassTag](ctx: CaseClass[CleanFromRow, T]): Typeclass[T] =
    new Typeclass[T] {
      override def clean(a: Any): T =
        a match {
          case a: T =&amp;gt; a
          case r: Row =&amp;gt;
            val values: Seq[Any] =
              r.toSeq
                .zip(ctx.parameters)
                .map {
                  case (rowValue, param) =&amp;gt; param.typeclass.clean(rowValue)
                }
            ctx.rawConstruct(values)
        }
    }

  implicit def gen[T]: CleanFromRow[T] = macro Magnolia.gen[T]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 10675e14-c303-4165-871c-a390c820784d--&gt;
&lt;!-- begin paragraph 3a890de0-acde-48e2-86d0-363aefd7dba7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3a890de0-acde-48e2-86d0-363aefd7dba7&quot;&gt;Reprenons maintenant notre exemple précédent en remplaçant &lt;code&gt;Implicits&lt;/code&gt; pour &lt;code&gt;Implicits2&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 3a890de0-acde-48e2-86d0-363aefd7dba7--&gt;
&lt;!-- begin code ebda5248-ee95-4f93-8658-85aa422d4bf7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ebda5248-ee95-4f93-8658-85aa422d4bf7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import Implicits2._

df.select(
  $&amp;quot;id&amp;quot;,
  $&amp;quot;family&amp;quot;.map((p: Person) =&amp;gt; p.name).as(&amp;quot;family&amp;quot;)
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ebda5248-ee95-4f93-8658-85aa422d4bf7--&gt;
&lt;!-- begin code b98c713e-2373-4332-b7c4-65f6c09f8756--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b98c713e-2373-4332-b7c4-65f6c09f8756&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+--------------------+
|id |family              |
+---+--------------------+
|1  |[John, Mary]        |
|2  |[Fred, Elsa, Daryll]|
+---+--------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b98c713e-2373-4332-b7c4-65f6c09f8756--&gt;
&lt;!-- begin paragraph 26679099-1f03-49ff-b7ce-971f35e48037--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26679099-1f03-49ff-b7ce-971f35e48037&quot;&gt;C&amp;#39;est le résultat attendu !&lt;/p&gt;
&lt;!-- end paragraph 26679099-1f03-49ff-b7ce-971f35e48037--&gt;
&lt;!-- begin divider 992de9ec-087b-4232-b8f5-e380526626e8--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-992de9ec-087b-4232-b8f5-e380526626e8&quot;&gt;
&lt;!-- end divider 992de9ec-087b-4232-b8f5-e380526626e8--&gt;
&lt;!-- begin heading_1 0208caac-dfa2-481b-a88d-f56c82d01ea8--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-0208caac-dfa2-481b-a88d-f56c82d01ea8&quot;&gt;Annexe&lt;/h2&gt;
&lt;!-- end heading_1 0208caac-dfa2-481b-a88d-f56c82d01ea8--&gt;
&lt;!-- begin paragraph 50e8306c-97b3-49be-92e7-fcaec79d4218--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50e8306c-97b3-49be-92e7-fcaec79d4218&quot;&gt;Suite de&lt;/p&gt;
&lt;!-- end paragraph 50e8306c-97b3-49be-92e7-fcaec79d4218--&gt;
&lt;!-- begin link_to_page 00205663-d040-4c2a-a4d8-915fc71d783d--&gt;&lt;a class=&quot;block-link_to_page&quot; href=&quot;/blog/articles/fonctions-d-ordre-superieur-dans-spark-2-pour-traiter-des-structures-imbriquees&quot; id=&quot;m-00205663-d040-4c2a-a4d8-915fc71d783d&quot;&gt;&lt;small&gt;&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/small&gt; Fonctions d&#039;ordre supérieur dans Spark 2 pour traiter des structures imbriquées&lt;/a&gt;
&lt;!-- end link_to_page 00205663-d040-4c2a-a4d8-915fc71d783d--&gt;
&lt;!-- begin code 8ff737e0-74dd-47fa-929b-f9b325c635d9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8ff737e0-74dd-47fa-929b-f9b325c635d9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;package utils

import com.twitter.chill.MeatLocker
import org.apache.spark.sql.Row

import scala.reflect.ClassTag

trait CleanFromRow[A] extends Serializable {
  def clean(a: Any): A
}

object CleanFromRow {

  private def instance[A]: Typeclass[A] = new Typeclass[A] {
    override def clean(a: Any): A = a.asInstanceOf[A]
  }

  implicit val double:  Typeclass[Double]  = instance
  implicit val boolean: Typeclass[Boolean] = instance
  implicit val strCFR:  Typeclass[String]  = instance
  implicit val intCFR:  Typeclass[Int]     = instance
  implicit val longCFR: Typeclass[Long]    = instance

  implicit def opt[T: Typeclass: Manifest]: Typeclass[Option[T]] =
    new Typeclass[Option[T]] {
      override def clean(a: Any): Option[T] = {
        a match {
          case x: Option[T] =&amp;gt; x
          case null =&amp;gt; None
          case x    =&amp;gt; Option(implicitly[Typeclass[T]].clean(x))
        }
      }
    }

  implicit def seq[T: Typeclass: Manifest]: Typeclass[Seq[T]] =
    new Typeclass[Seq[T]] {
      override def clean(a: Any): Seq[T] = a match {
        case x: Seq[T] =&amp;gt; x
        case x: Seq[_] =&amp;gt; x.map(implicitly[Typeclass[T]].clean)
      }
    }

  import magnolia._

  import language.experimental.macros

  type Typeclass[T] = CleanFromRow[T]

  def combine[T: ClassTag](ctx: CaseClass[CleanFromRow, T]): CleanFromRow[T] = {
    new CleanFromRow[T] {

      override def clean(a: Any): T = {
        a match {
          case a: T =&amp;gt; a
          case r: Row =&amp;gt;
            val values: Seq[Any] =
              r.toSeq
                .zip(ctx.parameters)
                .map({
                  case (rowValue, param) =&amp;gt; param.typeclass.clean(rowValue)
                })
            ctx.rawConstruct(values)

        }

      }
    }
  }

  implicit def gen[T]: CleanFromRow[T] = macro Magnolia.gen[T]
}

case class Tata(a:     String, b: Int)
case class Toto(tatas: Seq[Tata])

object UnnestedSpark {

  def tataToX(seq: Seq[Tata]): String = {
    seq.map(_.a).headOption.getOrElse(&amp;quot;&amp;quot;)
  }

  def cleanF[A: CleanFromRow, B](f: Seq[A] =&amp;gt; B): Seq[A] =&amp;gt; B = {
    val g: MeatLocker[A =&amp;gt; A] =
      com.twitter.chill.MeatLocker(implicitly[CleanFromRow[A]].clean _)
    val mf: MeatLocker[Seq[A] =&amp;gt; B] = com.twitter.chill.MeatLocker(f)

    a =&amp;gt;
      {

        if (a == null) {
          null.asInstanceOf[B]
        } else {
          val f0: Seq[A] =&amp;gt; B = mf.get
          val g0: A      =&amp;gt; A = g.get
          f0(a.map(g0))
        }
      }

  }

  /*def main(args: Array[String]): Unit = {

    val ss = TestSparkSession.create()

    import ss.implicits._

    val df = ss.createDataset(Seq(Toto(Seq(Tata(&amp;quot;a&amp;quot;, 1))))).toDF()

    import org.apache.spark.sql.functions._

    ss.udf.register(&amp;quot;tataToX&amp;quot;, cleanF(tataToX))

    df.select(expr(&amp;quot;tataToX(tatas)&amp;quot;)).show(false)

  }*/

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8ff737e0-74dd-47fa-929b-f9b325c635d9--&gt;
&lt;!-- begin paragraph 18c247e8-4b80-4fb3-b7d7-8f8c94cd7e62--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-18c247e8-4b80-4fb3-b7d7-8f8c94cd7e62&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 18c247e8-4b80-4fb3-b7d7-8f8c94cd7e62--&gt;
&lt;!-- begin paragraph c553c4bc-de81-46d3-a768-558f2d2e193f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c553c4bc-de81-46d3-a768-558f2d2e193f&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c553c4bc-de81-46d3-a768-558f2d2e193f--&gt;
&lt;!-- begin code aff9e841-bad7-4e50-aaf2-d6b975c3a8c3--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-aff9e841-bad7-4e50-aaf2-d6b975c3a8c3&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;ss.udf.register(&amp;quot;dmConfToX&amp;quot;, cleanF(niveauPack))


def niveauPack(confs: Seq[DmConfiguration]): String = {
    confs.flatMap(_.niveauPack).mkString(&amp;quot;, &amp;quot;)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code aff9e841-bad7-4e50-aaf2-d6b975c3a8c3--&gt;
&lt;!-- begin paragraph 781ad893-d263-4b05-a479-55ce3fdf6848--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-781ad893-d263-4b05-a479-55ce3fdf6848&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 781ad893-d263-4b05-a479-55ce3fdf6848--&gt;
&lt;!-- begin code 1a532014-8113-439f-a150-9ac684dc3a48--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1a532014-8113-439f-a150-9ac684dc3a48&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;trait Typeclass[T] {
  def clean(a: Any): T
}

object Typeclass {
  implicit def seq[T:Typeclass:Manifest]: Typeclass[Seq[T]] = {
    new Typeclass[Seq[T]] {
      private val rc = implicitly[Manifest[T]].runtimeClass
      override def clean(a: Any): Seq[T] =
        a match {
          case Nil =&amp;gt; Nil
          case xs: Seq[_]
              if xs.forall(x =&amp;gt; rc.isAssignableFrom(x.getClass)) =&amp;gt;
            xs.asInstanceOf[Seq[T]]
          case x: Seq[_] =&amp;gt; x.map(implicitly[Typeclass[T]].clean)
        }
    }
  }

  implicit def opt[T: Typeclass: Manifest]: Typeclass[Option[T]] =
    new Typeclass[Option[T]] {
      private val rc = implicitly[Manifest[T]].runtimeClass
      override def clean(a: Any): Option[T] =
        a match {
          case ox: Option[_]
              if ox.forall(x =&amp;gt; rc.isAssignableFrom(x.getClass)) =&amp;gt;
            ox.asInstanceOf[Option[T]]
          case null =&amp;gt; None
          case x    =&amp;gt; Option(implicitly[Typeclass[T]].clean(x))
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1a532014-8113-439f-a150-9ac684dc3a48--&gt;
&lt;!-- begin paragraph 2c415d18-880e-4b01-adfc-bf36fef1992b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c415d18-880e-4b01-adfc-bf36fef1992b&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 2c415d18-880e-4b01-adfc-bf36fef1992b--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:11037e2befc8437d8f4614841e86392c</id>
    <title>PKIX path building failed: unable to find valid certification path to requested target... À tes souhaits !</title>
    <updated>2019-02-20T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/pkix-path-building-failed-unable-to-find-valid-certification-path-to-requested-target-a-tes-souhaits.html"/>
    <!--summary PKIX path building failed: unable to find valid certification path to requested target... Quoi vous avez cette erreur ? On l&#039;a eu aussi et on a réussi à en sortir vainqueur et voici comment.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Sécurité"></category>    <category term="SSL"></category>    <category term="Java"></category>    <category term="Scala"></category>    <category term="TLS"></category>    <category term="X.509"></category>    <category term="Certificat"></category>    <content type="html">
&lt;!-- begin paragraph 138d6dee-ca6e-4a11-b07d-52418d3444bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-138d6dee-ca6e-4a11-b07d-52418d3444bb&quot;&gt;TLS (&lt;i&gt;Transport Layer Security&lt;/i&gt;), plus souvent dénommé SSL (&lt;i&gt;Secured Sockets Layer&lt;/i&gt; - son prédécesseur déprécié), est l&amp;#39;un des protocoles les plus utilisés sur le Web pour sécuriser les sites et les applications qui le peuplent. TLS repose à la fois sur des algorithmes de cryptographie asymétrique (clé publique et clé privée) et une relation de confiance avec des autorités reconnues à travers le monde, en se basant sur un système de certificats sécurisés.&lt;/p&gt;
&lt;!-- end paragraph 138d6dee-ca6e-4a11-b07d-52418d3444bb--&gt;
&lt;!-- begin paragraph 17cac8a8-8d2b-4e9e-8f2b-fb03e54f8469--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-17cac8a8-8d2b-4e9e-8f2b-fb03e54f8469&quot;&gt;Cependant, ce modèle ne fonctionne pas lorsque vous êtes enfermé derrière le proxy d&amp;#39;une entreprise, qui ne souhaite pas (à raison ?) que les applications qu&amp;#39;elle développe se mettent à transmettre (par erreur, dans le meilleur des cas !) des informations confidentielles ou qu&amp;#39;elles soient des portes ouvertes pour la plus grande joie des mauvaises intentions du Net 😱&lt;/p&gt;
&lt;!-- end paragraph 17cac8a8-8d2b-4e9e-8f2b-fb03e54f8469--&gt;
&lt;!-- begin paragraph 71013677-110c-4ba8-a7c4-68ac66136a1f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71013677-110c-4ba8-a7c4-68ac66136a1f&quot;&gt;Néanmoins, à l&amp;#39;intérieur de cette entreprise, comme il est nécessaire de sécuriser la communication sur le réseau, il faut passer par TLS. Mais cette fois, l&amp;#39;entreprise va se déclarer elle-même autorité de certificat, en exposant en interne un certificat dit &amp;quot;auto-signé&amp;quot;. C&amp;#39;est là que les ennuis commencent !&lt;/p&gt;
&lt;!-- end paragraph 71013677-110c-4ba8-a7c4-68ac66136a1f--&gt;
&lt;!-- begin paragraph efdb6004-25c1-4294-b58d-d3947e628929--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-efdb6004-25c1-4294-b58d-d3947e628929&quot;&gt;1/ Vous lancez Maven ou SBT... Et boom ! ça crashe, parce que ça ne passe pas le pare-feu. On vous dit alors de passer par un proxy Maven (Nexus ou Artifactory).&lt;/p&gt;
&lt;!-- end paragraph efdb6004-25c1-4294-b58d-d3947e628929--&gt;
&lt;!-- begin paragraph 617539bf-46fe-4ebd-a751-eea7d81e996b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-617539bf-46fe-4ebd-a751-eea7d81e996b&quot;&gt;2/ Vous configurez vos outils. Vous retentez... Et boom ! Vous avez une exception à peine compréhensible qui parle de certification, de Sun et de PKIX. On vous dit alors qu&amp;#39;il y a un vague JDK, qui a été correctement configuré avec tous les certificats et qui traine quelque part sur le réseau interne.&lt;/p&gt;
&lt;!-- end paragraph 617539bf-46fe-4ebd-a751-eea7d81e996b--&gt;
&lt;!-- begin paragraph 7795899a-1e23-4c88-851b-42179049db80--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7795899a-1e23-4c88-851b-42179049db80&quot;&gt;3/ Vous récupérez et installez ce JDK. Vous retentez... Et boom ! Vous obtenez la même erreur que la dernière fois. À ce moment là, vous commencez à avoir du mal à faire la distinction entre TLS et la magie. Et vous êtes sur le point de friser la folie 🤯&lt;/p&gt;
&lt;!-- end paragraph 7795899a-1e23-4c88-851b-42179049db80--&gt;
&lt;!-- begin heading_1 61969b55-a95b-45f8-95f5-838c57870513--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-61969b55-a95b-45f8-95f5-838c57870513&quot;&gt;Reproduction du cas&lt;/h2&gt;
&lt;!-- end heading_1 61969b55-a95b-45f8-95f5-838c57870513--&gt;
&lt;!-- begin paragraph b35b4d3d-2bab-42bc-8e9f-8f7def01dcd1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b35b4d3d-2bab-42bc-8e9f-8f7def01dcd1&quot;&gt;Commençons par générer un certificat auto-signé que nous allons placer dans un keystore, nommé ici &lt;code&gt;selfsigned.jks&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph b35b4d3d-2bab-42bc-8e9f-8f7def01dcd1--&gt;
&lt;!-- begin code 2f4b5ac5-fbe3-478c-84f6-b2c00862d176--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2f4b5ac5-fbe3-478c-84f6-b2c00862d176&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ keytool -genkey \
    -alias selfsigned -keyalg RSA \
    -keypass changeit -storepass changeit \
    -keystore tmp/certificate/selfsigned.jks

What is your first and last name?
  [Unknown]:  François Sarradin
What is the name of your organizational unit?
  [Unknown]:
What is the name of your organization?
  [Unknown]:  Univalence
What is the name of your City or Locality?
  [Unknown]:  Paris
What is the name of your State or Province?
  [Unknown]:
What is the two-letter country code for this unit?
  [Unknown]:  fr
Is
  CN=François Sarradin, OU=Unknown, O=Univalence, L=Paris, ST=Unknown, C=fr
correct?
  [no]:  yes&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2f4b5ac5-fbe3-478c-84f6-b2c00862d176--&gt;
&lt;!-- begin paragraph 1a1e43f6-7a0a-4412-9752-b14cc5bd370d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1a1e43f6-7a0a-4412-9752-b14cc5bd370d&quot;&gt;Nous allons utiliser ce keystore pour monter un faux service Web sécurisé. Pour cela nous créons d&amp;#39;abord un contexte SSL, à travers une fonction qui prend en paramètres le chemin vers ce keystore ainsi que le mot de passe associé.&lt;/p&gt;
&lt;!-- end paragraph 1a1e43f6-7a0a-4412-9752-b14cc5bd370d--&gt;
&lt;!-- begin code a07f63e7-f2dc-4b1d-bd4e-3011965aeacd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a07f63e7-f2dc-4b1d-bd4e-3011965aeacd&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import java.io.FileInputStream
import java.security.KeyStore
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory}

def createSSLContext(keystorePath: String, password: String): SSLContext = {
    val keyStore = KeyStore.getInstance(&amp;quot;JKS&amp;quot;)
    keyStore.load(new FileInputStream(keystorePath), password.toCharArray)

    val keyManagerFactory = KeyManagerFactory.getInstance(&amp;quot;SunX509&amp;quot;)
    keyManagerFactory.init(keyStore, password.toCharArray)
    val keyManagers = keyManagerFactory.getKeyManagers

    val trustManagerFactory = TrustManagerFactory.getInstance(&amp;quot;SunX509&amp;quot;)
    trustManagerFactory.init(keyStore)
    val trustManagers = trustManagerFactory.getTrustManagers

    val sslContext = SSLContext.getInstance(&amp;quot;TLS&amp;quot;)
    sslContext.init(keyManagers, trustManagers, null)

    sslContext
  }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a07f63e7-f2dc-4b1d-bd4e-3011965aeacd--&gt;
&lt;!-- begin paragraph fcbbc9cb-ed95-4171-8292-0b06f885753a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fcbbc9cb-ed95-4171-8292-0b06f885753a&quot;&gt;Il faut ensuite créer une configuration pour le service Web avec ce contexte.&lt;/p&gt;
&lt;!-- end paragraph fcbbc9cb-ed95-4171-8292-0b06f885753a--&gt;
&lt;!-- begin code c625fc1f-dda2-42dd-b3b8-2a2571331552--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c625fc1f-dda2-42dd-b3b8-2a2571331552&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import com.sun.net.httpserver._

    val httpsConfigurator =
      new HttpsConfigurator(sslContext) { self =&amp;gt;
        override def configure(params: HttpsParameters): Unit = {
          val c = self.getSSLContext
          val engine = c.createSSLEngine
          params.setNeedClientAuth(false)
          params.setCipherSuites(engine.getEnabledCipherSuites)
          params.setProtocols(engine.getEnabledProtocols)
          params.setSSLParameters(c.getDefaultSSLParameters)
        }
      }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c625fc1f-dda2-42dd-b3b8-2a2571331552--&gt;
&lt;!-- begin paragraph 16949707-c65e-45e7-a1ce-e354133fde6d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-16949707-c65e-45e7-a1ce-e354133fde6d&quot;&gt;Et on lance le service.&lt;/p&gt;
&lt;!-- end paragraph 16949707-c65e-45e7-a1ce-e354133fde6d--&gt;
&lt;!-- begin code e7f1fd23-2712-40ac-ac3e-9dc2bed1aac7--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e7f1fd23-2712-40ac-ac3e-9dc2bed1aac7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import com.sun.net.httpserver._
import java.net.InetSocketAddress

    val server = HttpsServer.create(new InetSocketAddress(&amp;quot;0.0.0.0&amp;quot;, 9443), 0)
    server.setHttpsConfigurator(httpsConfigurator)
    server.createContext(&amp;quot;/&amp;quot;, (exchange: HttpExchange) =&amp;gt; {
      val content = &amp;quot;Hello&amp;quot;
      val length = content.length
      val raw = content.getBytes

      exchange.sendResponseHeaders(200, length)
      exchange.getResponseBody.write(raw)

      exchange.close()
    })
    server.start()&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e7f1fd23-2712-40ac-ac3e-9dc2bed1aac7--&gt;
&lt;!-- begin paragraph bfdce1b8-aaa4-403d-a985-cb801c96d6a3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bfdce1b8-aaa4-403d-a985-cb801c96d6a3&quot;&gt;On va maintenant se créer un client Web.&lt;/p&gt;
&lt;!-- end paragraph bfdce1b8-aaa4-403d-a985-cb801c96d6a3--&gt;
&lt;!-- begin code 78fdfd2c-0e6e-46ac-827d-6136fc8c942f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-78fdfd2c-0e6e-46ac-827d-6136fc8c942f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import java.net.URL

object Main {
  def main(args: Array[String]): Unit = {
    val result = new URL(&amp;quot;https://127.0.0.1:9443/&amp;quot;).getContent()

    println(result)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 78fdfd2c-0e6e-46ac-827d-6136fc8c942f--&gt;
&lt;!-- begin paragraph 7101e7a4-39d2-4cea-a7a9-472bc94d6309--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7101e7a4-39d2-4cea-a7a9-472bc94d6309&quot;&gt;En lançant le service Web, puis le client, nous obtenons alors l&amp;#39;exception ci-dessous.&lt;/p&gt;
&lt;!-- end paragraph 7101e7a4-39d2-4cea-a7a9-472bc94d6309--&gt;
&lt;!-- begin code 33ef25ac-56a2-415a-867c-2ff7f617bf4a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-33ef25ac-56a2-415a-867c-2ff7f617bf4a&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Exception in thread &amp;quot;main&amp;quot; javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:128)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1329)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1204)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1151)
	at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
	at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
	at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
	at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
	at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
	at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
	at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1581)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
	at java.base/java.net.URLConnection.getContent(URLConnection.java:749)
	at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getContent(HttpsURLConnectionImpl.java:425)
	at java.base/java.net.URL.getContent(URL.java:1131)
	at io.univalence.test_ssl.Main$.main(Main.scala:7)
	at io.univalence.test_ssl.Main.main(Main.scala)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
	at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:290)
	at java.base/sun.security.validator.Validator.validate(Validator.java:264)
	at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:321)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:221)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1313)
	... 19 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
	at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
	at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
	at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
	... 25 more&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 33ef25ac-56a2-415a-867c-2ff7f617bf4a--&gt;
&lt;!-- begin heading_1 86bdf87b-0106-43b2-a33f-6d411d626228--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-86bdf87b-0106-43b2-a33f-6d411d626228&quot;&gt;Analyse&lt;/h2&gt;
&lt;!-- end heading_1 86bdf87b-0106-43b2-a33f-6d411d626228--&gt;
&lt;!-- begin paragraph 80e9bfbc-6c15-4809-9ce0-5f12788a0f5c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-80e9bfbc-6c15-4809-9ce0-5f12788a0f5c&quot;&gt;Voici le message : &lt;/p&gt;
&lt;!-- end paragraph 80e9bfbc-6c15-4809-9ce0-5f12788a0f5c--&gt;
&lt;!-- begin paragraph a29af9c3-d4a3-4958-9eeb-93b0f48afffe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a29af9c3-d4a3-4958-9eeb-93b0f48afffe&quot;&gt;&lt;code&gt;Exception in thread &amp;quot;main&amp;quot; javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph a29af9c3-d4a3-4958-9eeb-93b0f48afffe--&gt;
&lt;!-- begin paragraph e09fc9f0-a64d-4c48-a04e-e3a1bf61bf1b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e09fc9f0-a64d-4c48-a04e-e3a1bf61bf1b&quot;&gt;L&amp;#39;exception est de type &lt;code&gt;javax.net.ssl.SSLHandshakeException&lt;/code&gt;. En effet, elle intervient lors de la période de négociation avec le service Web. C&amp;#39;est à ce moment que sont convenus le protocole utilisé, sa version, l&amp;#39;algorithme de cryptographie utilisé... C&amp;#39;est aussi à ce moment que le service transmet son certificat, permettant ainsi au client d&amp;#39;effectuer les vérifications qui lui semblent nécessaires.&lt;/p&gt;
&lt;!-- end paragraph e09fc9f0-a64d-4c48-a04e-e3a1bf61bf1b--&gt;
&lt;!-- begin paragraph ac4cb542-28ae-49f0-821c-9cb8c3e918fd--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ac4cb542-28ae-49f0-821c-9cb8c3e918fd&quot;&gt;Les certificats utilisés ici sont des certificats à clé publique basé sur la norme &lt;a href=&quot;https://en.wikipedia.org/wiki/X.509&quot;&gt;X.509&lt;/a&gt;. Cette norme repose sur un algorithme permettant de vérifier qu&amp;#39;un certificat est lié à une autorité de certification et &amp;quot;signé&amp;quot; par celle-ci en utilisant sa clé privée. Or, dans notre cas, le certificat envoyé par notre service Web ne repose sur aucune autorité de certification. De plus, le site du service n&amp;#39;est pas reconnu comme autorité de certification par le client Web.&lt;/p&gt;
&lt;!-- end paragraph ac4cb542-28ae-49f0-821c-9cb8c3e918fd--&gt;
&lt;!-- begin paragraph c6545506-d3f1-4121-a8d9-a5cf255e6719--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c6545506-d3f1-4121-a8d9-a5cf255e6719&quot;&gt;Les certificats reconnus sont stockés dans un keystore. Dès qu&amp;#39;une connexion TLS est mise en place, à travers la classe &lt;code&gt;sun.security.ssl.TrustStoreManager.TrustStoreDescriptor&lt;/code&gt; (en Java 11), Java va d&amp;#39;abord rechercher le fichier keystore à travers la propriété &lt;code&gt;javax.net.ssl.trustStore&lt;/code&gt;, si elle a été définie. Sinon, Java va vérifier que le fichier &lt;code&gt;$JAVA_HOME/lib/security/jssecacerts&lt;/code&gt; existe et de même avec le fichier &lt;code&gt;$JAVA_HOME/lib/security/cacerts&lt;/code&gt;. Normalement, ce dernier existe et contient les certificats des autorités reconnues. &lt;code&gt;jssecacerts&lt;/code&gt; sert pour des besoins en développement et permet de ne pas  &amp;quot;polluer&amp;quot; &lt;code&gt;cacerts&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph c6545506-d3f1-4121-a8d9-a5cf255e6719--&gt;
&lt;!-- begin paragraph cdc87aab-4148-4eb2-92e2-f9aa08f59c9f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cdc87aab-4148-4eb2-92e2-f9aa08f59c9f&quot;&gt;Ainsi, pour faire fonctionner notre connexion TLS avec notre service Web, nous allons devoir récupérer le certificat de ce service et l&amp;#39;ajouter à l&amp;#39;un des keystores principaux.&lt;/p&gt;
&lt;!-- end paragraph cdc87aab-4148-4eb2-92e2-f9aa08f59c9f--&gt;
&lt;!-- begin heading_1 7f175dc3-3477-4cfe-a851-f3cc48e1c87c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-7f175dc3-3477-4cfe-a851-f3cc48e1c87c&quot;&gt;Résolution&lt;/h2&gt;
&lt;!-- end heading_1 7f175dc3-3477-4cfe-a851-f3cc48e1c87c--&gt;
&lt;!-- begin paragraph c4728952-4862-4373-90d9-353b105315f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c4728952-4862-4373-90d9-353b105315f2&quot;&gt;Si vous avez OpenSSL de disponible, la commande suivante permet de récupérer le certificat associé à notre service Web (vous pouvez d&amp;#39;ailleurs tenter la commande avec &lt;code&gt;google.com:443&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph c4728952-4862-4373-90d9-353b105315f2--&gt;
&lt;!-- begin code 51931606-44e0-4f41-a71e-d7dec5a9ff11--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-51931606-44e0-4f41-a71e-d7dec5a9ff11&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;echo -n \
    | openssl s_client -connect 127.0.0.1:9443 \
    | openssl x509 -outform PEM \
    &amp;gt; selfsigned_certificate.pem&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 51931606-44e0-4f41-a71e-d7dec5a9ff11--&gt;
&lt;!-- begin paragraph fc631621-76d9-4b7d-b6d2-185c10633650--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fc631621-76d9-4b7d-b6d2-185c10633650&quot;&gt;Sinon, passez par votre navigateur. Il faut vous rendre sur le site du service et afficher les informations de la page visitée. Si votre navigateur vous le permet, vous pourrez télécharger/exporter le certificat associé.&lt;/p&gt;
&lt;!-- end paragraph fc631621-76d9-4b7d-b6d2-185c10633650--&gt;
&lt;!-- begin paragraph aa4d676b-b298-48a8-8a52-ad2dfffab736--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aa4d676b-b298-48a8-8a52-ad2dfffab736&quot;&gt;Importez alors le certificat dans le keystore Java.&lt;/p&gt;
&lt;!-- end paragraph aa4d676b-b298-48a8-8a52-ad2dfffab736--&gt;
&lt;!-- begin code faeae7c8-acc4-414a-9e14-7077dc00a462--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-faeae7c8-acc4-414a-9e14-7077dc00a462&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$JAVA_HOME/bin/keytool -import \
    -alias selfsigned -file selfsigned_certificate.pem \
    -keystore $JAVA_HOME/lib/security/jssecacerts&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code faeae7c8-acc4-414a-9e14-7077dc00a462--&gt;
&lt;!-- begin paragraph ff8bd409-72f5-41a2-8335-ddce54776910--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff8bd409-72f5-41a2-8335-ddce54776910&quot;&gt;Vérifiez que l&amp;#39;import s&amp;#39;est bien passé.&lt;/p&gt;
&lt;!-- end paragraph ff8bd409-72f5-41a2-8335-ddce54776910--&gt;
&lt;!-- begin code 3a23959c-e011-4507-9730-599f7fef5718--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3a23959c-e011-4507-9730-599f7fef5718&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$JAVA_HOME/bin/keytool -list -v \
    -alias selfsigned \
    -keystore $JAVA_HOME/lib/security/jssecacerts&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3a23959c-e011-4507-9730-599f7fef5718--&gt;
&lt;!-- begin paragraph 41d91259-efcb-4952-bc1f-2b4baa4b3a8a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-41d91259-efcb-4952-bc1f-2b4baa4b3a8a&quot;&gt;Le certificat du service Web devrait s&amp;#39;afficher.&lt;/p&gt;
&lt;!-- end paragraph 41d91259-efcb-4952-bc1f-2b4baa4b3a8a--&gt;
&lt;!-- begin paragraph 1030501e-d81e-4db4-99e3-35ee1b5ceb30--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1030501e-d81e-4db4-99e3-35ee1b5ceb30&quot;&gt;Pour le JDK8, il vous faudra utiliser &lt;code&gt;$JAVA_HOME/jre/lib/security&lt;/code&gt; (attention au &lt;code&gt;jre&lt;/code&gt;) comme chemin vers les fichiers keystore gérés par Java. Il existe aussi une procédure dédiée à IntelliJ IDEA &lt;a href=&quot;https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000094584-IDEA-Ultimate-2016-3-4-throwing-unable-to-find-valid-certification-path-to-requested-target-when-trying-to-refresh-gradle?page=1#community_comment_115000405564&quot;&gt;décrite sur le site support&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 1030501e-d81e-4db4-99e3-35ee1b5ceb30--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:5d9412ef4d154ac3ac430e52ba27ad15</id>
    <title>cause-toujours : who can it be now?</title>
    <updated>2019-02-11T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/cause-toujours-who-can-it-be-now.html"/>
    <!--summary Savoir d&#039;où provient un appel dans le code est une nécessité. C&#039;est de ce concept de base qu&#039;est née cause-toujours, pour en savoir plus, ça se passe ici. 😉-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="cause-toujours"></category>    <category term="Callsite"></category>    <category term="OSS"></category>    <category term="Scala"></category>    <category term="Macro"></category>    <category term="Zoom"></category>    <content type="html">
&lt;!-- begin paragraph 4a3c9a0f-4775-45a0-9369-48a8a3b6d60d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a3c9a0f-4775-45a0-9369-48a8a3b6d60d&quot;&gt;Savez-vous ce que vous avez en production ? Lorsque votre application transmet un log, savez-vous depuis quel fichier source provient-il et surtout à quel commit est-il rattaché ?... Si toutefois, vous avez bien pensé à intégrer sous Git les dernières modifications...&lt;/p&gt;
&lt;!-- end paragraph 4a3c9a0f-4775-45a0-9369-48a8a3b6d60d--&gt;
&lt;!-- begin paragraph 2ce344a7-32b2-4fbd-b4bf-d67106bb1c47--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2ce344a7-32b2-4fbd-b4bf-d67106bb1c47&quot;&gt;Savoir d&amp;#39;où provient un appel dans le code est une nécessité. Savoir à quel commit est rattaché la ligne de code qui a déclenché l&amp;#39;appel est aussi une nécessité. D&amp;#39;une manière générale, plus il y aura d&amp;#39;informations fournies sur le contexte de production d&amp;#39;une ligne de code, plus il sera aisé de comprendre l&amp;#39;intention autour du déclenchement d&amp;#39;un appel et accessoirement de déboguer une application.&lt;/p&gt;
&lt;!-- end paragraph 2ce344a7-32b2-4fbd-b4bf-d67106bb1c47--&gt;
&lt;!-- begin paragraph 6a279cb9-fb1f-45fb-a024-cc2e54683ec9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a279cb9-fb1f-45fb-a024-cc2e54683ec9&quot;&gt;Nous sommes en 2019... Malgré ça, nous allons continuer à voir des &amp;quot;patchs&amp;quot; fait main et non versionnés, passer hors des contrôles des serveurs d&amp;#39;intégration continu, pour aller se loger bien au chaud dans vos serveurs de production ou ceux de vos clients 🐛.&lt;/p&gt;
&lt;!-- end paragraph 6a279cb9-fb1f-45fb-a024-cc2e54683ec9--&gt;
&lt;!-- begin paragraph 84adfcd5-da45-459a-82bb-2557d2214cd3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-84adfcd5-da45-459a-82bb-2557d2214cd3&quot;&gt;L&amp;#39;idée derrière notre projet &lt;a href=&quot;https://github.com/UNIVALENCE/cause-toujours&quot;&gt;cause-toujours&lt;/a&gt; est de fournir ces informations autour du lieu d&amp;#39;appel (&lt;i&gt;call site&lt;/i&gt; en anglais) et de fournir ce contexte manquant lorsqu&amp;#39;un soucis est remonté dans vos équipes.&lt;/p&gt;
&lt;!-- end paragraph 84adfcd5-da45-459a-82bb-2557d2214cd3--&gt;
&lt;!-- begin paragraph fea27145-3dd2-4430-9adf-cf35a856c992--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fea27145-3dd2-4430-9adf-cf35a856c992&quot;&gt;cause-toujours se base d&amp;#39;abord sur une structure de donnée permettant de conserver un ensemble de méta-données sur le site d&amp;#39;appel. Cette structure est nommée &lt;code&gt;CallSiteInfo&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph fea27145-3dd2-4430-9adf-cf35a856c992--&gt;
&lt;!-- begin code 5bb55d13-191f-46a4-ab99-3ba92403cae1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5bb55d13-191f-46a4-ab99-3ba92403cae1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class CallSiteInfo(
    enclosingClass: String, // name of the enclosing unit
    file: String, // file for the callsite (relative to the git repo root)
    line: Int, // line number in file
    commit: String, // id of the commit
    buildAt: Long, // time you build at
    status: String, // one of &amp;quot;clean&amp;quot;, &amp;quot;modified&amp;quot;, &amp;quot;untracked&amp;quot;
    fingerprint: String, // file content hash
    fileContent: Option[String] = None // if the build file is different
)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5bb55d13-191f-46a4-ab99-3ba92403cae1--&gt;
&lt;!-- begin paragraph c868f1ef-1329-45e7-8340-b50b8d550291--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c868f1ef-1329-45e7-8340-b50b8d550291&quot;&gt;Des types primitifs sont utilisés ici afin d&amp;#39;assurer la sérialisation de la structure et de faciliter son interprétation par des applications basées sur d&amp;#39;autres langages.&lt;/p&gt;
&lt;!-- end paragraph c868f1ef-1329-45e7-8340-b50b8d550291--&gt;
&lt;!-- begin paragraph 5a28828b-a891-4cb9-b771-d60ce96cfce5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a28828b-a891-4cb9-b771-d60ce96cfce5&quot;&gt;La récupération de ces informations se fait au moment de la compilation du code source. Scala propose en effet un système de macro syntaxique pour ça. Une macro Scala est un morceau de code qui intervient au moment de la compilation, lorsque l&amp;#39;arbre syntaxique (AST - &lt;i&gt;Abstract Syntax Tree&lt;/i&gt;) a été formé. Cette macro va permettre d&amp;#39;effectuer des transformations dans l&amp;#39;AST. C&amp;#39;est un peu comme de la génération de code, mais dans laquelle la syntaxe du langage est forcément respectée.&lt;/p&gt;
&lt;!-- end paragraph 5a28828b-a891-4cb9-b771-d60ce96cfce5--&gt;
&lt;!-- begin paragraph 7be76496-7278-423f-9840-7e1daaca2586--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7be76496-7278-423f-9840-7e1daaca2586&quot;&gt;La déclaration d&amp;#39;une macro se fait en utilisant le mot-clé correspondant.&lt;/p&gt;
&lt;!-- end paragraph 7be76496-7278-423f-9840-7e1daaca2586--&gt;
&lt;!-- begin code b8eb6865-b9f9-43b7-969c-cbd50da96f29--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b8eb6865-b9f9-43b7-969c-cbd50da96f29&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import language.experimental.macros

implicit def callSiteInfo: CallSiteInfo = macro CallSiteMacro.callSiteImpl&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b8eb6865-b9f9-43b7-969c-cbd50da96f29--&gt;
&lt;!-- begin paragraph 4176a079-67e0-4839-8e4a-7c19e954e694--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4176a079-67e0-4839-8e4a-7c19e954e694&quot;&gt;Ainsi, à chaque fois que &lt;code&gt;callSiteInfo&lt;/code&gt; apparaît dans le code, le compilateur donne la main à la fonction &lt;code&gt;CallSiteMacro.callSiteImpl&lt;/code&gt;. Les macros dans Scala restent une fonctionnalité expérimentale. Comme expliqué lors d&amp;#39;un &lt;a href=&quot;https://blog.univalence.io/scalaio-2018-le-retour-de-lequipe-univalence/#towards-scala-3-a-status-report-par-martin-odersky&quot;&gt;précédent article&lt;/a&gt;, dont le sort devrait être fixé avec Scala 3, qui devrait sortir en 2020.&lt;/p&gt;
&lt;!-- end paragraph 4176a079-67e0-4839-8e4a-7c19e954e694--&gt;
&lt;!-- begin code 1875a58c-e6cf-49f5-ab7d-11892bd2bd51--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1875a58c-e6cf-49f5-ab7d-11892bd2bd51&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import scala.reflect.macros.blackbox

def callSiteImpl(c: blackbox.Context): c.Expr[CallSiteInfo] = {
  import c._
  import universe._

  // ...

  c.Expr[CallSiteInfo](
    q&amp;quot;&amp;quot;&amp;quot;callsite.CallSiteInfo(
  enclosingClass = ${owner.fullName},
  file           = ${pathToRepoRoot(sourceFile)},
  ...
  )&amp;quot;&amp;quot;&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1875a58c-e6cf-49f5-ab7d-11892bd2bd51--&gt;
&lt;!-- begin paragraph 67d4b74c-63d0-4f33-949a-77088c6af57d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67d4b74c-63d0-4f33-949a-77088c6af57d&quot;&gt;Le paramètre de type &lt;code&gt;blackbox.Context&lt;/code&gt; correspond au contexte au niveau de l&amp;#39;AST dans lequel est appelé la macro. &lt;i&gt;Blackbox&lt;/i&gt; signifie ici que la macro agira dans un contexte dans lequel toute résolution de type est complète. La macro retourne une expression de type &lt;code&gt;CallSiteInfo&lt;/code&gt;, qui est formalisée par les dernières lignes de la macro en utilisant une quasiquote. Une quasiquote se présente comme une chaîne de caractères contenant du code Scala. Ce code est ensuite converti en AST et intégré dans l&amp;#39;AST de votre application. C&amp;#39;est ainsi qu&amp;#39;il est possible de récupérer la date de compilation du code. À partir de l&amp;#39;API &lt;code&gt;scala.reflect&lt;/code&gt;, il est possible d&amp;#39;obtenir le fichier, le numéro de ligne ou le code source dans lequel se fait le site d&amp;#39;appel. Et en utilisant JGit (&lt;code&gt;org.eclipse.jgit&lt;/code&gt;), il est aussi possible de récupérer le &lt;i&gt;commit ID&lt;/i&gt; ou d&amp;#39;indiquer si le fichier est dans une version conservé sous Git.&lt;/p&gt;
&lt;!-- end paragraph 67d4b74c-63d0-4f33-949a-77088c6af57d--&gt;
&lt;!-- begin paragraph 997052ae-0a14-4046-a8ad-53b0d79baf74--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-997052ae-0a14-4046-a8ad-53b0d79baf74&quot;&gt;On peut ainsi imaginer écrire une fonction de &lt;code&gt;log&lt;/code&gt; de la manière suivante :&lt;/p&gt;
&lt;!-- end paragraph 997052ae-0a14-4046-a8ad-53b0d79baf74--&gt;
&lt;!-- begin code 19acbeea-e99d-43e6-a98b-ca17a3743b2b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-19acbeea-e99d-43e6-a98b-ca17a3743b2b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def log(message: String)(implicit cs: callsite.CallSiteInfo): Unit = {
  println(s&amp;quot;[${cs.commit.take(7)} - ${cs.file}:${cs.line} @${cs.date}] $message&amp;quot;)
  cs.fileContent.foreach(println)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 19acbeea-e99d-43e6-a98b-ca17a3743b2b--&gt;
&lt;!-- begin paragraph 748c3b38-4962-4415-b239-4480b7941378--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-748c3b38-4962-4415-b239-4480b7941378&quot;&gt;Il n&amp;#39;y a pas besoin d&amp;#39;importer &lt;code&gt;callsite.CallSiteInfo&lt;/code&gt;. Et comme le paramètre &lt;code&gt;cs&lt;/code&gt; est implicite, il n&amp;#39;y a pas besoin de le renseigner lors de l&amp;#39;appel à &lt;code&gt;log&lt;/code&gt;. Voici un exemple d&amp;#39;utilisation :&lt;/p&gt;
&lt;!-- end paragraph 748c3b38-4962-4415-b239-4480b7941378--&gt;
&lt;!-- begin code 46b04ed0-7d15-4401-bdf5-287da3571862--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-46b04ed0-7d15-4401-bdf5-287da3571862&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import /*...*/.log

object MyMain {
  def main(args: Array[String]): Unit = {
    println(&amp;quot;Hello printed&amp;quot;)
    log(&amp;quot;Hello logged&amp;quot;)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 46b04ed0-7d15-4401-bdf5-287da3571862--&gt;
&lt;!-- begin paragraph b54c2d67-eaea-4a10-9307-53dc749bff3d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b54c2d67-eaea-4a10-9307-53dc749bff3d&quot;&gt;Si ce code est stocké dans un fichier appelé &lt;code&gt;MyMain.scala&lt;/code&gt; et que ce fichier n&amp;#39;a pas été modifié depuis le dernier commit Git, on aura alors l&amp;#39;affichage suivant :&lt;/p&gt;
&lt;!-- end paragraph b54c2d67-eaea-4a10-9307-53dc749bff3d--&gt;
&lt;!-- begin code 6ec5677a-11c0-4c43-b81e-1189f002cadf--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6ec5677a-11c0-4c43-b81e-1189f002cadf&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Hello printed
[b834c88 - MyMain.scala:6 @946684800] Hello logged&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6ec5677a-11c0-4c43-b81e-1189f002cadf--&gt;
&lt;!-- begin paragraph d988ec3b-c403-496d-8d6b-6f9fa461448c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d988ec3b-c403-496d-8d6b-6f9fa461448c&quot;&gt;cause-toujours est utilisé dans le cadre de notre projet &lt;a href=&quot;https://github.com/UNIVALENCE/Zoom&quot;&gt;Zoom&lt;/a&gt;. Zoom permet de centraliser, de réorganiser et de redistribuer des flux de données dans un SI. Ce qui inclut aussi les flux de logs.&lt;/p&gt;
&lt;!-- end paragraph d988ec3b-c403-496d-8d6b-6f9fa461448c--&gt;
&lt;!-- begin paragraph 2c21064e-6644-4f0b-b839-794f8b223643--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c21064e-6644-4f0b-b839-794f8b223643&quot;&gt;cause-toujours est un outil dédié à du code Scala. Une implémentation dans d&amp;#39;autres langages est possible dès lors que ce langage propose un système de macro ou d&amp;#39;injection de code à la compilation et que celui-ci permet de récupérer divers informations au &lt;i&gt;compile time&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 2c21064e-6644-4f0b-b839-794f8b223643--&gt;
&lt;!-- begin paragraph 7c2d4b91-063b-45c7-953d-a135d63f8184--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7c2d4b91-063b-45c7-953d-a135d63f8184&quot;&gt;cause-toujours est open source. Il est disponible sur GitHub et sur Maven Central.&lt;/p&gt;
&lt;!-- end paragraph 7c2d4b91-063b-45c7-953d-a135d63f8184--&gt;
&lt;!-- begin paragraph 69c84fe5-e769-4e99-8021-2da69f730761--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-69c84fe5-e769-4e99-8021-2da69f730761&quot;&gt;GitHub : &lt;a href=&quot;https://github.com/UNIVALENCE/cause-toujours&quot;&gt;https://github.com/UNIVALENCE/cause-toujours&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 69c84fe5-e769-4e99-8021-2da69f730761--&gt;
&lt;!-- begin paragraph 79f9481c-4aac-41ee-8fd1-bd803525d2a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79f9481c-4aac-41ee-8fd1-bd803525d2a0&quot;&gt;Maven : &lt;a href=&quot;https://mvnrepository.com/artifact/io.univalence/cause-toujours&quot;&gt;https://mvnrepository.com/artifact/io.univalence/cause-toujours&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 79f9481c-4aac-41ee-8fd1-bd803525d2a0--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:8126c05eda044681a01b08b07b438c23</id>
    <title>Lancer un job Spark dans un cluster Kubernetes en local</title>
    <updated>2019-02-06T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/lancer-un-job-spark-dans-un-cluster-kubernetes-en-local.html"/>
    <!--summary -->    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="Spark"></category>    <category term="Kubernetes"></category>    <content type="html">
&lt;!-- begin paragraph 5c0f3c95-7d23-4bda-91bd-0d6370e76872--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5c0f3c95-7d23-4bda-91bd-0d6370e76872&quot;&gt;Nous en avons &lt;a href=&quot;https://blog.univalence.io/spark-2-4-tour-dhorizon-de-la-nouvelle-version/#spark-sur-kubernetes&quot;&gt;déjà parlé&lt;/a&gt;, dans les dernières versions de Spark, Kubernetes peut être utilisé comme un orchestrateur à la place de Yarn ou de Mesos. Kubernetes utilise les images docker, ce qui permet de livrer des conteneurs Docker à la place du traditionnel jar ou paquet natif contenant le job Spark. Aussi le déploiement sur Kubernetes permet d&amp;#39;utiliser les ressources dédiées à Kubernetes, ce qui maximise l&amp;#39;utilisation de ces ressources en limitant la fragmentation entre plusieurs clusters, avec un cluster pour le Big Data, un autre pour le &lt;i&gt;deep learning&lt;/i&gt;, ou un autre pour servir les API.&lt;/p&gt;
&lt;!-- end paragraph 5c0f3c95-7d23-4bda-91bd-0d6370e76872--&gt;
&lt;!-- begin paragraph 3927c845-c5f8-47b5-9e8e-bbefd4f6fd90--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3927c845-c5f8-47b5-9e8e-bbefd4f6fd90&quot;&gt;Faire tourner un job Spark sur Kubernetes est aussi un bon moyen d&amp;#39;en apprendre plus sur le fonctionnement de Kubernetes en tant qu&amp;#39;orchestrateur de conteneurs.&lt;/p&gt;
&lt;!-- end paragraph 3927c845-c5f8-47b5-9e8e-bbefd4f6fd90--&gt;
&lt;!-- begin paragraph 304bb9ff-4d74-4dc2-829b-cf8b4147cee7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-304bb9ff-4d74-4dc2-829b-cf8b4147cee7&quot;&gt;La documentation sur le site de Spark introduit en détails le sujet. Je vous propose d&amp;#39;ajouter ici des éléments en complémentaire.&lt;/p&gt;
&lt;!-- end paragraph 304bb9ff-4d74-4dc2-829b-cf8b4147cee7--&gt;
&lt;!-- begin paragraph 0251a731-8ad4-4a53-85b8-6c983f4aa6f0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0251a731-8ad4-4a53-85b8-6c983f4aa6f0&quot;&gt;&lt;del&gt;Je vous propose de suivre ici &lt;/del&gt;&lt;a href=&quot;https://spark.apache.org/docs/latest/running-on-kubernetes.html&quot;&gt;&lt;del&gt;le tutoriel&lt;/del&gt;&lt;/a&gt;&lt;del&gt; proposé sur le site de Spark et d&amp;#39;ajouter des commentaires pertinents au guide déjà existant.&lt;/del&gt;&lt;/p&gt;
&lt;!-- end paragraph 0251a731-8ad4-4a53-85b8-6c983f4aa6f0--&gt;
&lt;!-- begin heading_1 3a46eb55-ff94-4045-a356-c8051b9ef3da--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-3a46eb55-ff94-4045-a356-c8051b9ef3da&quot;&gt;En pratique&lt;/h2&gt;
&lt;!-- end heading_1 3a46eb55-ff94-4045-a356-c8051b9ef3da--&gt;
&lt;!-- begin paragraph b440a661-eec7-4819-8e3a-3cc753e5a80e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b440a661-eec7-4819-8e3a-3cc753e5a80e&quot;&gt;Toutes les manipulations ont été réalisées sous Ubuntu 18.04.&lt;/p&gt;
&lt;!-- end paragraph b440a661-eec7-4819-8e3a-3cc753e5a80e--&gt;
&lt;!-- begin paragraph 57a66e83-a584-4cbb-a996-f2d6548152b2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-57a66e83-a584-4cbb-a996-f2d6548152b2&quot;&gt;En se basant sur la documentation fournie par Spark, ainsi que le Dockerfile fourni depuis Spark 2.3, nous allons voir comment lancer un job Spark à l&amp;#39;aide de Kubernetes (et de Docker).&lt;/p&gt;
&lt;!-- end paragraph 57a66e83-a584-4cbb-a996-f2d6548152b2--&gt;
&lt;!-- begin paragraph 7783538a-783a-4154-8c7d-08975b642288--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7783538a-783a-4154-8c7d-08975b642288&quot;&gt;Nous aurons ainsi besoin de :&lt;/p&gt;
&lt;!-- end paragraph 7783538a-783a-4154-8c7d-08975b642288--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Spark &lt;a href=&quot;https://spark.apache.org/downloads.html&quot;&gt;https://spark.apache.org/downloads.html&lt;/a&gt; (pris en version 2.4 ici).&lt;/li&gt;&lt;li&gt;Le code source de Spark &lt;a href=&quot;https://github.com/apache/spark/releases&quot;&gt;https://github.com/apache/spark/releases&lt;/a&gt; (toujours en version 2.4.0).&lt;/li&gt;&lt;li&gt;Docker CE (Community Edition) &lt;a href=&quot;https://docs.docker.com/install/linux/docker-ce/ubuntu/&quot;&gt;https://docs.d ocker.com/install/linux/docker-ce/ubuntu/&lt;/a&gt; (testé avec docker version 18.09.1).&lt;/li&gt;&lt;li&gt;Java 8 à minima (testé avec le JDK d&amp;#39;Oracle &lt;a href=&quot;https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&quot;&gt;https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;kubectl &lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/install-kubectl/&quot;&gt;https://kubernetes.io/docs/tasks/tools/install-kubectl/&lt;/a&gt;,&lt;/li&gt;&lt;li&gt;minikube &lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/install-minikube/&quot;&gt;https://kubernetes.io/docs/tasks/tools/install-minikube/&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;VirtualBox 6.0 &lt;a href=&quot;https://www.virtualbox.org/wiki/Linux_Downloads&quot;&gt;https://www.virtualbox.org/wiki/Linux_Downloads&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 000c6fe6-0ed9-4524-a21b-4157e42cf68d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-000c6fe6-0ed9-4524-a21b-4157e42cf68d&quot;&gt;Après avoir installé tout ce qu&amp;#39;il vous faut, il faut ensuite builder Spark avec Maven en lançant cette commande depuis le dossier contenant le code source de Spark :&lt;/p&gt;
&lt;!-- end paragraph 000c6fe6-0ed9-4524-a21b-4157e42cf68d--&gt;
&lt;!-- begin code 0a53eebe-6a67-4e8d-a52e-45c2090b20a2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0a53eebe-6a67-4e8d-a52e-45c2090b20a2&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./build/mvn -Pkubernetes -DskipTests clean package&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0a53eebe-6a67-4e8d-a52e-45c2090b20a2--&gt;
&lt;!-- begin paragraph dabaea2a-c35d-4fd0-b839-21c720081121--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dabaea2a-c35d-4fd0-b839-21c720081121&quot;&gt;Il faut aussi construire l&amp;#39;image Docker depuis Spark avec la commande suivante :&lt;/p&gt;
&lt;!-- end paragraph dabaea2a-c35d-4fd0-b839-21c720081121--&gt;
&lt;!-- begin code 6fd5d6f5-cce9-46bb-9f58-da0b7438d232--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6fd5d6f5-cce9-46bb-9f58-da0b7438d232&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;docker build -t spark:latest -f kubernetes/dockerfiles/spark/Dockerfile .&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6fd5d6f5-cce9-46bb-9f58-da0b7438d232--&gt;
&lt;!-- begin paragraph c7313967-8db8-4ed4-96bd-8ab419abbdb5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c7313967-8db8-4ed4-96bd-8ab419abbdb5&quot;&gt;Vous pouvez maintenant lancer un cluster Kubernetes local grâce à minikube :&lt;/p&gt;
&lt;!-- end paragraph c7313967-8db8-4ed4-96bd-8ab419abbdb5--&gt;
&lt;!-- begin code 4bf1ca6c-10dc-4764-a7a5-c2d800752372--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4bf1ca6c-10dc-4764-a7a5-c2d800752372&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;minikube start --cpus 3 --memory 4096&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4bf1ca6c-10dc-4764-a7a5-c2d800752372--&gt;
&lt;!-- begin paragraph 04fa5e1a-4a3c-43f6-bce0-6beb15d02214--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-04fa5e1a-4a3c-43f6-bce0-6beb15d02214&quot;&gt;Il s&amp;#39;agit maintenant de construire l&amp;#39;image Docker de Spark dans le contexte de Kubernetes. En effet, pour pouvoir lancer des images Docker depuis votre cluster Kubernetes local, il est nécessaire de faire ces manipulations suivantes (placez vous à la racine du dossier contenant la distribution de Spark) :&lt;/p&gt;
&lt;!-- end paragraph 04fa5e1a-4a3c-43f6-bce0-6beb15d02214--&gt;
&lt;!-- begin code faa6faa4-540a-49b8-9968-c37d18a2875d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-faa6faa4-540a-49b8-9968-c37d18a2875d&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;eval $(minikube docker-env)
docker build -t spark:latest -f kubernetes/dockerfiles/spark/Dockerfile .&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code faa6faa4-540a-49b8-9968-c37d18a2875d--&gt;
&lt;!-- begin paragraph 969c4a0c-de5d-4d49-982c-5bf54386163b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-969c4a0c-de5d-4d49-982c-5bf54386163b&quot;&gt;Récupérez ensuite l&amp;#39;adresse et le port de votre cluster avec : &lt;/p&gt;
&lt;!-- end paragraph 969c4a0c-de5d-4d49-982c-5bf54386163b--&gt;
&lt;!-- begin code b1e3a518-7aa7-46cb-a0c9-cc57c9ddb8b2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b1e3a518-7aa7-46cb-a0c9-cc57c9ddb8b2&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;kubectl cluster-info&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b1e3a518-7aa7-46cb-a0c9-cc57c9ddb8b2--&gt;
&lt;!-- begin paragraph d7d9b09f-2dae-443c-b18d-6f132126700b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d7d9b09f-2dae-443c-b18d-6f132126700b&quot;&gt;Vous pourrez ensuite lancer un job Spark en mode cluster (en remplaçant &lt;i&gt;hostname &lt;/i&gt;par l&amp;#39;adresse IP du cluster et &lt;i&gt;port &lt;/i&gt;par le port du cluster) en lançant cette commande depuis la racine du dossier contenant Spark (et non pas le dossier contenant le code source de Spark) :&lt;/p&gt;
&lt;!-- end paragraph d7d9b09f-2dae-443c-b18d-6f132126700b--&gt;
&lt;!-- begin code b6908627-2623-4566-8e53-bc083f71c1be--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b6908627-2623-4566-8e53-bc083f71c1be&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/spark-submit \
    --master k8s://https://&amp;lt;hostname&amp;gt;:&amp;lt;port&amp;gt; \
    --deploy-mode cluster \
    --name spark-pi \
    --class org.apache.spark.examples.SparkPi \
    --conf spark.executor.instances=5 \
    --conf spark.kubernetes.container.image=spark:latest \
    local:///path/to/examples.jar&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b6908627-2623-4566-8e53-bc083f71c1be--&gt;
&lt;!-- begin heading_2 d1e160e0-51dc-4c12-a3ad-67fbd3fa2c22--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-d1e160e0-51dc-4c12-a3ad-67fbd3fa2c22&quot;&gt;Monitoring &amp;amp; logs&lt;/h3&gt;
&lt;!-- end heading_2 d1e160e0-51dc-4c12-a3ad-67fbd3fa2c22--&gt;
&lt;!-- begin paragraph 91c8fea9-7d61-4553-8a1f-e3cc243630f8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-91c8fea9-7d61-4553-8a1f-e3cc243630f8&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 91c8fea9-7d61-4553-8a1f-e3cc243630f8--&gt;
&lt;!-- begin paragraph 307edf71-6188-4591-9b98-7b198cb72172--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-307edf71-6188-4591-9b98-7b198cb72172&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 307edf71-6188-4591-9b98-7b198cb72172--&gt;
&lt;!-- begin heading_1 bc5fce2c-d33f-4968-bff4-ca5c43087d15--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-bc5fce2c-d33f-4968-bff4-ca5c43087d15&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 bc5fce2c-d33f-4968-bff4-ca5c43087d15--&gt;
&lt;!-- begin paragraph eb8da44c-6367-461a-a8d3-7f1094e1b0d5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-eb8da44c-6367-461a-a8d3-7f1094e1b0d5&quot;&gt;Le guide proposé sur le site de Spark est assez bien expliqué. Mais il passe assez vite sur certains points et ne mentionne pas par exemple le fait qu&amp;#39;il faille aussi builder Spark à partir du code source ou bien qu&amp;#39;il faille créer l&amp;#39;image Docker dans le contexte de Kubernetes.&lt;/p&gt;
&lt;!-- end paragraph eb8da44c-6367-461a-a8d3-7f1094e1b0d5--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:1f19206ad9b544788f13f2e8083760f0</id>
    <title>Fonctions d&#039;ordre supérieur dans Spark 2 pour traiter des structures imbriquées</title>
    <updated>2019-02-04T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/fonctions-d-ordre-superieur-dans-spark-2-pour-traiter-des-structures-imbriquees.html"/>
    <!--summary Spark 2.4 n&#039;est pas à la porter de tous projets alors pour ceux qui veulent utiliser des fonctions d&#039;ordre supérieur avec Spark &lt;2.4 alors cet article est fait pour vous.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Spark"></category>    <category term="Scala"></category>    <category term="Programmation fonctionnelle"></category>    <content type="html">
&lt;!-- begin paragraph 4a02d633-a0ef-49e9-82d3-0291b7288e21--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4a02d633-a0ef-49e9-82d3-0291b7288e21&quot;&gt;Nous l&amp;#39;avons annoncé dans un &lt;a href=&quot;https://blog.univalence.io/spark-2-4-tour-dhorizon-de-la-nouvelle-version/&quot;&gt;précédent article&lt;/a&gt; : Spark 2.4 est arrivé avec tout un lot de fonctions d&amp;#39;ordre supérieur (HOF - &lt;i&gt;Higher Order Function&lt;/i&gt;) permettant de manipuler des structures imbriquées (en se limitant aux collections) dans les dataframes. Ce qui est bien pratique pour ce type de colonne, ce qui permet aussi de mieux les appréhender. Néanmoins, cette fonctionnalité est cantonnée à Spark 2.4+ et accessible uniquement dans les requêtes Spark SQL. Or actuellement, bien des datalakes n&amp;#39;ont pas encore migré en version 2.4 et les fournisseurs Hadoop &lt;i&gt;on prem&lt;/i&gt; et cloud sont pour le moment limités à la version 2.3. Alors, comment faire ?&lt;/p&gt;
&lt;!-- end paragraph 4a02d633-a0ef-49e9-82d3-0291b7288e21--&gt;
&lt;!-- begin paragraph 8aa79b7e-42c8-4707-9f3e-2078db9874d9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8aa79b7e-42c8-4707-9f3e-2078db9874d9&quot;&gt;L&amp;#39;idée va être d&amp;#39;ajouter de nouvelles opérations au type &lt;code&gt;Column&lt;/code&gt; utilisant des UDF (&lt;i&gt;User Defined Function&lt;/i&gt;). Nous définissons ci-dessous les fonctions &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;flatMap&lt;/code&gt; et &lt;code&gt;foldLeft&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 8aa79b7e-42c8-4707-9f3e-2078db9874d9--&gt;
&lt;!-- begin code 27c8333a-c74a-48e6-8070-cdab35b3dde5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-27c8333a-c74a-48e6-8070-cdab35b3dde5&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.spark.sql.Column
import org.apache.spark.sql.functions.udf
import scala.reflect.runtime.universe._

object Implicits {
  
  implicit class RicherColumn(column: Column) {

    /** Transform each element of an array by applying the function g.
      */
    def map[A: TypeTag, B: TypeTag](g: A =&amp;gt; B): Column = {
      val f = udf[Seq[B], Seq[A]](s =&amp;gt; s.map(g))

      f(column)
    }

    /** Transform each element of an array by applying the function g
      * and using the elements of the resulting array.
      */
    def flatMap[A: TypeTag, B: TypeTag](g: A =&amp;gt; Seq[B]): Column = {
      val f = udf[Seq[B], Seq[A]](s =&amp;gt; s.flatMap(g))

      f(column)
    }

    /** Aggregate an array to a single value.
      */
    def foldLeft[A: TypeTag, B: TypeTag](init: B)(g: (B, A) =&amp;gt; B): Column = {
      val f = udf[B, Seq[A]](s =&amp;gt; s.foldLeft(init)(g))

      f(column)
    }
    
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 27c8333a-c74a-48e6-8070-cdab35b3dde5--&gt;
&lt;!-- begin paragraph 86dd83e9-8509-4a4e-8b7c-0868b49a859d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86dd83e9-8509-4a4e-8b7c-0868b49a859d&quot;&gt;Voyons à présent comment utiliser ces fonctions. Nous allons pour cela utiliser le dataset ci-dessous.&lt;/p&gt;
&lt;!-- end paragraph 86dd83e9-8509-4a4e-8b7c-0868b49a859d--&gt;
&lt;!-- begin code a3464082-2327-4e80-9a53-5c391c5ed627--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a3464082-2327-4e80-9a53-5c391c5ed627&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val df =
  Seq(
    (&amp;quot;abc&amp;quot;, Seq(&amp;quot;Hello world&amp;quot;),                   Seq(0L, 2L)),
    (&amp;quot;def&amp;quot;, Seq(&amp;quot;Wild Horses&amp;quot;, &amp;quot;Paint It Black&amp;quot;), Seq(1L, 2L))
  ).toDF(
     &amp;quot;id&amp;quot;,  &amp;quot;text&amp;quot;,                               &amp;quot;timestamps&amp;quot;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a3464082-2327-4e80-9a53-5c391c5ed627--&gt;
&lt;!-- begin code b6f5223c-5067-4bf2-880a-a362db210113--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-b6f5223c-5067-4bf2-880a-a362db210113&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+-----------------------------+----------+
|id |text                         |timestamps|
+---+-----------------------------+----------+
|abc|[Hello world]                |[0, 2]    |
|def|[Wild Horses, Paint It Black]|[1, 2]    |
+---+-----------------------------+----------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code b6f5223c-5067-4bf2-880a-a362db210113--&gt;
&lt;!-- begin paragraph 4ea1d5c1-0004-4e21-b308-443cf512b88c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4ea1d5c1-0004-4e21-b308-443cf512b88c&quot;&gt;Utilisons à présent nos fonctions afin de réaliser quelques transformations sur nos données.&lt;/p&gt;
&lt;!-- end paragraph 4ea1d5c1-0004-4e21-b308-443cf512b88c--&gt;
&lt;!-- begin code 6519b484-2e8f-40ee-8336-9ff9b84270a9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6519b484-2e8f-40ee-8336-9ff9b84270a9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import Implicits._

val result =
  df.select(
    $&amp;quot;id&amp;quot;,
    // multiply each timestamp by 2
    $&amp;quot;timestamps&amp;quot;
      .map((t: Long) =&amp;gt; t * 2)
      .as(&amp;quot;map&amp;quot;),
    // get the words in the arrays
    $&amp;quot;text&amp;quot;
      .flatMap((t: String) =&amp;gt; t.split(&amp;quot; &amp;quot;).toSeq)
      .as(&amp;quot;flatMap&amp;quot;),
    // compute the sum of timestamps
    $&amp;quot;timestamps&amp;quot;
      .foldLeft[Long, Long](0)(_ + _)
      .as(&amp;quot;foldLeft&amp;quot;)
  )&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6519b484-2e8f-40ee-8336-9ff9b84270a9--&gt;
&lt;!-- begin paragraph 63416da4-9968-4025-bc42-e39f5460041e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-63416da4-9968-4025-bc42-e39f5460041e&quot;&gt;Ce qui donne :&lt;/p&gt;
&lt;!-- end paragraph 63416da4-9968-4025-bc42-e39f5460041e--&gt;
&lt;!-- begin code 2c059fe3-56be-467c-a72f-9f78a68fa9ad--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2c059fe3-56be-467c-a72f-9f78a68fa9ad&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+------+--------------------------------+--------+
|id |map   |flatMap                         |foldLeft|
+---+------+--------------------------------+--------+
|abc|[0, 4]|[Hello, world]                  |2       |
|def|[2, 4]|[Wild, Horses, Paint, It, Black]|3       |
+---+------+--------------------------------+--------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2c059fe3-56be-467c-a72f-9f78a68fa9ad--&gt;
&lt;!-- begin paragraph 3500aa93-3c9b-41c8-add4-e6d44a41f6d5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3500aa93-3c9b-41c8-add4-e6d44a41f6d5&quot;&gt;À titre de comparaison, essayons d&amp;#39;obtenir un résultat équivalent avec Spark 2.4.&lt;/p&gt;
&lt;!-- end paragraph 3500aa93-3c9b-41c8-add4-e6d44a41f6d5--&gt;
&lt;!-- begin code 155fc236-e754-4835-a6c8-59b5f0be67ad--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-155fc236-e754-4835-a6c8-59b5f0be67ad&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT
  id,
  transform(timestamps, t -&amp;gt; t * 2) as transform,
  flatten(transform(text, t -&amp;gt; split(t, &amp;quot; &amp;quot;))) as flatten_transform,
  aggregate(timestamps, bigint(0), (s, v) -&amp;gt; s + v) as aggregate
FROM data&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 155fc236-e754-4835-a6c8-59b5f0be67ad--&gt;
&lt;!-- begin paragraph 68f29127-8583-4ded-b2a0-4cf28d2423d9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-68f29127-8583-4ded-b2a0-4cf28d2423d9&quot;&gt;Ce qui donne :&lt;/p&gt;
&lt;!-- end paragraph 68f29127-8583-4ded-b2a0-4cf28d2423d9--&gt;
&lt;!-- begin code 5a971ee0-4917-4abb-9f0d-017242c138d5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5a971ee0-4917-4abb-9f0d-017242c138d5&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+---------+--------------------------------+---------+
|id |transform|flatten_transform               |aggregate|
+---+---------+--------------------------------+---------+
|abc|[0, 4]   |[Hello, world]                  |2        |
|def|[2, 4]   |[Wild, Horses, Paint, It, Black]|3        |
+---+---------+--------------------------------+---------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5a971ee0-4917-4abb-9f0d-017242c138d5--&gt;
&lt;!-- begin paragraph 86942515-1956-4c83-99a3-fc91346e14fe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-86942515-1956-4c83-99a3-fc91346e14fe&quot;&gt;En dehors du changement de nom entre nos fonctions et la version 2.4 de Spark 😉 (&lt;code&gt;map&lt;/code&gt; et &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;foldLeft&lt;/code&gt; et &lt;code&gt;aggregate&lt;/code&gt;), nous pouvons remarquer qu&amp;#39;il n&amp;#39;y a pas de fonction équivalente à &lt;code&gt;flatMap&lt;/code&gt;. Nous devons pour le coup utiliser une composition entre &lt;code&gt;flatten&lt;/code&gt; et &lt;code&gt;transform&lt;/code&gt;. Ceci dit, nous avons bien un résultat identique.&lt;/p&gt;
&lt;!-- end paragraph 86942515-1956-4c83-99a3-fc91346e14fe--&gt;
&lt;!-- begin paragraph e8464794-9994-41af-b382-6958b49ee936--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e8464794-9994-41af-b382-6958b49ee936&quot;&gt;Nous avons vu ici quelques fonctions d&amp;#39;ordre supérieur sur dataframe pour les impatients 😉. Comprenez bien sûr que le lot présenté ici peut être complété (filter, take, zip, traverse). Comme il s&amp;#39;agit d&amp;#39;UDF, il n&amp;#39;y a pas d&amp;#39;optimisation possible. Néanmoins, l&amp;#39;avantage de ces fonctions est qu&amp;#39;elles peuvent être utilisées directement dans le code Scala. &lt;/p&gt;
&lt;!-- end paragraph e8464794-9994-41af-b382-6958b49ee936--&gt;
&lt;!-- begin paragraph 55dfe543-5429-4eba-bdd6-b2335fe08302--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-55dfe543-5429-4eba-bdd6-b2335fe08302&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 55dfe543-5429-4eba-bdd6-b2335fe08302--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:c1257e1c6d9047beaee0c061e684e378</id>
    <title>Transition C#.Net vers Scala : un sentier abrupt néanmoins franchissable</title>
    <updated>2019-01-30T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/transition-c-net-vers-scala-un-sentier-abrupt-neanmoins-franchissable.html"/>
    <!--summary -->    <author>
      <name>Bernarith Men</name>
      <uri>https://univalence.io/blog/auteurs/bernarith-men.html</uri>
    </author>        <category term="Scala"></category>    <category term="C#"></category>    <category term=".Net"></category>    <category term="Programmation fonctionnelle"></category>    <category term="Getting Started"></category>    <content type="html">
&lt;!-- begin paragraph 6f5d397a-21ce-4614-9ec2-fc9902a608c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6f5d397a-21ce-4614-9ec2-fc9902a608c0&quot;&gt;Après un peu plus de 3 ans d&amp;#39;expérience dans l&amp;#39;environnement .Net, principalement en tant que software engineer C#, j&amp;#39;ai rejoint Univalence en tant que data engineer pour cette fois-ci travailler avec le langage Scala. Scala est un langage que je n&amp;#39;avais jamais manipulé auparavant et dont je ne connaissais pas grand chose.&lt;/p&gt;
&lt;!-- end paragraph 6f5d397a-21ce-4614-9ec2-fc9902a608c0--&gt;
&lt;!-- begin paragraph 646c94a6-eca7-4d72-a7a5-12d38958cffc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-646c94a6-eca7-4d72-a7a5-12d38958cffc&quot;&gt;Je suis donc parti du monde du langage impératif, où je travaillais sur le back-end et le front-end des projets, pour pénétrer le monde du langage fonctionnel afin de manipuler des données. Il me fallait donc une période de transition pour m&amp;#39;adapter à ce tout nouveau langage pour moi, ainsi qu&amp;#39;à son écosystème. C&amp;#39;est cette expérience que je vais traiter dans cet article où je donnerai mes ressentis, les difficultés auxquelles j&amp;#39;ai fait face et comment je les ai surmontées.&lt;/p&gt;
&lt;!-- end paragraph 646c94a6-eca7-4d72-a7a5-12d38958cffc--&gt;
&lt;!-- begin heading_1 62532afc-be8e-4732-86cc-8d557ebe15d0--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-62532afc-be8e-4732-86cc-8d557ebe15d0&quot;&gt;Les difficultés rencontrées&lt;/h2&gt;
&lt;!-- end heading_1 62532afc-be8e-4732-86cc-8d557ebe15d0--&gt;
&lt;!-- begin heading_2 dd2970b7-524b-403a-b371-62d00fdcd4de--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-dd2970b7-524b-403a-b371-62d00fdcd4de&quot;&gt;&lt;b&gt;1re difficulté : la syntaxe Scala en elle-même !&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 dd2970b7-524b-403a-b371-62d00fdcd4de--&gt;
&lt;!-- begin paragraph b5e2d9c0-2b39-4d72-b50a-db1e069330c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b5e2d9c0-2b39-4d72-b50a-db1e069330c1&quot;&gt;La syntaxe, aux premiers abords, peut paraître vraiment étrange !&lt;/p&gt;
&lt;!-- end paragraph b5e2d9c0-2b39-4d72-b50a-db1e069330c1--&gt;
&lt;!-- begin paragraph c001f7b1-05c8-4c2c-81fe-596b11a1e1f1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c001f7b1-05c8-4c2c-81fe-596b11a1e1f1&quot;&gt;Par exemple : le type se met à la fin de la déclaration de la variable.&lt;/p&gt;
&lt;!-- end paragraph c001f7b1-05c8-4c2c-81fe-596b11a1e1f1--&gt;
&lt;!-- begin paragraph e1f7b715-201b-4b58-9573-f3b932122a2d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e1f7b715-201b-4b58-9573-f3b932122a2d&quot;&gt;En C# :&lt;/p&gt;
&lt;!-- end paragraph e1f7b715-201b-4b58-9573-f3b932122a2d--&gt;
&lt;!-- begin code 94e5cb7a-e87c-450b-b249-e3b5d4083268--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-94e5cb7a-e87c-450b-b249-e3b5d4083268&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;int i = 10;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 94e5cb7a-e87c-450b-b249-e3b5d4083268--&gt;
&lt;!-- begin paragraph 793b8ed8-a1dc-4169-8bb9-4657804104a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-793b8ed8-a1dc-4169-8bb9-4657804104a7&quot;&gt;En Scala :&lt;/p&gt;
&lt;!-- end paragraph 793b8ed8-a1dc-4169-8bb9-4657804104a7--&gt;
&lt;!-- begin code 4fd0567b-4854-4bf5-a927-a90124345166--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4fd0567b-4854-4bf5-a927-a90124345166&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val i: Int = 10
// ou
val j = 10&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4fd0567b-4854-4bf5-a927-a90124345166--&gt;
&lt;!-- begin paragraph df972f4b-4b84-40eb-ad86-c2c704ff77bb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-df972f4b-4b84-40eb-ad86-c2c704ff77bb&quot;&gt;Ici c&amp;#39;est une question d&amp;#39;habitude. On aurait potentiellement le même ressenti en passant à un autre langage que Scala. Mais, c&amp;#39;est vrai que quand on vient du C#, de C, ou de Java, on comprendra plus facilement des écritures comme l&amp;#39;exemple en C#.&lt;/p&gt;
&lt;!-- end paragraph df972f4b-4b84-40eb-ad86-c2c704ff77bb--&gt;
&lt;!-- begin paragraph aa9f2c2a-d2fb-4acd-a95e-dff20c8a2d8f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aa9f2c2a-d2fb-4acd-a95e-dff20c8a2d8f&quot;&gt;Mais en Scala le langage est fortement &amp;quot;sucré&amp;quot;. Ce que je veux dire par là, c&amp;#39;est qu&amp;#39;il existe beaucoup de sucres syntaxiques. C&amp;#39;est-à-dire des syntaxes alternatives, plus simples et plus concises, mais qu&amp;#39;il faut apprendre et connaître.&lt;/p&gt;
&lt;!-- end paragraph aa9f2c2a-d2fb-4acd-a95e-dff20c8a2d8f--&gt;
&lt;!-- begin paragraph 1bdf1fb8-448d-4e28-9e19-1fc040fdf471--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1bdf1fb8-448d-4e28-9e19-1fc040fdf471&quot;&gt;Exemple :&lt;/p&gt;
&lt;!-- end paragraph 1bdf1fb8-448d-4e28-9e19-1fc040fdf471--&gt;
&lt;!-- begin code 67d7570b-a290-4c77-b07a-a9e83a260c9a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-67d7570b-a290-4c77-b07a-a9e83a260c9a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// si on veut multiplier les entiers entre 1 et 10, on peut l&amp;#39;écrire comme ça
(1 to 10).fold(0)(_ * _)
// ou comme ça
(1 to 10).fold(0)((s, v) =&amp;gt; s * v)
// ou comme ça
(1 to 10).fold(0) { case (s, v) =&amp;gt; s * v }
// ou tout simplement
(1 to 10).product&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 67d7570b-a290-4c77-b07a-a9e83a260c9a--&gt;
&lt;!-- begin paragraph e621e5bd-6827-4ba8-8aa9-cdbb5f273b65--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e621e5bd-6827-4ba8-8aa9-cdbb5f273b65&quot;&gt;Par contre, on retrouve les expressions lambda en Scala, ce qui est un avantage quand on en a déjà fait en C#.&lt;/p&gt;
&lt;!-- end paragraph e621e5bd-6827-4ba8-8aa9-cdbb5f273b65--&gt;
&lt;!-- begin heading_2 615f8202-7cf6-4f97-aa8e-2eb66a38f18d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-615f8202-7cf6-4f97-aa8e-2eb66a38f18d&quot;&gt;&lt;b&gt;De la programmation impérative vers la programmation fonctionnelle&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 615f8202-7cf6-4f97-aa8e-2eb66a38f18d--&gt;
&lt;!-- begin paragraph 370e0285-5e3c-43ae-90a0-72e1cb821af3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-370e0285-5e3c-43ae-90a0-72e1cb821af3&quot;&gt;La plus grande difficulté de cette transition est le passage vers la programmation fonctionnelle. En effet, lors de l&amp;#39;apprentissage du langage, on y est confronté rapidement. Il faut presque complètement changer sa façon de penser le code, comme par exemple essayer d&amp;#39;éliminer au maximum les composantes mutables du code (on ne réaffecte plus les variables préalablement déclarées).&lt;/p&gt;
&lt;!-- end paragraph 370e0285-5e3c-43ae-90a0-72e1cb821af3--&gt;
&lt;!-- begin paragraph 06e664b2-33ae-42a3-a49c-7922d16e88cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06e664b2-33ae-42a3-a49c-7922d16e88cb&quot;&gt;Quand on va un peu plus loin en Scala et en programmation fonctionnelle, on rentre beaucoup dans des notions de mathématiques et l&amp;#39;algorithmie y est plus poussée. J&amp;#39;ai trouvé cela assez amusant de voir que certaines notions apprises en mathématiques lors de mes études étaient utilisées en programmation fonctionnelle, alors que lorsque j&amp;#39;étais dans mon ancien poste je ne m&amp;#39;étais jamais dit que j&amp;#39;allais réutiliser ces notions un jour.&lt;/p&gt;
&lt;!-- end paragraph 06e664b2-33ae-42a3-a49c-7922d16e88cb--&gt;
&lt;!-- begin heading_2 0dd3d30e-4574-48f3-89d6-3340337cb696--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-0dd3d30e-4574-48f3-89d6-3340337cb696&quot;&gt;&lt;b&gt;L&amp;#39;écosystème&lt;/b&gt;&lt;/h3&gt;
&lt;!-- end heading_2 0dd3d30e-4574-48f3-89d6-3340337cb696--&gt;
&lt;!-- begin paragraph 7bc71191-d739-4e72-b50b-4b88f5a4d25e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7bc71191-d739-4e72-b50b-4b88f5a4d25e&quot;&gt;Quand je parle d&amp;#39;écosystème, je parle des outils autour du langage, des bonnes pratiques, etc. En .Net, l&amp;#39;IDE utilisé est Visual Studio, où le plus souvent nous utilisons les mêmes plugins, librairies et la gestion de code source se fait en général avec Team Foundation Server, intégré à Visual Studio dans les dernières versions. Quant à la documentation, elle est très fournie et on y trouve toujours des exemples. Là où je veux en venir, c&amp;#39;est qu&amp;#39;en .Net nous avons quasiment tout au même endroit et il existe beaucoup de standards. Alors qu&amp;#39;en commençant à me mettre à Scala, nous avons plus de choix, déjà pour l&amp;#39;IDE, où on peut utiliser IntelliJ, Eclipse, Visual Studio Code ou même un simple éditeur de texte !&lt;/p&gt;
&lt;!-- end paragraph 7bc71191-d739-4e72-b50b-4b88f5a4d25e--&gt;
&lt;!-- begin paragraph 06bdbc1f-bb4f-4efd-a47d-acace8f43564--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06bdbc1f-bb4f-4efd-a47d-acace8f43564&quot;&gt;C&amp;#39;est bien là, un autre facteur de difficulté, qui vaut aussi un certain temps d&amp;#39;adaptation. Il a fallu que je m&amp;#39;adapte à :&lt;/p&gt;
&lt;!-- end paragraph 06bdbc1f-bb4f-4efd-a47d-acace8f43564--&gt;&lt;ul class=&quot;block-bulleted_list_item level-1&quot;&gt;&lt;li&gt;&lt;b&gt;Git&lt;/b&gt;. Car en venant de .Net, il est tout à fait possible de n&amp;#39;avoir jamais fait de Git. J&amp;#39;utilisais surtout Team Foundation Server et dans les projets sur lesquels j&amp;#39;ai été, nous étions sur une base de lock/unlock (lock pessimiste), il y avait aussi moins d&amp;#39;histoire de conflit de merge.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Aux raccourcis d&amp;#39;IntelliJ&lt;/b&gt; (l&amp;#39;IDE avec lequel j&amp;#39;ai appris Scala), ce qui implique une réadaptation de mes réflexes.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Chercher des librairies&lt;/b&gt;. Là où certaines fonctionnalités existaient &lt;b&gt;peut être&lt;/b&gt; déjà en .Net, elles n&amp;#39;existent pas forcément en Scala/Java, ou il n&amp;#39;existe pas de librairies dites standards. En .Net, on pouvait les chercher et les obtenir directement dans Visual Studio avec NuGet, alors que maintenant nous irions plutôt sur Maven Central et chercher la bonne version d&amp;#39;une librairie pour la mettre dans notre &lt;code&gt;build.sbt&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;b&gt;La documentation&lt;/b&gt;. en .Net, elle très fournie et détaillé avec des exemples. Mais nous n&amp;#39;avons pas souvent accès au code source. Alors qu&amp;#39;en Scala, nous avons aussi une documentation, mais pas avec autant d&amp;#39;exemple. Il faut parfois explorer le code. Néanmoins, l&amp;#39;avantage c&amp;#39;est que nous pouvons souvent voir ce code source pour comprendre mieux.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Au contexte du data engineering&lt;/b&gt;. Avec mon ancien poste je développais surtout des clients lourds/légers (donc qui avait une interface graphique ou seulement une console), une couche d&amp;#39;accès aux données et une couche d&amp;#39;abstraction entre les deux. Autrement dit des applications répondant aux design pattern MVVM ou MVC. Maintenant en Scala, je fais des outils sans suivre ces design pattern et surtout sans DAO.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph f9b25965-0b07-47c7-8b06-9228b5f2bf18--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9b25965-0b07-47c7-8b06-9228b5f2bf18&quot;&gt;Pour ces difficultés, ce n&amp;#39;était qu&amp;#39;une question d&amp;#39;habitude. Ce que je conseille pour tous ceux qui veulent se lancer dans Scala et la programmation fonctionnelle, c&amp;#39;est de faire ce &lt;a href=&quot;https://www.coursera.org/learn/progfun1&quot;&gt;cours en ligne&lt;/a&gt; sur Coursera donné par Martin Odersky, le créateur du langage.&lt;/p&gt;
&lt;!-- end paragraph f9b25965-0b07-47c7-8b06-9228b5f2bf18--&gt;
&lt;!-- begin paragraph 74ec5d66-ab78-4812-8a2a-73377c145086--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-74ec5d66-ab78-4812-8a2a-73377c145086&quot;&gt;Si vous avez cette chance de travailler avec des personnes confirmées en Scala, ce qui est mon cas au sein d&amp;#39;Univalence, le pair programming m&amp;#39;a permis de progresser énormément. Avoir des exercices réguliers, a aussi été un bon moyen de progression.&lt;/p&gt;
&lt;!-- end paragraph 74ec5d66-ab78-4812-8a2a-73377c145086--&gt;
&lt;!-- begin heading_1 a7c03bc3-0d33-4db5-85e1-38d8a5cf41a5--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a7c03bc3-0d33-4db5-85e1-38d8a5cf41a5&quot;&gt;Le ressenti après 6 mois&lt;/h2&gt;
&lt;!-- end heading_1 a7c03bc3-0d33-4db5-85e1-38d8a5cf41a5--&gt;
&lt;!-- begin paragraph f826f3c1-11f6-4fb9-9883-a1b5c0888447--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f826f3c1-11f6-4fb9-9883-a1b5c0888447&quot;&gt;Au bout de maintenant 6 mois à utiliser Scala, je me sens plus à l&amp;#39;aise par rapport à ce qui a été dit précédemment. Je me suis surpris à utiliser des opérations comme &lt;code&gt;map&lt;/code&gt; ou &lt;code&gt;flatMap&lt;/code&gt; de moi-même, et surtout à réussir à obtenir le résultat voulu à partir de ces opérations. La pente à gravir est assez difficile quand on vient du monde impératif. Et je pense d&amp;#39;autant plus quand on ne vient pas de Java. Mais c&amp;#39;est toujours surmontable. En tout cas, je comprends mieux les avantages de la programmation fonctionnelle.&lt;/p&gt;
&lt;!-- end paragraph f826f3c1-11f6-4fb9-9883-a1b5c0888447--&gt;
&lt;!-- begin paragraph c1bd751a-48d9-4484-9b65-f4de1d008bfb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c1bd751a-48d9-4484-9b65-f4de1d008bfb&quot;&gt;Lors de cette montée en compétence, j&amp;#39;ai eu la chance d&amp;#39;être mentorés par nos experts. J&amp;#39;ai pu participer à &lt;a href=&quot;http://scala.io/&quot;&gt;Scala IO&lt;/a&gt; 2018 dont nous avons fait un retour &lt;a href=&quot;https://blog.univalence.io/scalaio-2018-le-retour-de-lequipe-univalence/&quot;&gt;ic&lt;/a&gt;i. J&amp;#39;ai en tout cas remarqué que la communauté Scala est très humble et que mêmes les grands noms sont &amp;quot;accessibles&amp;quot;. Cela a en tout cas été un facteur motivant pour en apprendre plus.&lt;/p&gt;
&lt;!-- end paragraph c1bd751a-48d9-4484-9b65-f4de1d008bfb--&gt;
&lt;!-- begin paragraph b19acff0-423d-4c86-85d1-0bbbcbe50b85--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b19acff0-423d-4c86-85d1-0bbbcbe50b85&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph b19acff0-423d-4c86-85d1-0bbbcbe50b85--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:54c2ce1fd7ff4983bdb1818a7eb439d9</id>
    <title>Formattage du code en Scala</title>
    <updated>2019-01-28T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/formattage-du-code-en-scala.html"/>
    <!--summary -->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Scalafmt"></category>    <category term="Scala"></category>    <category term="Outil"></category>    <content type="html">
&lt;!-- begin paragraph 9dfb9060-befe-4201-a052-58a6c9faaa70--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9dfb9060-befe-4201-a052-58a6c9faaa70&quot;&gt;Il est important dans un projet de conserver une bonne maîtrise de votre base de code. C&amp;#39;est en tout cas un prérequis pour faciliter la mise en place de nouvelles fonctionnalités dans une application, mais aussi la correction de bugs, le refactoring et enfin de faciliter la transmission de connaissance dans un projet. Un code bien maîtrisé nécessite un code lisible au moins par l&amp;#39;équipe de développement. Outre le fait que les classes, les fonctions et les variables soient bien nommées, qu&amp;#39;il y ait une séparation nette entre aspects techniques et aspects métiers, que le code soit correctement documenté, la façon de formater ce code participe à sa propre lisibilité.&lt;/p&gt;
&lt;!-- end paragraph 9dfb9060-befe-4201-a052-58a6c9faaa70--&gt;
&lt;!-- begin paragraph 955cc8a2-5798-4d1c-b8b5-742549dd40bc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-955cc8a2-5798-4d1c-b8b5-742549dd40bc&quot;&gt;Avec Scala, il existe différents outils permettant de formater automatiquement votre code source. Les IDE et la plupart des éditeurs de code proposent généralement une fonctionnalité de formatage. Le problème dans ce cas est de partager la configuration avec l&amp;#39;équipe. Ce qui vous force à vous restreindre qu&amp;#39;à un seul type d&amp;#39;éditeur et même à avoir les fichiers de configuration de l&amp;#39;éditeur intégré dans votre gestionnaire de version. Ainsi, votre projet se retrouve en dépendance avec l&amp;#39;éditeur de code ! Il vaut mieux éviter cette perspective 🧐. Sinon, il existe dans l&amp;#39;écosystème Scala deux outils reconnus. Il y a &lt;a href=&quot;http://scala-ide.org/scalariform/&quot;&gt;Scalariform&lt;/a&gt; et son &lt;a href=&quot;https://github.com/sbt/sbt-scalariform&quot;&gt;plugin SBT&lt;/a&gt;, ainsi que &lt;a href=&quot;http://scalameta.org/scalafmt/&quot;&gt;Scalafmt&lt;/a&gt; qui a aussi un &lt;a href=&quot;https://scalameta.org/scalafmt/docs/installation.html#sbt&quot;&gt;plugin SBT&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 955cc8a2-5798-4d1c-b8b5-742549dd40bc--&gt;
&lt;!-- begin paragraph db42f7da-56b8-4a17-a538-79e7b1236978--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-db42f7da-56b8-4a17-a538-79e7b1236978&quot;&gt;J&amp;#39;ai personnellement une préférence pour Scalafmt. Cet outil propose de décrire la configuration du formatage dans un fichier séparé, nommé &lt;code&gt;.scalafmt.conf&lt;/code&gt;. Il est compatible avec &lt;a href=&quot;http://dotty.epfl.ch/&quot;&gt;Dotty&lt;/a&gt;, représentant le futur de Scala. Il possède une &lt;a href=&quot;https://scalameta.org/scalafmt/docs/installation.html&quot;&gt;documentation assez bien fournie&lt;/a&gt; (l&amp;#39;équipe Scalafmt indique toutefois qu&amp;#39;elle n&amp;#39;est pas exhaustive et qu&amp;#39;il faut parfois fouiller dans le code de l&amp;#39;outil 🤓 !) Il y a aussi un &lt;a href=&quot;https://plugins.jetbrains.com/plugin/8236-scalafmt&quot;&gt;plugin&lt;/a&gt; disponible pour IntelliJ IDEA, qui se base sur le fichier &lt;code&gt;.scalafmt.conf&lt;/code&gt;. Enfin, Scalafmt est aussi intégré dans &lt;a href=&quot;https://scalameta.org/metals/&quot;&gt;Metals&lt;/a&gt; : un &amp;quot;&lt;a href=&quot;https://langserver.org/&quot;&gt;serveur de langage&lt;/a&gt;&amp;quot; assurant une intégration avancées de Scala avec éditeurs de texte sophistiqués comme Atom, VSC, Vim et Emacs.&lt;/p&gt;
&lt;!-- end paragraph db42f7da-56b8-4a17-a538-79e7b1236978--&gt;
&lt;!-- begin paragraph 6d301729-2634-4b99-8c12-2614b9a99a93--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d301729-2634-4b99-8c12-2614b9a99a93&quot;&gt;L&amp;#39;intégration de Scalafmt dans les projet basé sur SBT nécessite de déclarer le plugin associé dans le fichier &lt;code&gt;plugins.sbt&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 6d301729-2634-4b99-8c12-2614b9a99a93--&gt;
&lt;!-- begin code 33401f94-c898-4f01-9d77-4b4852eaa644--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-33401f94-c898-4f01-9d77-4b4852eaa644&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;addSbtPlugin(&amp;quot;com.geirsson&amp;quot; % &amp;quot;sbt-scalafmt&amp;quot; % &amp;quot;1.5.1&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 33401f94-c898-4f01-9d77-4b4852eaa644--&gt;
&lt;!-- begin paragraph 3ac5b885-8558-4e3b-8af8-13a57641b9f9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ac5b885-8558-4e3b-8af8-13a57641b9f9&quot;&gt;Pour que le formatage soit fait avant chaque compilation, il faut ajouter la ligne suivante dans &lt;code&gt;build.sbt&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 3ac5b885-8558-4e3b-8af8-13a57641b9f9--&gt;
&lt;!-- begin code 31540e1e-35c7-40c4-b974-32757e391e9e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-31540e1e-35c7-40c4-b974-32757e391e9e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;scalafmtOnCompile in ThisBuild := true&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 31540e1e-35c7-40c4-b974-32757e391e9e--&gt;
&lt;!-- begin paragraph aacb5886-77ea-4696-a131-cbd54fb134b9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aacb5886-77ea-4696-a131-cbd54fb134b9&quot;&gt;L&amp;#39;équipe Scalafmt déconseille cette fonctionnalité, sans forcément donner plus d&amp;#39;explications. Néanmoins, le formatage du code est une étape qui est coûteuse en temps sur les gros projets. De plus, lors de cette étape, vous pouvez toujours éditer le code source. Vous vous retrouvez alors avec des cas de conflits entre le reformatage fait par Scalafmt et vos modifications. Enfin, vous pouvez avoir des conflits en cas d&amp;#39;import de code par Git (&lt;i&gt;pull&lt;/i&gt;, &lt;i&gt;rebase&lt;/i&gt;, &lt;i&gt;pull&lt;/i&gt;). Ceci dit, avec un IDE comme IntelliJ IDEA et cette option activée, vous pouvez avoir un code régulièrement reformaté sans trop de désagrément — à ce propos, il est recommandé dans sa configuration &lt;a href=&quot;https://scalameta.org/scalafmt/docs/installation.html#continue-using-intellij-formatter&quot;&gt;d’utiliser le formateur d&amp;#39;IntelliJ&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph aacb5886-77ea-4696-a131-cbd54fb134b9--&gt;
&lt;!-- begin paragraph ec844f8f-308a-4456-8f3e-59ba435e41ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec844f8f-308a-4456-8f3e-59ba435e41ae&quot;&gt;Pour avoir un formatage intéressant, vous pouvez commencer par deux lignes de configuration dans &lt;code&gt;.scalafmt.conf&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph ec844f8f-308a-4456-8f3e-59ba435e41ae--&gt;
&lt;!-- begin code efa6d25d-17ee-4b23-8dfa-1ba228916621--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-efa6d25d-17ee-4b23-8dfa-1ba228916621&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;align = most
maxColumn = 120&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code efa6d25d-17ee-4b23-8dfa-1ba228916621--&gt;
&lt;!-- begin paragraph b10259d6-8540-4e86-9aa9-66c5575f9b64--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b10259d6-8540-4e86-9aa9-66c5575f9b64&quot;&gt;À noter que s&amp;#39;il est parfois considéré comme mal venu de limiter la taille des lignes dans le code, il reste néanmoins plus confortable d&amp;#39;avoir un code pas trop large en règle générale.&lt;/p&gt;
&lt;!-- end paragraph b10259d6-8540-4e86-9aa9-66c5575f9b64--&gt;
&lt;!-- begin paragraph d8d4c3f3-177f-4952-ae08-adf0926e033f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d8d4c3f3-177f-4952-ae08-adf0926e033f&quot;&gt;La partie alignement est l&amp;#39;une des fonctionnalités sur laquelle il peut y avoir le plus de discussions. La configuration &lt;code&gt;align&lt;/code&gt; peut avoir comme valeur &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;some&lt;/code&gt; ou &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;more&lt;/code&gt;, &lt;code&gt;most&lt;/code&gt;, selon le degré d&amp;#39;alignement que vous souhaitez dans votre code. Les valeurs &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;default&lt;/code&gt; et &lt;code&gt;some&lt;/code&gt; sont celles qui limiteront les surprises lors du reformatage du code et aussi les conflits dans Git. Dans la mesure où le reformatage du code est automatisé au sein de l&amp;#39;équipe de développement et exécuté très régulièrement (avant chaque compilation, avant chaque sauvegarde du fichier, avant chaque &lt;i&gt;push&lt;/i&gt; sur un repo central...), vous pouvez envisager les valeurs &lt;code&gt;more&lt;/code&gt; et &lt;code&gt;most&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph d8d4c3f3-177f-4952-ae08-adf0926e033f--&gt;
&lt;!-- begin paragraph 373acb9d-9815-42fc-b494-d213854cf455--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-373acb9d-9815-42fc-b494-d213854cf455&quot;&gt;Scalafmt propose plusieurs options pour paramétrer l&amp;#39;alignement des différentes parties de votre code, l&amp;#39;ajout d&amp;#39;espaces, de sauts de ligne et d&amp;#39;autres règles de réécriture. Il permet de mettre en place des règles spécifiques par rapport à des symboles et même par rapport à des symboles dans des contextes particuliers au niveau de l&amp;#39;&lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;AST&lt;/a&gt;. L&amp;#39;exemple ci-dessous permet de réaliser un alignement par rapport au symbole &lt;code&gt;=&amp;gt;&lt;/code&gt; uniquement dans le cadre d&amp;#39;un pattern matching avec &lt;code&gt;case&lt;/code&gt; et non dans le cadre d&amp;#39;une fonction.&lt;/p&gt;
&lt;!-- end paragraph 373acb9d-9815-42fc-b494-d213854cf455--&gt;
&lt;!-- begin code dc8aa087-2699-4517-a358-682e4447649f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-dc8aa087-2699-4517-a358-682e4447649f&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;align.tokens = [{code = &amp;quot;=&amp;gt;&amp;quot;, owner = &amp;quot;Case&amp;quot;}]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code dc8aa087-2699-4517-a358-682e4447649f--&gt;
&lt;!-- begin paragraph b72addc8-2890-4b0e-a676-3214c68b488f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b72addc8-2890-4b0e-a676-3214c68b488f&quot;&gt;Il est possible aussi d&amp;#39;activer ou de désactiver Scalafmt selon certains critères : soit pour certaines catégories de fichiers (uniquement ceux gérés par Git et/ou correspondant à un certain motif), soit sur des blocs de code en utilisant le commentaire spécial &lt;code&gt;// format:&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph b72addc8-2890-4b0e-a676-3214c68b488f--&gt;
&lt;!-- begin code 31f0a0ba-378a-454e-b7e6-27e12d8b9ff1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-31f0a0ba-378a-454e-b7e6-27e12d8b9ff1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;// format: off
// this part won&amp;#39;t modified by scalafmt
val identity = Array(1, 0, 0,
                     0, 1, 0,
                     0, 0, 1)
// format: on&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 31f0a0ba-378a-454e-b7e6-27e12d8b9ff1--&gt;
&lt;!-- begin paragraph d7914c52-6706-4602-882b-d3aed59e4540--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d7914c52-6706-4602-882b-d3aed59e4540&quot;&gt;Scalafmt est un outil pratique pour conserver un code Scala homogène au niveau d&amp;#39;un projet. Aussi, nous vous recommandons de l&amp;#39;intégrer (celui-ci ou tout autre outil équivalent) dans vos projet et de discuter en équipe de la configuration de cet outil.&lt;/p&gt;
&lt;!-- end paragraph d7914c52-6706-4602-882b-d3aed59e4540--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:39bbbbbbccca4a4fa0fe983c45a63e08</id>
    <title>Amélioration du lead time des chaînes en Spark avec un peu de Monix</title>
    <updated>2019-01-24T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/amelioration-du-lead-time-des-chaines-en-spark-avec-un-peu-de-monix.html"/>
    <!--summary -->    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="Spark"></category>    <category term="Monix"></category>    <content type="html">
&lt;!-- begin heading_2 2453a652-67c4-4529-8677-a53425eb1440--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-2453a652-67c4-4529-8677-a53425eb1440&quot;&gt;Monix&lt;/h3&gt;
&lt;!-- end heading_2 2453a652-67c4-4529-8677-a53425eb1440--&gt;
&lt;!-- begin paragraph 72a17770-e178-440c-9240-bfa30d5f4b36--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-72a17770-e178-440c-9240-bfa30d5f4b36&quot;&gt;Monix est un framework Scala permettant la gestion des effets grâce à l&amp;#39;abstraction Task, qui peut englober tout ce qui relève du code à effet, comme &lt;i&gt;print&lt;/i&gt; ou la lecture de fichier.&lt;/p&gt;
&lt;!-- end paragraph 72a17770-e178-440c-9240-bfa30d5f4b36--&gt;
&lt;!-- begin paragraph 04027e40-5635-40e3-849b-9efee87df446--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-04027e40-5635-40e3-849b-9efee87df446&quot;&gt;Nous utilisons ici la version 3.0.0 (qui est toujours en RC), car c&amp;#39;est la première version de Monix à implémenter les fibers. Tout comme les coroutines, les &lt;a href=&quot;https://fr.wikipedia.org/wiki/Fibre_(informatique)&quot;&gt;&lt;i&gt;fibers&lt;/i&gt;&lt;/a&gt; (ou fibres) sont des petites unités de traitement coopératifs, pouvant s&amp;#39;intégrer au sein d&amp;#39;un Thread.&lt;/p&gt;
&lt;!-- end paragraph 04027e40-5635-40e3-849b-9efee87df446--&gt;
&lt;!-- begin heading_2 e900f60e-91cb-4640-962e-cc1cca6dd14c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e900f60e-91cb-4640-962e-cc1cca6dd14c&quot;&gt;Expérience sur Monix avec Spark&lt;/h3&gt;
&lt;!-- end heading_2 e900f60e-91cb-4640-962e-cc1cca6dd14c--&gt;
&lt;!-- begin paragraph 3dc086aa-5562-4da6-a56a-e7db823b0ea7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3dc086aa-5562-4da6-a56a-e7db823b0ea7&quot;&gt;En lançant deux jobs Spark en parallèle avec Monix dans une même application Spark, nous voulions montrer (dans le cadre d&amp;#39;un audit Univalence) qu&amp;#39;il était possible de réduire significativement la durée totale d&amp;#39;un traitement qui lance plusieurs jobs.&lt;/p&gt;
&lt;!-- end paragraph 3dc086aa-5562-4da6-a56a-e7db823b0ea7--&gt;
&lt;!-- begin paragraph e273668d-c311-4d15-ab65-fdfd86d47c85--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e273668d-c311-4d15-ab65-fdfd86d47c85&quot;&gt;Deux dataframes sont créées et sont mises dans des Task de Monix. En utilisant une &lt;i&gt;for-comprehension&lt;/i&gt;, ces deux Task peuvent être utilisées afin de pouvoir montrer qu&amp;#39;il est possible grâce à Monix d&amp;#39;avoir des gains de temps sur les jobs Spark.&lt;/p&gt;
&lt;!-- end paragraph e273668d-c311-4d15-ab65-fdfd86d47c85--&gt;
&lt;!-- begin paragraph b5576450-b051-4c78-bbd9-657a83574f74--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b5576450-b051-4c78-bbd9-657a83574f74&quot;&gt;Dans l&amp;#39;application, la jointure est mise en mémoire cache afin de pouvoir ensuite lancer deux jobs en parallèle depuis ce cache. Ces deux jobs sont lancés grâce à la méthode &lt;code&gt;start()&lt;/code&gt; de Monix, qui permet de lancer Task avec des Fibers.&lt;/p&gt;
&lt;!-- end paragraph b5576450-b051-4c78-bbd9-657a83574f74--&gt;
&lt;!-- begin paragraph a74aa6c5-2514-499c-ae2e-d7586d743455--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a74aa6c5-2514-499c-ae2e-d7586d743455&quot;&gt;En ce qui concerne le &lt;code&gt;slow&lt;/code&gt;, cette fonction sert juste à ralentir le &lt;code&gt;count&lt;/code&gt; car il serait sinon trop rapide.&lt;/p&gt;
&lt;!-- end paragraph a74aa6c5-2514-499c-ae2e-d7586d743455--&gt;
&lt;!-- begin paragraph 87172796-3f59-412d-adb2-4fdfe975a747--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-87172796-3f59-412d-adb2-4fdfe975a747&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 87172796-3f59-412d-adb2-4fdfe975a747--&gt;
&lt;!-- begin code 6a1a00bf-f9e1-4286-904a-f7909eb825bb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6a1a00bf-f9e1-4286-904a-f7909eb825bb&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;for{
      df1 &amp;lt;- loadDF1.memoize
      df2 &amp;lt;- loadDF2.memoize

      join = df1.join(df2).repartition(16).cache()

      job1 &amp;lt;- Task(join.slow.count).start
      job2 &amp;lt;- Task(join.slow.count).start

      end &amp;lt;- Task.parZip2(job1.join,job2.join)
} yield end&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6a1a00bf-f9e1-4286-904a-f7909eb825bb--&gt;
&lt;!-- begin paragraph 8f78dec4-5807-48c7-8118-428468e54d07--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8f78dec4-5807-48c7-8118-428468e54d07&quot;&gt;Voici le DAG des différentes étapes du code donné ci-dessus.&lt;/p&gt;
&lt;!-- end paragraph 8f78dec4-5807-48c7-8118-428468e54d07--&gt;
&lt;!-- begin paragraph abf51e7f-5563-400e-b5b8-f53ba8411908--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-abf51e7f-5563-400e-b5b8-f53ba8411908&quot;&gt;Sur l&amp;#39;image ci-dessous, nous voyons que nous avons bien réussi à lancer deux jobs en parallèle au sein d&amp;#39;une seule instance de SparkContext.&lt;/p&gt;
&lt;!-- end paragraph abf51e7f-5563-400e-b5b8-f53ba8411908--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/39bbbbbb-ccca-4a4f-a0fe-983c45a63e08/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph db7ca9b4-c570-44ba-ba74-80b89b4527ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-db7ca9b4-c570-44ba-ba74-80b89b4527ed&quot;&gt;Sur l&amp;#39;image ci-dessous, nous remarquons que même si le premier &lt;code&gt;count&lt;/code&gt; n&amp;#39;est pas terminé, le deuxième est déjà lancé.&lt;/p&gt;
&lt;!-- end paragraph db7ca9b4-c570-44ba-ba74-80b89b4527ed--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/39bbbbbb-ccca-4a4f-a0fe-983c45a63e08/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 66f3b79f-42f2-461d-9f84-f4aa99445a92--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-66f3b79f-42f2-461d-9f84-f4aa99445a92&quot;&gt;Au final : le deuxième &lt;code&gt;count&lt;/code&gt; est lancé en même temps, avec un temps de mise en œuvre total de 7 minutes, au lieu de 2 * 6.1 minutes (le lancement en concurrence dans ce cas à fait gagner 85% du temps d’exécution sans demander de ressource supplémentaires).&lt;/p&gt;
&lt;!-- end paragraph 66f3b79f-42f2-461d-9f84-f4aa99445a92--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/39bbbbbb-ccca-4a4f-a0fe-983c45a63e08/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 a46a9c1f-75c0-41d9-ad89-8f1936d054d1--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a46a9c1f-75c0-41d9-ad89-8f1936d054d1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 a46a9c1f-75c0-41d9-ad89-8f1936d054d1--&gt;
&lt;!-- begin paragraph d09edac0-d74f-4572-8499-0f11b3ac5739--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d09edac0-d74f-4572-8499-0f11b3ac5739&quot;&gt;Monix est une librairie vraiment intéressante à combiner avec Spark cependant nous avons aussi trouvé qu&amp;#39;il était impossible d&amp;#39;annuler un job Spark se trouvant dans une fiber que ce soit avec Monix ou ZIO. Quand bien même l&amp;#39;use case d&amp;#39;annuler des jobs Spark en cours n&amp;#39;est pas très commun, il aurait été plus sympathique de pouvoir le faire car c&amp;#39;est normalement une des fonctionnalités majeures lorsque nous utilisons des fibers que ce soit avec Monix ou tout autre librairie similaire. &lt;/p&gt;
&lt;!-- end paragraph d09edac0-d74f-4572-8499-0f11b3ac5739--&gt;
&lt;!-- begin paragraph 0201e997-81cb-4d00-b2d7-7cfa680bfcfc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0201e997-81cb-4d00-b2d7-7cfa680bfcfc&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0201e997-81cb-4d00-b2d7-7cfa680bfcfc--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:fa5823fc988c413c97a27b418e58abf4</id>
    <title>Parler de programmation fonctionnelle à l&#039;université - retour sur mon intervention à l&#039;Université Paris Nanterre</title>
    <updated>2019-01-20T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/parler-de-programmation-fonctionnelle-a-l-universite-retour-sur-mon-intervention-a-l-universite-paris-nanterre.html"/>
    <!--summary J&#039;ai eu l&#039;occasion et la chance d&#039;intervenir dans l&#039;Université Paris Nanterre pour parler de programmation fonctionnelle et voici ce qui en est ressorti.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Programmation fonctionnelle"></category>    <category term="Partage"></category>    <category term="Scala"></category>    <category term="Java"></category>    <content type="html">
&lt;!-- begin paragraph c73d0d70-c1f5-4be3-baa2-b204dbc1d849--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c73d0d70-c1f5-4be3-baa2-b204dbc1d849&quot;&gt;Je suis intervenu récemment à l&amp;#39;Université Paris Nanterre pour un séminaire de deux heures focalisé sur la programmation fonctionnelle, suite à une proposition d&amp;#39;un enseignant-chercheur. La session était appelée &amp;quot;Ce qu&amp;#39;il faut savoir de la programmation fonctionnelle&amp;quot; et était présentée face à une vingtaine d&amp;#39;étudiants de niveau licence 3 et master 1 et 2 en informatique, ainsi que des enseignants. Mon objectif est alors de sensibiliser un tel public à ce style de programmation, lui expliquer qu&amp;#39;on retrouve ce paradigme de plus en plus dans le projet sous une forme ou sous une autre.&lt;/p&gt;
&lt;!-- end paragraph c73d0d70-c1f5-4be3-baa2-b204dbc1d849--&gt;
&lt;!-- begin paragraph 9219be0a-68a1-4dc5-8c9a-8cd5757ac132--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9219be0a-68a1-4dc5-8c9a-8cd5757ac132&quot;&gt;Ceci dit, la difficulté a été pour moi de me mettre à la place d&amp;#39;un étudiant. Vous souvenez vous de cette période où il vous semblait (pour certains) difficile, voire très difficile, de trouver un emploi et de trouver la bonne approche pour vous mettre en valeur ? Vous souvenez vous de cette époque où vous ne saviez pas encore ce qu&amp;#39;est vraiment un projet informatique en entreprise ? Vous souvenez vous surtout de cette époque où vous aviez de toute façon pas d&amp;#39;autres choix que de développer des projets jetables ? C&amp;#39;est-à-dire, qu&amp;#39;entre les cours et les TDs, et les projets à faire pour les autres modules de formation, vous deviez passer du temps, parfois seul, parfois en équipe, sur des projets cherchant généralement à résoudre... un problème technique. Et vous cherchiez alors à faire le juste nécessaire pour obtenir une bonne note, avec pour seule véritable exigence en termes de qualité de code de pouvoir être revu par l&amp;#39;enseignant. Même si on y retrouve cette notion de date limite de livraison, ceci contraste fortement face à des projets en équipe pleinement dédiée qui doit apporter un produit avec une valeur métier, censés durer dans le temps et être repris par d&amp;#39;autres développeurs que vous connaissez ou que vous ne connaîtrez jamais. Ces points, je les avais oubliés. C&amp;#39;est en discutant avec les enseignants qu&amp;#39;ils me sont revenus en mémoire.&lt;/p&gt;
&lt;!-- end paragraph 9219be0a-68a1-4dc5-8c9a-8cd5757ac132--&gt;
&lt;!-- begin paragraph 5200a4cf-2ff6-4049-86f9-ba488800d4e2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5200a4cf-2ff6-4049-86f9-ba488800d4e2&quot;&gt;En attendant, il fallait donc faire cet effort d&amp;#39;expliquer ce qu&amp;#39;est un projet informatique en entreprise, expliquer les problèmes rencontrés, pour finalement expliquer ce qu&amp;#39;apporte la solution mise en avant.&lt;/p&gt;
&lt;!-- end paragraph 5200a4cf-2ff6-4049-86f9-ba488800d4e2--&gt;
&lt;!-- begin paragraph 4f594ddf-36c9-491a-a6c9-ab6188a11930--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4f594ddf-36c9-491a-a6c9-ab6188a11930&quot;&gt;Plus qu&amp;#39;une simple présentation sur la programmation fonctionnelle, j&amp;#39;ai ainsi cherché à expliquer mon parcours et comment est-ce que j&amp;#39;ai vécu la montée en popularité de la programmation. J&amp;#39;ai montré ensuite comment se déroule un projet informatique en entreprise, comment s&amp;#39;organise une équipe de développement et quelles sont les difficultés qui y sont rencontrées (communication, maintenabilité du code, tentatives de séparation des aspects métiers et techniques...). Et pour terminer, j&amp;#39;ai présenté l&amp;#39;origine de la programmation fonctionnelle, ses apports dans le monde du développement et aussi les difficultés rencontrées avec ce type de paradigme. J&amp;#39;ai alors montré des concepts de la programmation fonctionnelle applicables dans la plupart des langages modernes, donné quelques cas d&amp;#39;usage et déroulé une démonstration à travers une application Web, dans laquelle j&amp;#39;expose clairement l&amp;#39;architecture et dans laquelle j&amp;#39;ai aussi organisé un &lt;i&gt;live coding&lt;/i&gt;, passant de données écrites en dur dans le code, à des données provenant d&amp;#39;une base PostgreSQL dans un conteneur Docker, puis de &lt;a href=&quot;https://opendata.paris.fr/&quot;&gt;Paris Open Data&lt;/a&gt;. Quelque chose me dit que la partie démonstration / &lt;i&gt;live coding&lt;/i&gt; avait bien attiré l&amp;#39;attention, au vu du changement de posture dans le public.&lt;/p&gt;
&lt;!-- end paragraph 4f594ddf-36c9-491a-a6c9-ab6188a11930--&gt;
&lt;!-- begin paragraph 79b2674a-3912-4a73-aab1-cf6f05224535--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79b2674a-3912-4a73-aab1-cf6f05224535&quot;&gt;Difficile néanmoins d&amp;#39;avoir les avis des étudiants sur la session. Ce sont surtout les enseignants qui m&amp;#39;ont fait leurs retours. Ils ont en tout cas perçu l&amp;#39;intérêt d&amp;#39;intégrer la programmation fonctionnelle dans les cours, mais aussi de voir un peu autrement l&amp;#39;élaboration des projets à donner aux étudiants, grâce à cette démonstration autour de l&amp;#39;application Web.&lt;/p&gt;
&lt;!-- end paragraph 79b2674a-3912-4a73-aab1-cf6f05224535--&gt;
&lt;!-- begin paragraph e7aff0ce-e683-4514-a525-616af7640b68--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e7aff0ce-e683-4514-a525-616af7640b68&quot;&gt;De cette expérience, il me paraît important de conserver une relation avec les institutions d&amp;#39;enseignement supérieur quel qu&amp;#39;ils soient, dans la mesure où ils sont responsables de la formation des développeurs qui renouvelleront le marché de l&amp;#39;emploi. En partageant, nos points de vue, nous participons à l&amp;#39;amélioration de l&amp;#39;enseignement. Il apparaît aussi qu&amp;#39;une bonne démonstration vaut mieux qu&amp;#39;un long discours ! Ça fonctionne non seulement dans les conférences communautaires, mais aussi auprès de n&amp;#39;importe quel public sensible au code et à ce qu&amp;#39;il apporte.&lt;/p&gt;
&lt;!-- end paragraph e7aff0ce-e683-4514-a525-616af7640b68--&gt;
&lt;!-- begin paragraph ce91cac5-9d49-4e4e-88ab-9d35241dbea4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ce91cac5-9d49-4e4e-88ab-9d35241dbea4&quot;&gt;En tout cas, j&amp;#39;espère bien renouveler l&amp;#39;expérience !&lt;/p&gt;
&lt;!-- end paragraph ce91cac5-9d49-4e4e-88ab-9d35241dbea4--&gt;
&lt;!-- begin divider 2ea634a1-975d-45f9-99b9-2bf6c244b717--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-2ea634a1-975d-45f9-99b9-2bf6c244b717&quot;&gt;
&lt;!-- end divider 2ea634a1-975d-45f9-99b9-2bf6c244b717--&gt;
&lt;!-- begin paragraph a9decae3-e137-4e94-8bd9-779fd9217f22--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a9decae3-e137-4e94-8bd9-779fd9217f22&quot;&gt;Je tiens à remercier l&amp;#39;Université Paris Nanterre, ainsi que Pascal Poizat et Lom Messan Hillah, tous deux enseignants-chercheurs, pour la mise en place de tels séminaires et pour l&amp;#39;accueil qui m&amp;#39;a été réservé.&lt;/p&gt;
&lt;!-- end paragraph a9decae3-e137-4e94-8bd9-779fd9217f22--&gt;
&lt;!-- begin paragraph 5e67866c-7ec5-4931-ba2b-ed4e7dfa5244--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e67866c-7ec5-4931-ba2b-ed4e7dfa5244&quot;&gt;Voici les supports qui ont servi lors du séminaire :&lt;/p&gt;
&lt;!-- end paragraph 5e67866c-7ec5-4931-ba2b-ed4e7dfa5244--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1sqU0kjQ0fK9uANFsXDkQARK_3H2hQuWnDXx4zXxseRw/edit?usp=sharing&quot;&gt;Slides&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/fsarradin/aboutfp&quot;&gt;Code source&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:ba1f971f0bdc4b2a965c2d0b17cc4c44</id>
    <title>Kafka : synthèse</title>
    <updated>2019-01-07T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/kafka-synthese.html"/>
    <!--summary Kafka est devenu un incontournable de la sphère Big Data. Pour ceux ayant passé à côté, il n&#039;est pas trop tard pour mettre la main à la pâte notamment grâce à cet article plus que complet.-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Kafka"></category>    <category term="Kafka Streams"></category>    <category term="Kafka Connect"></category>    <content type="html">
&lt;!-- begin paragraph 9bec69a8-8c85-4cd6-b426-509832a6e1ca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9bec69a8-8c85-4cd6-b426-509832a6e1ca&quot;&gt;Kafka 0.10&lt;/p&gt;
&lt;!-- end paragraph 9bec69a8-8c85-4cd6-b426-509832a6e1ca--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Kafka Streams&lt;/li&gt;&lt;li&gt;Rack awareness&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 06010d00-4dff-4a12-9b4e-cb248ba70207--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06010d00-4dff-4a12-9b4e-cb248ba70207&quot;&gt;Kafka 0.11&lt;/p&gt;
&lt;!-- end paragraph 06010d00-4dff-4a12-9b4e-cb248ba70207--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Record headers&lt;/li&gt;&lt;li&gt;Exactly-once semantic&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph bed9b563-3d2e-48d0-8982-fbf29e485817--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bed9b563-3d2e-48d0-8982-fbf29e485817&quot;&gt;Kafka 1.0&lt;/p&gt;
&lt;!-- end paragraph bed9b563-3d2e-48d0-8982-fbf29e485817--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Améliorations diverses&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph e5ce4b14-67ae-47d0-a9ca-f58adfea904d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e5ce4b14-67ae-47d0-a9ca-f58adfea904d&quot;&gt;Kafka 2.0&lt;/p&gt;
&lt;!-- end paragraph e5ce4b14-67ae-47d0-a9ca-f58adfea904d--&gt;
&lt;!-- begin paragraph 2dee1704-b530-4a3a-85bd-01af6d5d0874--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2dee1704-b530-4a3a-85bd-01af6d5d0874&quot;&gt;+ ACLs, sécurité&lt;/p&gt;
&lt;!-- end paragraph 2dee1704-b530-4a3a-85bd-01af6d5d0874--&gt;
&lt;!-- begin paragraph 5a7a6bd1-7ad1-4668-8fdf-abc81b893a2b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5a7a6bd1-7ad1-4668-8fdf-abc81b893a2b&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 5a7a6bd1-7ad1-4668-8fdf-abc81b893a2b--&gt;&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player youtube&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/lIGFH9TkL2w?autoplay=0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;(CEO of DataCumulus, une boite parisienne, fondée en juillet 2018, spécialisé en Kafka + AWS)&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 1f5a983d-c4a7-447b-940d-d02503b942ee--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1f5a983d-c4a7-447b-940d-d02503b942ee&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1f5a983d-c4a7-447b-940d-d02503b942ee--&gt;
&lt;!-- begin heading_1 1fe96a5e-0756-4347-a83c-d5b2651f252f--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1fe96a5e-0756-4347-a83c-d5b2651f252f&quot;&gt;Introduction&lt;/h2&gt;
&lt;!-- end heading_1 1fe96a5e-0756-4347-a83c-d5b2651f252f--&gt;
&lt;!-- begin paragraph 477e0988-e820-47b1-a037-1fe297edd7a5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-477e0988-e820-47b1-a037-1fe297edd7a5&quot;&gt;Alors que dans la plupart des systèmes d&amp;#39;information, la donnée est vue comme un élément sujet au cours du temps à de nombreuses modifications et qu&amp;#39;il faut entreposer sous toutes ses formes (en ajoutant en plus des couches de caches pour des raisons de performances et des couches d&amp;#39;audit pour savoir par qui, quand et comment la donnée a été modifiée) et que les inévitables migrations de ces entrepôts sont perçues comme des projets pharaoniques voire insolubles, il y a des solutions qui vous proposent de sortir de ce carcan et de vous offrir une vision en flux, permettant de fournir une plus grande flexibilité dans les systèmes d&amp;#39;information. Avec nombre d&amp;#39;avantages complétant cette vision, Kafka est de ces solutions.&lt;/p&gt;
&lt;!-- end paragraph 477e0988-e820-47b1-a037-1fe297edd7a5--&gt;
&lt;!-- begin paragraph 2dc4a2e8-1ed2-43e5-bed8-47449234413b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2dc4a2e8-1ed2-43e5-bed8-47449234413b&quot;&gt;Depuis sa sortie en 2011 au sein de LinkedIn, Kafka est devenu une plateforme hautement recommandée, voire quasiment incontournable, dans le monde de la data. Open Source via le modèle de la fondation Apache (comme Hadoop, Spark ou Cassandra) et fortement soutenu par Confluent (la société fondée par les créateurs de Kafka), Kafka a déjà des adeptes importants comme &lt;a href=&quot;https://quickbooks-engineering.intuit.com/lessons-learnt-from-netflix-keystone-pipeline-with-trillions-of-daily-messages-64cc91b3c8ea&quot;&gt;Netflix&lt;/a&gt;, &lt;a href=&quot;https://eng.uber.com/reliable-reprocessing/&quot;&gt;Uber&lt;/a&gt;, &lt;a href=&quot;https://blog.twitter.com/engineering/en_us/topics/insights/2018/twitters-kafka-adoption-story.html&quot;&gt;Twitter&lt;/a&gt; ou &lt;a href=&quot;https://www.infoq.com/articles/resilient-kafka-goldman-sachs&quot;&gt;Goldman Sachs&lt;/a&gt;. Et en Europe, il y a &lt;a href=&quot;https://www.confluent.io/kafka-summit-sf17/Infrastructure-for-Streaming-Applications-in-Criteo&quot;&gt;Criteo&lt;/a&gt;, &lt;a href=&quot;https://www.confluent.io/kafka-summit-london18/the-evolution-of-kafka-at-ing-bank&quot;&gt;ING&lt;/a&gt;, &lt;a href=&quot;https://www.lemagit.fr/etude/Comment-Natixis-booste-les-performances-de-ses-clusters-Hadoop&quot;&gt;Natixis&lt;/a&gt;, EDF, etc.&lt;/p&gt;
&lt;!-- end paragraph 2dc4a2e8-1ed2-43e5-bed8-47449234413b--&gt;
&lt;!-- begin paragraph b7d858fb-081f-4109-a7ab-d967b0c6faa5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b7d858fb-081f-4109-a7ab-d967b0c6faa5&quot;&gt;Kafka est &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;librement téléchargeable&lt;/a&gt;. Mais ce framework est aussi inclue à des distributions Hadoop. Par exemple, Hortonworks le propose dans son offre &lt;a href=&quot;https://fr.hortonworks.com/products/data-platforms/hdf/&quot;&gt;DataFlow (HDF)&lt;/a&gt;. Nous pouvons aussi trouver Kafka en tant que service managé auprès des &lt;i&gt;cloud providers&lt;/i&gt;. Cette plateforme est ainsi disponible chez &lt;a href=&quot;https://www.confluent.io/confluent-cloud/&quot;&gt;Confluent&lt;/a&gt; (avec une plateforme dédiée) et &lt;a href=&quot;https://aws.amazon.com/msk/&quot;&gt;AWS&lt;/a&gt;. &lt;/p&gt;
&lt;!-- end paragraph b7d858fb-081f-4109-a7ab-d967b0c6faa5--&gt;
&lt;!-- begin paragraph e645528d-36d5-4ca1-a5f8-f98c3e6fca1f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e645528d-36d5-4ca1-a5f8-f98c3e6fca1f&quot;&gt;Ce qui fait la particularité de Kafka, ce qui le différencie des approches alternatives, c&amp;#39;est que Kafka impose une vision distribuée et hautement disponible, tout en proposant une API bas niveau — ainsi, il est facile de rentrer dans les méandres de la plateforme et de son architecture. Pour paraphraser Confluent, il s&amp;#39;agit d&amp;#39;un gage de performance.&lt;/p&gt;
&lt;!-- end paragraph e645528d-36d5-4ca1-a5f8-f98c3e6fca1f--&gt;
&lt;!-- begin paragraph 541e4ea1-4530-45c4-b60b-967783492c6a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-541e4ea1-4530-45c4-b60b-967783492c6a&quot;&gt;Nous allons dans cet article revenir les différentes utilisations de Kafka, puis nous verrons l&amp;#39;architecture de Kafka et observer assez rapidement son écosystème, tout en nous intéressant aux apports.&lt;/p&gt;
&lt;!-- end paragraph 541e4ea1-4530-45c4-b60b-967783492c6a--&gt;
&lt;!-- begin heading_1 d82ae03b-cbb9-4f4a-8c2f-31f9002f41c2--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d82ae03b-cbb9-4f4a-8c2f-31f9002f41c2&quot;&gt;Quels sont les usages de Kafka ?&lt;/h2&gt;
&lt;!-- end heading_1 d82ae03b-cbb9-4f4a-8c2f-31f9002f41c2--&gt;
&lt;!-- begin paragraph dcbf55b8-bace-447f-a5bb-3ee1763ce0ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dcbf55b8-bace-447f-a5bb-3ee1763ce0ed&quot;&gt;Il y a autour de Kafka plusieurs cas d&amp;#39;utilisation possibles. Son principal usage est lié à sa capacité à transférer de la donnée même si son volume est important, avec un haut niveau de fiabilité entre des services répartis. En se basant sur des flux nommés de données découpés en messages, autour desquels des services peuvent envoyer et d&amp;#39;autres services peuvent se mettre en attentes d&amp;#39;un envoie, en proposant de répartir et répliquer ces flux sur plusieurs machines, Kafka facilite la mise en place de systèmes répartis.&lt;/p&gt;
&lt;!-- end paragraph dcbf55b8-bace-447f-a5bb-3ee1763ce0ed--&gt;
&lt;!-- begin paragraph 85dc82aa-9dbc-4709-b4ea-d2b45108ab19--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-85dc82aa-9dbc-4709-b4ea-d2b45108ab19&quot;&gt;En effet, d&amp;#39;un côté, Kafka assure à la fois une transmission asynchrone des données entre les services. Ainsi, lorsque le développeur crée un service, il n&amp;#39;a pas nécessairement besoin de passer par du temps sur ces aspects. D&amp;#39;un autre côté, Kafka offre une haute disponibilité de son service et des données qu&amp;#39;il gère. Cet aspect possible grâce au mécanisme de répartition / réplication.&lt;/p&gt;
&lt;!-- end paragraph 85dc82aa-9dbc-4709-b4ea-d2b45108ab19--&gt;
&lt;!-- begin paragraph 36946efb-f2a7-4251-a730-4aa597d2eed9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-36946efb-f2a7-4251-a730-4aa597d2eed9&quot;&gt;Il facilite aussi une vision alternative sur la donnée gérée par une entreprise : une vision unifiée dans laquelle nous accordons une importance à la transmission des événements modifiant les données, plus qu&amp;#39;au stockage d&amp;#39;un état mutable. L&amp;#39;état est ainsi reconstruit à partir de ces événements. Cette approche offre plus de flexibilité sur l&amp;#39;organisation d&amp;#39;une application ou d&amp;#39;un système d&amp;#39;information, avec la capacité conserver explicitement l&amp;#39;historique de la donnée ou de réinterpréter les événements pour en extraire d&amp;#39;autres données ou apporter des corrections.&lt;/p&gt;
&lt;!-- end paragraph 36946efb-f2a7-4251-a730-4aa597d2eed9--&gt;
&lt;!-- begin paragraph 3db018fa-6a6a-45a2-bb67-dd3d30cc74ce--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3db018fa-6a6a-45a2-bb67-dd3d30cc74ce&quot;&gt;Néanmoins, malgré les facilités offertes par Kafka, il est nécessaire de garder à l&amp;#39;esprit que nous sommes en présence d&amp;#39;un système réparti et que la conception d&amp;#39;un tel système repose sur un découplage des différents services qui le compose. Autre point, Zookeeper est une dépendance sur laquelle Kafka se base pour gérer son fonctionnement. Déployer Kafka revient donc à déployer deux services et Zookeeper n&amp;#39;est nécessairement le plus simple dans cette catégorie. Enfin, il y a bien évidemment des cas où Kafka sera de trop, en particulier si les volumes sont faibles ou les producteurs ou consommateurs autour de la données sont peu nombreux. Dans ces cas, une simple base de données relationnelle peut suffire.&lt;/p&gt;
&lt;!-- end paragraph 3db018fa-6a6a-45a2-bb67-dd3d30cc74ce--&gt;
&lt;!-- begin heading_2 4bec7fe5-9570-405a-b8e2-0c663077b3b7--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4bec7fe5-9570-405a-b8e2-0c663077b3b7&quot;&gt;Dans le détail...&lt;/h3&gt;
&lt;!-- end heading_2 4bec7fe5-9570-405a-b8e2-0c663077b3b7--&gt;
&lt;!-- begin paragraph dc27a131-e2a2-4347-a879-b68d1605238d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dc27a131-e2a2-4347-a879-b68d1605238d&quot;&gt;Kafka a été créé par LinkedIn afin de faciliter et unifier la communication et la transmission de données dans le cadre de la transition vers une architecture microservices. Kafka apparaît donc comme un MOM (&lt;i&gt;Message Oriented Middleware&lt;/i&gt;) dans ce cadre.&lt;/p&gt;
&lt;!-- end paragraph dc27a131-e2a2-4347-a879-b68d1605238d--&gt;
&lt;!-- begin paragraph e012c273-cfd7-4bba-8463-87e0eed3276a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e012c273-cfd7-4bba-8463-87e0eed3276a&quot;&gt;Depuis sa version 0.10, Kafka propose une API de traitement de données en flux, appelée Kafka Streams, et une API d&amp;#39;intégration, appelée Kafka Connect. Ces API facilitent la mise en place de chaîne de traitements, notamment dans le cadre de traitements BigData ou sur des architectures microservices.&lt;/p&gt;
&lt;!-- end paragraph e012c273-cfd7-4bba-8463-87e0eed3276a--&gt;
&lt;!-- begin paragraph 9401ab80-74c6-47b9-a351-9d8ad824eca2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9401ab80-74c6-47b9-a351-9d8ad824eca2&quot;&gt;Kafka peut aussi être utilisé pour agréger les logs provenant de plusieurs applications, par exemple dans le cadre d&amp;#39;une application microservices. Certains frameworks de log permettent de transmettre les logs directement dans Kafka.&lt;/p&gt;
&lt;!-- end paragraph 9401ab80-74c6-47b9-a351-9d8ad824eca2--&gt;
&lt;!-- begin paragraph c3ba0cc0-a374-40c8-acb6-d452ea70a52c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c3ba0cc0-a374-40c8-acb6-d452ea70a52c&quot;&gt;De ces premiers cas d&amp;#39;usage, il est possible de constituer assez simplement des fonctionnalités de type &lt;i&gt;analytics&lt;/i&gt;, de système de mise à jour de vues, d&amp;#39;éviction de caches...&lt;/p&gt;
&lt;!-- end paragraph c3ba0cc0-a374-40c8-acb6-d452ea70a52c--&gt;
&lt;!-- begin paragraph c39543e6-179f-4c84-96bd-180044b0c1a0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c39543e6-179f-4c84-96bd-180044b0c1a0&quot;&gt;Kafka est un framework basé sur le concept de &lt;i&gt;commit-log&lt;/i&gt; ou journal de &lt;i&gt;commits&lt;/i&gt;. Dans le cadre d&amp;#39;une base de données, un &lt;i&gt;commit&lt;/i&gt; correspond à l&amp;#39;étape d&amp;#39;&amp;quot;engagement&amp;quot; des opérations impliquées dans une transaction. Il existe des cas d&amp;#39;utilisation où Kafka est adossé à une base de données, afin de récupérer l&amp;#39;ensemble des modifications sur cette base. Ceci est réalisé dans un but de réplication de la donnée et aussi afin de pouvoir reconstruire la base en cas de panne.&lt;/p&gt;
&lt;!-- end paragraph c39543e6-179f-4c84-96bd-180044b0c1a0--&gt;
&lt;!-- begin paragraph aaf5bae3-8e88-475c-b34d-7f6e73b4bef5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aaf5bae3-8e88-475c-b34d-7f6e73b4bef5&quot;&gt;En partant de ce cas d&amp;#39;usage, il est possible de transformer Kafka en base de type &lt;i&gt;event source&lt;/i&gt;. Dans ce cadre, ce qui est stocké ce n&amp;#39;est pas l&amp;#39;état de la donnée, mais l&amp;#39;ensemble des événements ayant participé à modifier cette donnée. L&amp;#39;état est alors déduit à partir de cet ensemble d&amp;#39;événements. Kafka n&amp;#39;est pas le meilleur outil pour l&amp;#39;&lt;i&gt;event sourcing&lt;/i&gt;. Une base comme &lt;a href=&quot;https://eventstore.org/&quot;&gt;Event Store&lt;/a&gt; sera plus adaptée. Mais, dans la mesure où il est déjà en place, Kafka permet d&amp;#39;explorer ce paradigme.&lt;/p&gt;
&lt;!-- end paragraph aaf5bae3-8e88-475c-b34d-7f6e73b4bef5--&gt;
&lt;!-- begin heading_1 77e61e4f-8ce5-4789-b31a-3e816adb9e35--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-77e61e4f-8ce5-4789-b31a-3e816adb9e35&quot;&gt;Architecture et fonctionnement&lt;/h2&gt;
&lt;!-- end heading_1 77e61e4f-8ce5-4789-b31a-3e816adb9e35--&gt;
&lt;!-- begin paragraph 48161bd0-9649-4cb5-8dc2-1e993d5fc368--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-48161bd0-9649-4cb5-8dc2-1e993d5fc368&quot;&gt;Kafka est avant tout une plateforme permettant de transférer des messages, aussi appelés des &lt;i&gt;logs&lt;/i&gt;, entre différents processus. Il s&amp;#39;apparente en ce sens à d&amp;#39;autres frameworks comme RabbitMQ ou ZeroMQ, ou des solutions Cloud comme PubSub chez Google et Kinesis chez AWS.&lt;/p&gt;
&lt;!-- end paragraph 48161bd0-9649-4cb5-8dc2-1e993d5fc368--&gt;
&lt;!-- begin heading_2 3db8c6cb-a62b-40c9-a61e-57d7a0ca4f76--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-3db8c6cb-a62b-40c9-a61e-57d7a0ca4f76&quot;&gt;Architecture&lt;/h3&gt;
&lt;!-- end heading_2 3db8c6cb-a62b-40c9-a61e-57d7a0ca4f76--&gt;
&lt;!-- begin paragraph 920e9762-1d9d-472b-93d6-441a69434f90--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-920e9762-1d9d-472b-93d6-441a69434f90&quot;&gt;Il y a deux points de vue avec Kafka : la vue physique qui concerne l&amp;#39;intégration avec les infrastructures d&amp;#39;un cluster et la vue logique qui concerne l&amp;#39;organisation que fait apparaître la plateforme afin de permettre la transmission de messages.&lt;/p&gt;
&lt;!-- end paragraph 920e9762-1d9d-472b-93d6-441a69434f90--&gt;
&lt;!-- begin paragraph 79f01f79-1711-4405-9edd-34a4aca034ff--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-79f01f79-1711-4405-9edd-34a4aca034ff&quot;&gt;D&amp;#39;un point de vue physique, de base les instances de Kafka sont réparties dans un cluster. Les instances de Kafka, appelées &lt;i&gt;&lt;b&gt;brokers&lt;/b&gt;&lt;/i&gt;, sont réparties sur plusieurs machines. Elles permettent à la fois de répartir la charge et de répliquer les messages.&lt;/p&gt;
&lt;!-- end paragraph 79f01f79-1711-4405-9edd-34a4aca034ff--&gt;
&lt;!-- begin paragraph 67f1c7c2-0a9d-48ad-93b0-eb92ecfd1b86--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67f1c7c2-0a9d-48ad-93b0-eb92ecfd1b86&quot;&gt;D&amp;#39;un point de vue logique, les messages sont transmis sur des &lt;i&gt;&lt;b&gt;topics&lt;/b&gt;&lt;/i&gt; nommés. Un &lt;i&gt;topic&lt;/i&gt; va être constitué de plusieurs &lt;b&gt;partitions&lt;/b&gt;, réparties dans les différents &lt;i&gt;brokers&lt;/i&gt;. Au niveau d&amp;#39;un &lt;i&gt;topic&lt;/i&gt;, des processus vont jouer le rôle de &lt;b&gt;producteur&lt;/b&gt; pour ceux qui envoient des messages et d&amp;#39;autres processus vont jouer le rôle de &lt;b&gt;consommateur&lt;/b&gt; pour ceux qui attendent la réception de messages.&lt;/p&gt;
&lt;!-- end paragraph 67f1c7c2-0a9d-48ad-93b0-eb92ecfd1b86--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;figure class=&quot;figure&quot;&gt;&lt;img src=&quot;/blog/assets/ba1f971f-0bdc-4b2a-965c-2d0b17cc4c44/kafkaarchitecture.png&quot; alt=&quot;&quot; /&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Cluster Kafka de trois &lt;i&gt;brokers&lt;/i&gt; avec deux &lt;i&gt;topics&lt;/i&gt; de deux partitions chacun et un facteur de réplication de trois.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin paragraph 707fc04d-a356-4285-bfd4-abba8748283b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-707fc04d-a356-4285-bfd4-abba8748283b&quot;&gt;Kafka se base sur Zookeeper pour gérer une partie de son fonctionnement (liste des &lt;i&gt;topics&lt;/i&gt;, &lt;i&gt;offset&lt;/i&gt;...). Une migration est prévue pour intégrer l&amp;#39;algorithme de consensus &lt;a href=&quot;vers%20l%27algorithme%20de%20consensus%20Raft&quot;&gt;Raft&lt;/a&gt; afin de rendre Kafka plus indépendant.&lt;/p&gt;
&lt;!-- end paragraph 707fc04d-a356-4285-bfd4-abba8748283b--&gt;
&lt;!-- begin heading_2 1b008109-9012-4c0a-9ab6-8d805c82e2f5--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-1b008109-9012-4c0a-9ab6-8d805c82e2f5&quot;&gt;Fonctionnement&lt;/h3&gt;
&lt;!-- end heading_2 1b008109-9012-4c0a-9ab6-8d805c82e2f5--&gt;
&lt;!-- begin paragraph 9bdaa3fc-6ca7-482d-bc5e-9406591e2346--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9bdaa3fc-6ca7-482d-bc5e-9406591e2346&quot;&gt;La connexion à Kafka se fait en créant un &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/producer/KafkaProducer.html&quot;&gt;producteur&lt;/a&gt; ou un &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html&quot;&gt;consommateur&lt;/a&gt; et en passant en paramètre un ensemble de propriétés. Il faut au minimum préciser l&amp;#39;ensemble des &lt;i&gt;brokers&lt;/i&gt; qui seront utilisés, ainsi que les outils de sérialisation qui seront utilisés pour la clé et la donnée dans les messages. Les &lt;b&gt;messages&lt;/b&gt; apparaissent en effet sous la forme clé-valeur, avec la possibilité d&amp;#39;ajouter une en-tête depuis la version 0.11.&lt;/p&gt;
&lt;!-- end paragraph 9bdaa3fc-6ca7-482d-bc5e-9406591e2346--&gt;
&lt;!-- begin paragraph a9b5f57f-7462-4d5b-b3e6-e4ba7559d5ca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a9b5f57f-7462-4d5b-b3e6-e4ba7559d5ca&quot;&gt;Pour le consommateur, il faut en plus préciser un &lt;i&gt;group ID&lt;/i&gt; pour indiquer dans quel groupe se trouve le consommateur. Ce &lt;i&gt;&lt;b&gt;group ID&lt;/b&gt;&lt;/i&gt; est surtout utilisé lorsque plusieurs consommateurs sont utilisés en parallèle. Si des consommateurs sont dans le même groupe, ils sont en compétition pour consommer les messages. Dans ce cas, si un consommateur récupère un message, les autres consommateurs du groupe auront les message suivants. Si les consommateurs sont dans des groupes séparés, alors ils fonctionnent en parallèle pour consommer les messages. Dans ce cas, si un consommateur d&amp;#39;un groupe récupère un message, les consommateurs des autres groupes pourront aussi récupérer ce même message s&amp;#39;il ne l&amp;#39;ont pas déjà fait.&lt;/p&gt;
&lt;!-- end paragraph a9b5f57f-7462-4d5b-b3e6-e4ba7559d5ca--&gt;
&lt;!-- begin paragraph aef4751b-d238-471f-831f-db7c49f0f966--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aef4751b-d238-471f-831f-db7c49f0f966&quot;&gt;L&amp;#39;envoi d&amp;#39;un message par un producteur se fait en utilisant la méthode &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/producer/KafkaProducer.html#send-org.apache.kafka.clients.producer.ProducerRecord-&quot;&gt;send()&lt;/a&gt;. Cette méthode prend en paramètre un &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/producer/ProducerRecord.html&quot;&gt;ProducerRecord&lt;/a&gt;, qui doit préciser au minimum le &lt;i&gt;topic&lt;/i&gt; et le contenu du message. Il est possible de spécifier la clé du message, la partition, des en-têtes et un &lt;i&gt;timestamp&lt;/i&gt;. Depuis Kafka 0.11, il est possible de regrouper des messages dans des transactions. Dans une transaction, les messages peuvent être destinés à plusieurs partitions d&amp;#39;un même &lt;i&gt;topic&lt;/i&gt; et aussi à plusieurs &lt;i&gt;topic&lt;/i&gt;. Les transactions sont atomiques et il est possible de les interrompre (comme pour une base de données relationnelle).&lt;/p&gt;
&lt;!-- end paragraph aef4751b-d238-471f-831f-db7c49f0f966--&gt;
&lt;!-- begin paragraph f24ae6bc-1e33-483e-a646-712d29b190c7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f24ae6bc-1e33-483e-a646-712d29b190c7&quot;&gt;Pour qu&amp;#39;un consommateur reçoivent des messages, il faut que celui-ci &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html#subscribe-java.util.Collection-&quot;&gt;souscrive&lt;/a&gt; à un ou plusieurs &lt;i&gt;topics&lt;/i&gt; (il est possible de spécifier ces &lt;i&gt;topics&lt;/i&gt; en fournissant une expression régulière). L&amp;#39;attente d&amp;#39;un message se fait avec la méthode &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html#poll-java.time.Duration-&quot;&gt;poll()&lt;/a&gt;, qui retourne un &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/consumer/ConsumerRecords.html&quot;&gt;ConsumerRecords&lt;/a&gt; représentant l&amp;#39;ensemble des messages récupérés depuis les &lt;i&gt;topics&lt;/i&gt; souscrits. Il faut nécessairement indiquer un temps d&amp;#39;attente maximal. Si ce temps est dépassé, poll() retourne une structure vide. Un ConsumerRecords est composé de &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/consumer/ConsumerRecord.html&quot;&gt;ConsumerRecord&lt;/a&gt;. Dans une telle structure, il est possible de récupérer les informations transmises au niveau du producteur, ainsi que l&amp;#39;&lt;i&gt;offset&lt;/i&gt; du message dans une partition donnée.&lt;/p&gt;
&lt;!-- end paragraph f24ae6bc-1e33-483e-a646-712d29b190c7--&gt;
&lt;!-- begin heading_2 f1c45d5b-89d6-47cf-ab8e-391612ebd5ba--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-f1c45d5b-89d6-47cf-ab8e-391612ebd5ba&quot;&gt;Fonctionnement d&amp;#39;un &lt;i&gt;topic&lt;/i&gt;&lt;/h3&gt;
&lt;!-- end heading_2 f1c45d5b-89d6-47cf-ab8e-391612ebd5ba--&gt;
&lt;!-- begin paragraph 02f476da-b570-4aac-afa9-39d9fa0a0501--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-02f476da-b570-4aac-afa9-39d9fa0a0501&quot;&gt;Nous avons vu qu&amp;#39;un &lt;i&gt;topic&lt;/i&gt; est composé de partitions. Chaque partition est en fait un fichier sur disque. Il est en ajout seulement pour le producteur et en lecture seul pour le consommateur. Chaque message est ajouté à la fin du fichier et se voit attribué un numéro séquentiel appelé &lt;i&gt;offset&lt;/i&gt;, indiquant le numéro d&amp;#39;arrivée du message dans la partition.&lt;/p&gt;
&lt;!-- end paragraph 02f476da-b570-4aac-afa9-39d9fa0a0501--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/ba1f971f-0bdc-4b2a-965c-2d0b17cc4c44/kafkatopic.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 7a563a9f-9ca1-4746-88f2-2b436398b2d8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7a563a9f-9ca1-4746-88f2-2b436398b2d8&quot;&gt;L&amp;#39;ordre des messages est garanti au niveau d&amp;#39;une partition. Par contre, il n&amp;#39;est pas garanti au niveau d&amp;#39;un &lt;i&gt;topic&lt;/i&gt;, si celui-ci contient plusieurs partitions.&lt;/p&gt;
&lt;!-- end paragraph 7a563a9f-9ca1-4746-88f2-2b436398b2d8--&gt;
&lt;!-- begin heading_2 ab5f8608-4776-41f5-8812-655585d2dee3--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-ab5f8608-4776-41f5-8812-655585d2dee3&quot;&gt;Pour résumer&lt;/h3&gt;
&lt;!-- end heading_2 ab5f8608-4776-41f5-8812-655585d2dee3--&gt;
&lt;!-- begin paragraph 0981ef58-2e21-4cd0-abba-93f3ff8fcbaa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0981ef58-2e21-4cd0-abba-93f3ff8fcbaa&quot;&gt;La persistance est donc au cœur de la plateforme, car Kafka se base principalement sur de la gestion de fichiers. Sur ce point, les performances de Kafka sont du coup directement dépendante du matériel utilisé, de la gestion de ce matériel par le hardware et aussi la gestion de ce matériel par l&amp;#39;OS. Il faut aussi savoir que le moindre message transmis sur le réseau est d&amp;#39;abord enregistré sur disque.&lt;/p&gt;
&lt;!-- end paragraph 0981ef58-2e21-4cd0-abba-93f3ff8fcbaa--&gt;
&lt;!-- begin paragraph 5457cb2d-a053-4016-9a56-41d414ab38e5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5457cb2d-a053-4016-9a56-41d414ab38e5&quot;&gt;La tolérance à la panne est assurée à travers la réplication des partitions entre les &lt;i&gt;brokers&lt;/i&gt;. Si une machine tombe en panne, les opérations sont reprises au niveau d&amp;#39;un réplicat, qui devient &lt;i&gt;lead&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 5457cb2d-a053-4016-9a56-41d414ab38e5--&gt;
&lt;!-- begin heading_1 a8a77edc-9db0-4fea-ba28-7e0ab84d18ae--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-a8a77edc-9db0-4fea-ba28-7e0ab84d18ae&quot;&gt;Écosystème de Kafka&lt;/h2&gt;
&lt;!-- end heading_1 a8a77edc-9db0-4fea-ba28-7e0ab84d18ae--&gt;
&lt;!-- begin paragraph d5bc82ee-a350-4126-895c-fb3381eaeaac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d5bc82ee-a350-4126-895c-fb3381eaeaac&quot;&gt;Il existe tout un écosystème autour de Kafka afin de le spécialiser autour de différents usages (gestion de flux de données, connexion à des services externes, gestion des schémas...). Une partie de cet écosystème est fourni avec la plateforme Confluent. Nous n&amp;#39;aborderons pas cette plateforme ici et nous verrons juste Kafka Streams et Kafka Connect disponibles librement.&lt;/p&gt;
&lt;!-- end paragraph d5bc82ee-a350-4126-895c-fb3381eaeaac--&gt;
&lt;!-- begin heading_2 4529d5c1-3093-4d60-8557-c9a9560d2f8d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-4529d5c1-3093-4d60-8557-c9a9560d2f8d&quot;&gt;Kafka Streams&lt;/h3&gt;
&lt;!-- end heading_2 4529d5c1-3093-4d60-8557-c9a9560d2f8d--&gt;
&lt;!-- begin paragraph 88a284ad-ec3e-4152-b6af-fb5fbade7e56--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-88a284ad-ec3e-4152-b6af-fb5fbade7e56&quot;&gt;Kafka Streams est un framework qui permet de faciliter le &lt;i&gt;stream processing&lt;/i&gt;. Nous sont ainsi fourni à travers une API des transformations, voire des outils pour remodeler les données gérées par Kafka entre différents &lt;i&gt;topics&lt;/i&gt;. Plus précisément en terme d&amp;#39;opération, il est possible de modifier les &lt;i&gt;logs&lt;/i&gt; un à un (map), d&amp;#39;ajouter des &lt;i&gt;logs&lt;/i&gt; (flatmap), de les filtrer, de regrouper des &lt;i&gt;logs&lt;/i&gt; selon une clé donnée (group-by) et même de fusionner des &lt;i&gt;topics&lt;/i&gt; (join).&lt;/p&gt;
&lt;!-- end paragraph 88a284ad-ec3e-4152-b6af-fb5fbade7e56--&gt;
&lt;!-- begin heading_2 49336284-6134-47c3-9d66-bccc90ac4b0d--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-49336284-6134-47c3-9d66-bccc90ac4b0d&quot;&gt;Kafka Connect&lt;/h3&gt;
&lt;!-- end heading_2 49336284-6134-47c3-9d66-bccc90ac4b0d--&gt;
&lt;!-- begin paragraph 4bd94b45-e518-4344-a811-47868f5a0912--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4bd94b45-e518-4344-a811-47868f5a0912&quot;&gt;Kafka Connect est un framework permettant de transférer des données en flux entre différents systèmes, en se basant sur Kafka. En terme de système, il peut s&amp;#39;agir de SGBDR, de base NoSQL, de récupération de logs, de stockage sur le Cloud, etc.&lt;/p&gt;
&lt;!-- end paragraph 4bd94b45-e518-4344-a811-47868f5a0912--&gt;
&lt;!-- begin paragraph 4ca34fe7-6dda-44f6-a6c2-169dc3708684--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4ca34fe7-6dda-44f6-a6c2-169dc3708684&quot;&gt;Confluent fourni un &lt;i&gt;hub&lt;/i&gt; contenant différents connecteurs : &lt;a href=&quot;https://www.confluent.io/hub/&quot;&gt;https://www.confluent.io/hub/&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 4ca34fe7-6dda-44f6-a6c2-169dc3708684--&gt;
&lt;!-- begin heading_2 7687c9c2-e174-4749-a7ce-0d9c3f20db77--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-7687c9c2-e174-4749-a7ce-0d9c3f20db77&quot;&gt;À propos...&lt;/h3&gt;
&lt;!-- end heading_2 7687c9c2-e174-4749-a7ce-0d9c3f20db77--&gt;
&lt;!-- begin paragraph 78a653af-ca4c-4a7f-9d6d-0dbb0059e169--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-78a653af-ca4c-4a7f-9d6d-0dbb0059e169&quot;&gt;Ensemble Kafka Streams, Kafka Connect et Kafka couvrent ainsi les besoins couverts par des frameworks comme Apache Camel ou Akka Stream et permettent de remplacer les ESB par une plateforme de stream processing et/ou orientée événements... En tout cas, là où les ESB représentent une approche trop lourde et peu flexible, c&amp;#39;est-à-dire dès qu&amp;#39;il s&amp;#39;agit juste de mettre à disposition des flux de données caractérisés. L&amp;#39;avantage qu&amp;#39;apportera Kafka, dès lors que son cas d&amp;#39;usage est respecté, sera de fournir en plus de la performance et de la haute disponibilité.&lt;/p&gt;
&lt;!-- end paragraph 78a653af-ca4c-4a7f-9d6d-0dbb0059e169--&gt;
&lt;!-- begin heading_1 4df105f5-3c19-4e17-be23-41c0971cae6f--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-4df105f5-3c19-4e17-be23-41c0971cae6f&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 4df105f5-3c19-4e17-be23-41c0971cae6f--&gt;
&lt;!-- begin paragraph 882b80c1-e466-4faa-8cbd-3d190c7a989e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-882b80c1-e466-4faa-8cbd-3d190c7a989e&quot;&gt;Kafka et son écosystème présentent ainsi de nombreux avantages pour mettre en place non seulement un bus de messages, mais aussi un outil de transport et transformation de données, en particulier avec des données en flux. Tout ceci est réalisé en bénéficiant des optimisations sous-jacentes à Kafka et aussi d&amp;#39;un système de persistance, de réplication et de partitionnement assurant une haute disponibilité de la plateforme.&lt;/p&gt;
&lt;!-- end paragraph 882b80c1-e466-4faa-8cbd-3d190c7a989e--&gt;
&lt;!-- begin paragraph 8394d721-1bf1-4452-9c78-c48b34a761fb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8394d721-1bf1-4452-9c78-c48b34a761fb&quot;&gt;Toutes ces particularités font de Kafka un outil à part entière, qui est certes générique, mais qui est capable de supplanter bon nombre d&amp;#39;outils spécialisés autour de la donnée, tant Kafka en plus d&amp;#39;être performant, apporte beaucoup de flexibilité.&lt;/p&gt;
&lt;!-- end paragraph 8394d721-1bf1-4452-9c78-c48b34a761fb--&gt;
&lt;!-- begin paragraph 0aa478c6-2ad9-4893-bf31-e2f5fe82e343--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0aa478c6-2ad9-4893-bf31-e2f5fe82e343&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0aa478c6-2ad9-4893-bf31-e2f5fe82e343--&gt;
&lt;!-- begin divider a222dfa0-b146-4bc2-adaa-43b3133f0680--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-a222dfa0-b146-4bc2-adaa-43b3133f0680&quot;&gt;
&lt;!-- end divider a222dfa0-b146-4bc2-adaa-43b3133f0680--&gt;
&lt;!-- begin heading_1 99a7d755-0701-4c48-89bf-19fe99d8b740--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-99a7d755-0701-4c48-89bf-19fe99d8b740&quot;&gt;Draft 0.1&lt;/h2&gt;
&lt;!-- end heading_1 99a7d755-0701-4c48-89bf-19fe99d8b740--&gt;
&lt;!-- begin paragraph 684ab55a-3d47-41e0-aeaf-a87992a41d7d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-684ab55a-3d47-41e0-aeaf-a87992a41d7d&quot;&gt;Kafka est sorti récemment en version 2.1. Cette version inclue un certains nombres de correctifs par rapport à la version 2.0, mais aussi l&amp;#39;intégration de Java 11 et la possibilité de un nouvel algorithme de compression développé par Facebook et plus rapide que GZip pour un taux de compression équivalent : ZStandard.&lt;/p&gt;
&lt;!-- end paragraph 684ab55a-3d47-41e0-aeaf-a87992a41d7d--&gt;
&lt;!-- begin paragraph 8c90d286-7f13-4f0a-b878-a4a550c9015c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c90d286-7f13-4f0a-b878-a4a550c9015c&quot;&gt;Cette article est l&amp;#39;occasion de revenir sur les dernières évolutions de Kafka par rapport aux versions encore assez utilisés dans les systèmes d&amp;#39;information actuels : nous allons pour cela partir de Kafka 0.10.&lt;/p&gt;
&lt;!-- end paragraph 8c90d286-7f13-4f0a-b878-a4a550c9015c--&gt;
&lt;!-- begin heading_1 446eda80-8e0e-49f2-a66a-4fc62a19dc71--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-446eda80-8e0e-49f2-a66a-4fc62a19dc71&quot;&gt;Le versioning dans Kafka&lt;/h2&gt;
&lt;!-- end heading_1 446eda80-8e0e-49f2-a66a-4fc62a19dc71--&gt;
&lt;!-- begin paragraph 5ad87c06-1946-4f4d-8dd3-e29a8b350191--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ad87c06-1946-4f4d-8dd3-e29a8b350191&quot;&gt;Les numéros de version de Kafka ne suivent pas exactement un schéma classique. Par exemple, les changements entre la 0.10 et la 0.11 sont plus important que les changements apparaissant entre la 0.11 et la 1.0, sachant que la 1.0 est plus une version de stabilisation. Autre exemple, les nouveautés la 2.1 crée un changement qui ne permet pas de revenir aux versions précédentes.&lt;/p&gt;
&lt;!-- end paragraph 5ad87c06-1946-4f4d-8dd3-e29a8b350191--&gt;
&lt;!-- begin heading_1 ac11d150-0b1c-4adc-a581-1d74099a5045--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-ac11d150-0b1c-4adc-a581-1d74099a5045&quot;&gt;Kafka 0.10&lt;/h2&gt;
&lt;!-- end heading_1 ac11d150-0b1c-4adc-a581-1d74099a5045--&gt;
&lt;!-- begin paragraph fd4168db-b791-42ba-a9af-b74ac9452090--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fd4168db-b791-42ba-a9af-b74ac9452090&quot;&gt;Jusqu&amp;#39;à la version 0.10, Kafka était déjà connu comme un framework de messaging distribué.&lt;/p&gt;
&lt;!-- end paragraph fd4168db-b791-42ba-a9af-b74ac9452090--&gt;
&lt;!-- begin paragraph fab92749-99e1-402f-a842-fc1efb5b3894--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fab92749-99e1-402f-a842-fc1efb5b3894&quot;&gt;Avec la version 0.10, Kafka devient aussi un framework de streaming.&lt;/p&gt;
&lt;!-- end paragraph fab92749-99e1-402f-a842-fc1efb5b3894--&gt;
&lt;!-- begin heading_1 47b4f20e-5828-4c8a-9f05-e672565c3a38--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-47b4f20e-5828-4c8a-9f05-e672565c3a38&quot;&gt;Kafka 0.11&lt;/h2&gt;
&lt;!-- end heading_1 47b4f20e-5828-4c8a-9f05-e672565c3a38--&gt;
&lt;!-- begin heading_2 179617fc-0fc9-4126-b14d-63754e631700--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-179617fc-0fc9-4126-b14d-63754e631700&quot;&gt;Header&lt;/h3&gt;
&lt;!-- end heading_2 179617fc-0fc9-4126-b14d-63754e631700--&gt;
&lt;!-- begin paragraph 308d9028-b3ce-4c60-a039-ba539d7661cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-308d9028-b3ce-4c60-a039-ba539d7661cf&quot;&gt;Dans un objectif de s&amp;#39;aligner avec la plupart des protocoles réseaux, comme HTTP ou TCP, ainsi que les systèmes de message, comme JMS ou QPID, la version 0.11 de Kafka permet d&amp;#39;ajouter un en-tête à chaque message. Un message est donc depuis cette version défini :&lt;/p&gt;
&lt;!-- end paragraph 308d9028-b3ce-4c60-a039-ba539d7661cf--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;par un corps, composé d&amp;#39;une clé et d&amp;#39;une valeur ;&lt;/li&gt;&lt;li&gt;par une en-tête, qui est un ensemble clé-valeur.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 630ed9eb-fc9c-4721-ab70-fc103cc42661--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-630ed9eb-fc9c-4721-ab70-fc103cc42661&quot;&gt;L&amp;#39;en-tête permet d&amp;#39;ajouter des méta-données aux messages dans différents objectifs, comme le routage automatique des messages entre différents cluster, l&amp;#39;intégration d&amp;#39;informations d&amp;#39;audit comme l&amp;#39;ID client, l&amp;#39;ID cluster où a été produit le message, etc. ou enfin, la mise en place de métriques.&lt;/p&gt;
&lt;!-- end paragraph 630ed9eb-fc9c-4721-ab70-fc103cc42661--&gt;
&lt;!-- begin paragraph e191779c-4871-4f22-85c5-92bb2a6bad24--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e191779c-4871-4f22-85c5-92bb2a6bad24&quot;&gt;Une en-tête est représentée par l&amp;#39;interface &lt;a href=&quot;https://kafka.apache.org/0110/javadoc/org/apache/kafka/common/header/Header.html&quot;&gt;Header&lt;/a&gt;. Dans celle-ci, la clé est sous forme d&amp;#39;une chaîne de caractères (&lt;code&gt;String&lt;/code&gt;) au format UTF-8 et le contenu sous forme de tableau d&amp;#39;octet (&lt;code&gt;byte[]&lt;/code&gt;). Ce qui permet d&amp;#39;y mettre toute sorte de données sérialisées.&lt;/p&gt;
&lt;!-- end paragraph e191779c-4871-4f22-85c5-92bb2a6bad24--&gt;
&lt;!-- begin paragraph 92213076-5cc2-4da0-8695-7ae07cd987ae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-92213076-5cc2-4da0-8695-7ae07cd987ae&quot;&gt;Les Header sont rassemblés dans une structure mutable appelé Headers. Il s&amp;#39;agit d&amp;#39;une interface dérivé de &lt;code&gt;Iterable&amp;lt;Header&amp;gt;&lt;/code&gt;. C&amp;#39;est cette structure qui rattachée aux Record depuis Kafka 0.11.&lt;/p&gt;
&lt;!-- end paragraph 92213076-5cc2-4da0-8695-7ae07cd987ae--&gt;
&lt;!-- begin paragraph e741f72a-b5f5-41c3-a9e6-81a380a84378--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e741f72a-b5f5-41c3-a9e6-81a380a84378&quot;&gt;Dans Headers, les méthodes les plus intéressantes sont :&lt;/p&gt;
&lt;!-- end paragraph e741f72a-b5f5-41c3-a9e6-81a380a84378--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;add(String key, byte[] value)&lt;/code&gt; pour ajouter une métadonnée ;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Iterable&amp;lt;Header&amp;gt; headers(String key)&lt;/code&gt; pour retourner toutes les en-têtes dont la clé est &lt;code&gt;key&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 9b9859bd-4f1f-40be-a35f-96586bca0cc7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b9859bd-4f1f-40be-a35f-96586bca0cc7&quot;&gt;Il y a aussi &lt;code&gt;Header lastHeader(String key)&lt;/code&gt;, qui retourne la &amp;quot;dernière&amp;quot; métadonnée de clé &lt;code&gt;key&lt;/code&gt;. Mais il ne semble pas avoir d&amp;#39;explication sur ce qui est entendu par &amp;quot;dernière&amp;quot;. Dans l&amp;#39;&lt;a href=&quot;https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/header/internals/RecordHeaders.java&quot;&gt;implémentation actuelle&lt;/a&gt;, un &lt;code&gt;java.util.ArrayList&lt;/code&gt; est utilisé pour stocker les en-têtes. Dernière dans cette implémentation signifie l&amp;#39;élément avec le plus grand indice. La recherche se fait donc en &lt;i&gt;O&lt;/i&gt;(&lt;i&gt;n&lt;/i&gt;) en partant du dernier élément.&lt;/p&gt;
&lt;!-- end paragraph 9b9859bd-4f1f-40be-a35f-96586bca0cc7--&gt;
&lt;!-- begin heading_2 690df11d-a1af-47fd-86c6-01dde361b98c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-690df11d-a1af-47fd-86c6-01dde361b98c&quot;&gt;Exactly once&lt;/h3&gt;
&lt;!-- end heading_2 690df11d-a1af-47fd-86c6-01dde361b98c--&gt;
&lt;!-- begin paragraph 3b72a186-c38a-45f7-8ae6-c770039e0682--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3b72a186-c38a-45f7-8ae6-c770039e0682&quot;&gt;Il existe typiquement trois sémantiques de livraison de message dans un système répartie :&lt;/p&gt;
&lt;!-- end paragraph 3b72a186-c38a-45f7-8ae6-c770039e0682--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;b&gt;At-least-once :&lt;/b&gt; le système garanti qu&amp;#39;un message sera transmis et qu&amp;#39;il apparaît au moins une fois au niveau d&amp;#39;un consommateur. Ce qui veut dire qu&amp;#39;un message produit pourra être consommé. Ce qui veut aussi dire qu&amp;#39;un message peut être dupliqué.&lt;/li&gt;&lt;li&gt;&lt;b&gt;At-most-once :&lt;/b&gt; le système garanti qu&amp;#39;un message ne sera pas dupliqué. Mais il peut ne pas transmettre le message.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Exactly-once :&lt;/b&gt; le système garanti qu&amp;#39;un message produit sera transmis et fourni en un seul exemplaire au consommateur.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph d99ac034-0e66-4171-8c55-6080b510d045--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d99ac034-0e66-4171-8c55-6080b510d045&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph d99ac034-0e66-4171-8c55-6080b510d045--&gt;
&lt;!-- begin divider 558ec1dd-9a69-4407-a2e3-ba47bbfed731--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-558ec1dd-9a69-4407-a2e3-ba47bbfed731&quot;&gt;
&lt;!-- end divider 558ec1dd-9a69-4407-a2e3-ba47bbfed731--&gt;
&lt;!-- begin paragraph 28086e79-9284-4b8b-b967-26e16c2928cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-28086e79-9284-4b8b-b967-26e16c2928cb&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 28086e79-9284-4b8b-b967-26e16c2928cb--&gt;
&lt;!-- begin bookmark 695f2925-e86d-49c2-8625-f2ed0694d9b0--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-695f2925-e86d-49c2-8625-f2ed0694d9b0&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://www.youtube.com/watch?time_continue=3&amp;v=lIGFH9TkL2w&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://i.ytimg.com/vi/lIGFH9TkL2w/maxresdefault.jpg&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;What&#039;s New in Kafka 2.1?&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Kafka 2.1 is out! Java 11 on Kafka, Support for ZStandard compression, Intuitive Producer Timeout, Security Changes… Find out what has changed in the latest ...&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://www.youtube.com/watch?time_continue=3&amp;v=lIGFH9TkL2w&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 695f2925-e86d-49c2-8625-f2ed0694d9b0--&gt;
&lt;!-- begin paragraph 3a4c0200-ece6-421d-939b-7834338cdfe7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3a4c0200-ece6-421d-939b-7834338cdfe7&quot;&gt;La GetSet parisienne qui découvre Kafka&lt;/p&gt;
&lt;!-- end paragraph 3a4c0200-ece6-421d-939b-7834338cdfe7--&gt;
&lt;!-- begin bookmark e6af9761-2a33-42e6-8356-a0949959446a--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-e6af9761-2a33-42e6-8356-a0949959446a&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://lescastcodeurs.com/2018/07/30/lcc-193-interview-apache-kafka-avec-florent-ramiere/&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://lescastcodeurs.com/images/default-twitter-card.png&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;LCC 193 - Interview Apache Kafka avec Florent Ramière&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Florent Ramière vient discuter avec Emmanuel d’Apache Kafka, de ses usages, son fonctionnement, son écosystème. Et roule ma poule sur piste noire.&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;/blog/assets/favicon_aff1983741648370e32ade19d1727cef.png&quot;&gt; https://lescastcodeurs.com/2018/07/30/lcc-193-interview-apache-kafka-avec-florent-ramiere/&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark e6af9761-2a33-42e6-8356-a0949959446a--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:65c624c5f3be4164bd26e2a531dc6c41</id>
    <title>ZIO : mais d&#039;où vient cette stack trace ? </title>
    <updated>2018-11-21T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/zio-mais-d-ou-vient-cette-stack-trace.html"/>
    <!--summary -->    <author>
      <name>Philippe Hong</name>
      <uri>https://univalence.io/blog/auteurs/philippe-hong.html</uri>
    </author>        <category term="Quick-tips"></category>    <category term="ZIO"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin paragraph 27ec034a-9cd7-4be3-8b8f-6bad0ff16867--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-27ec034a-9cd7-4be3-8b8f-6bad0ff16867&quot;&gt;Si vous utilisez ZIO, une librairie Scala permettant de gérer votre code à effet, vous avez sûrement rencontré un jour une erreur de ce type :&lt;/p&gt;
&lt;!-- end paragraph 27ec034a-9cd7-4be3-8b8f-6bad0ff16867--&gt;
&lt;!-- begin code 3d5ef4ad-3bfd-455e-b7b9-592aa9ff105f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3d5ef4ad-3bfd-455e-b7b9-592aa9ff105f&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;scalaz.zio.Errors$UnhandledError: An error was not handled by a fiber: java.io.FileNotFoundException: Philippe (No such file or directory)
	at scalaz.zio.RTS$FiberContext.$anonfun$reportErrors$1(RTS.scala:887)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
	at scalaz.zio.RTS$$anon$1.run(RTS.scala:91)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread &amp;quot;main&amp;quot; scalaz.zio.Errors$UnhandledError: An error was not handled by a fiber: java.io.FileNotFoundException: Philippe (No such file or directory)
	at scalaz.zio.RTS.unsafeRun(RTS.scala:25)
	at scalaz.zio.RTS.unsafeRun$(RTS.scala:21)
	at Toto$.unsafeRun(Titi.scala:5)
	at Toto$.main(Titi.scala:11)
	at Toto.main(Titi.scala)

Process finished with exit code 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3d5ef4ad-3bfd-455e-b7b9-592aa9ff105f--&gt;
&lt;!-- begin code 26863fae-d98c-4015-b38d-cbd8a0d79e06--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-26863fae-d98c-4015-b38d-cbd8a0d79e06&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Toto extends RTS {
  def exceptionIO(filename: String): IO[Exception, Seq[String]] = {
    IO.syncException(Source.fromFile(filename).getLines().toSeq)
  }

  def main(args: Array[String]): Unit = {
    unsafeRun(exceptionIO(&amp;quot;Philippe&amp;quot;))
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 26863fae-d98c-4015-b38d-cbd8a0d79e06--&gt;
&lt;!-- begin paragraph 2c9a487f-2140-4078-9a04-b0291075963d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c9a487f-2140-4078-9a04-b0291075963d&quot;&gt;Super méga chouette, nous avons une &lt;code&gt;java.io.FileNotFoundException: Philippe&lt;/code&gt; mais rien sur l&amp;#39;endroit où se cache le coupable.&lt;/p&gt;
&lt;!-- end paragraph 2c9a487f-2140-4078-9a04-b0291075963d--&gt;&lt;div class=&quot;block-embed level-0&quot;&gt;&lt;div class=&quot;player gfycat&quot;&gt;&lt;iframe src=&#039;https://gfycat.com/ifr/SolidAmpleCrane?controls=0&amp;speed=1&#039;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph f5eb50dc-ba3d-4abb-baaa-eed7a3efb7ac--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f5eb50dc-ba3d-4abb-baaa-eed7a3efb7ac&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=EOxUWLl2HFs&quot;&gt;Philippe! Je sais où tu te caches!&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph f5eb50dc-ba3d-4abb-baaa-eed7a3efb7ac--&gt;
&lt;!-- begin heading_1 2e2b3c28-04be-4b70-94c4-6247b25e4f4f--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2e2b3c28-04be-4b70-94c4-6247b25e4f4f&quot;&gt;La doc de ZIO&lt;/h2&gt;
&lt;!-- end heading_1 2e2b3c28-04be-4b70-94c4-6247b25e4f4f--&gt;
&lt;!-- begin paragraph f968f168-b26d-45cf-9caa-92026ad42d20--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f968f168-b26d-45cf-9caa-92026ad42d20&quot;&gt;Si on essaye l&amp;#39;exemple de la &lt;a href=&quot;https://scalaz.github.io/scalaz-zio/usage/main.html&quot;&gt;documentation&lt;/a&gt;:&lt;/p&gt;
&lt;!-- end paragraph f968f168-b26d-45cf-9caa-92026ad42d20--&gt;
&lt;!-- begin code e7f2d997-27d2-4987-aba9-f59d99e2c4e9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e7f2d997-27d2-4987-aba9-f59d99e2c4e9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Titi extends App {
  override def run(args: List[String]): IO[Nothing, Titi.ExitStatus] = {
    Toto.exceptionIO(&amp;quot;Philippe&amp;quot;).attempt.map(_.fold(_ =&amp;gt; 1, _ =&amp;gt; 0)).map(ExitStatus.ExitNow(_))
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e7f2d997-27d2-4987-aba9-f59d99e2c4e9--&gt;
&lt;!-- begin code 9b9f56d5-9eda-4d99-a491-12e2409e41b9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9b9f56d5-9eda-4d99-a491-12e2409e41b9&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Process finished with exit code 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9b9f56d5-9eda-4d99-a491-12e2409e41b9--&gt;
&lt;!-- begin paragraph 3766cd2a-cf91-4962-91f2-bb0b003b5818--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3766cd2a-cf91-4962-91f2-bb0b003b5818&quot;&gt;On n&amp;#39;est pas plus avancé !&lt;/p&gt;
&lt;!-- end paragraph 3766cd2a-cf91-4962-91f2-bb0b003b5818--&gt;
&lt;!-- begin heading_1 cdd15c6f-4040-4b74-b195-2c31f2f1fdce--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-cdd15c6f-4040-4b74-b195-2c31f2f1fdce&quot;&gt;Do It Yourself&lt;/h2&gt;
&lt;!-- end heading_1 cdd15c6f-4040-4b74-b195-2c31f2f1fdce--&gt;
&lt;!-- begin paragraph 486112eb-2ced-4f81-8c5a-5c8474e727df--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-486112eb-2ced-4f81-8c5a-5c8474e727df&quot;&gt;On cherche à récupérer la stack trace d&amp;#39;une manière ou d&amp;#39;une autre pour avoir un peu plus d&amp;#39;informations sur la ligne de code qui nous embête.&lt;/p&gt;
&lt;!-- end paragraph 486112eb-2ced-4f81-8c5a-5c8474e727df--&gt;
&lt;!-- begin paragraph fe48dc4e-e6b8-4bbd-962e-25eb446b329f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fe48dc4e-e6b8-4bbd-962e-25eb446b329f&quot;&gt;Pour cela nous allons créer quelques fonctions (accompagnées de leur coulis d&amp;#39;implicit class) qui nous permettent de reporter la stack trace en console :&lt;/p&gt;
&lt;!-- end paragraph fe48dc4e-e6b8-4bbd-962e-25eb446b329f--&gt;
&lt;!-- begin code f77256fc-d713-4dcb-8ad2-20510e1af257--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f77256fc-d713-4dcb-8ad2-20510e1af257&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Utils {

  implicit class IOOps[E, A](io: IO[E, A]) {
    def peekError(peek: E =&amp;gt; IO[Nothing, Unit]): IO[E, A] = {
      io.flip.peek(peek).flip
    }
  }

  implicit class ExceptionOps[E &amp;lt;: Throwable, A](io: IO[E, A]) {
    def printException: IO[Nothing, Option[A]] = {
      io.peekError(x =&amp;gt; IO.sync({
        x.printStackTrace()
      })).attempt.map(_.right.toOption)
    }
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f77256fc-d713-4dcb-8ad2-20510e1af257--&gt;
&lt;!-- begin code 13d39ea6-06e4-4ab3-9d27-f9a5be490a7e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-13d39ea6-06e4-4ab3-9d27-f9a5be490a7e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object Toto extends RTS {
  def exceptionIO(filename: String): IO[Exception, Seq[String]] = {
    IO.syncException(Source.fromFile(filename).getLines().toSeq)
  }

  def main(args: Array[String]): Unit = {
    import Utils._
    unsafeRun(exceptionIO(&amp;quot;Philippe&amp;quot;).printException)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 13d39ea6-06e4-4ab3-9d27-f9a5be490a7e--&gt;
&lt;!-- begin code c7173263-cc2d-467e-b4c1-487fe644cb98--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-c7173263-cc2d-467e-b4c1-487fe644cb98&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;java.io.FileNotFoundException: Philippe (No such file or directory)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.&amp;lt;init&amp;gt;(FileInputStream.java:138)
	at scala.io.Source$.fromFile(Source.scala:90)
	at scala.io.Source$.fromFile(Source.scala:75)
	at scala.io.Source$.fromFile(Source.scala:53)
	at Toto$.$anonfun$exceptionIO$1(Titi.scala:7)
	at scalaz.zio.IO$.$anonfun$syncCatch$1(IO.scala:974)
	at scalaz.zio.RTS$FiberContext.evaluate(RTS.scala:372)
	at scalaz.zio.RTS.unsafeRunSync(RTS.scala:39)
	at scalaz.zio.RTS.unsafeRunSync$(RTS.scala:37)
	at Toto$.unsafeRunSync(Titi.scala:5)
	at scalaz.zio.RTS.unsafeRun(RTS.scala:21)
	at scalaz.zio.RTS.unsafeRun$(RTS.scala:21)
	at Toto$.unsafeRun(Titi.scala:5)
	at Toto$.main(Titi.scala:11)
	at Toto.main(Titi.scala)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code c7173263-cc2d-467e-b4c1-487fe644cb98--&gt;
&lt;!-- begin paragraph 40336029-8cad-42f4-adc5-10aebfb099db--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-40336029-8cad-42f4-adc5-10aebfb099db&quot;&gt;Voilà, maintenant nous savons à quelle ligne de code on cherchait Philippe.&lt;/p&gt;
&lt;!-- end paragraph 40336029-8cad-42f4-adc5-10aebfb099db--&gt;
&lt;!-- begin heading_1 107e6fbb-ed3e-4301-9210-6409fe8e193a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-107e6fbb-ed3e-4301-9210-6409fe8e193a&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 107e6fbb-ed3e-4301-9210-6409fe8e193a--&gt;
&lt;!-- begin paragraph 45c56c92-2a58-4308-8551-aa72598774d4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-45c56c92-2a58-4308-8551-aa72598774d4&quot;&gt;ZIO c&amp;#39;est top, néanmoins il manque parfois quelques fonctions pour gérer des cas classiques.&lt;/p&gt;
&lt;!-- end paragraph 45c56c92-2a58-4308-8551-aa72598774d4--&gt;
&lt;!-- begin paragraph dafc0bbf-bca7-431a-b217-27915bca20c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dafc0bbf-bca7-431a-b217-27915bca20c3&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph dafc0bbf-bca7-431a-b217-27915bca20c3--&gt;
&lt;!-- begin paragraph c943e3a1-f212-420f-829f-68da3a1d85d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c943e3a1-f212-420f-829f-68da3a1d85d3&quot;&gt;On peut légitimement se poser la question du choix de ZIO par rapport à Cats-Effects quand on fait ce genre de &amp;quot;bêtises&amp;quot;, mais nous n&amp;#39;avons pas la place dans cet article pour en discuter ! 😃&lt;/p&gt;
&lt;!-- end paragraph c943e3a1-f212-420f-829f-68da3a1d85d3--&gt;
&lt;!-- begin paragraph 0f894910-6775-4caf-81f1-f1f101f35c1a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0f894910-6775-4caf-81f1-f1f101f35c1a&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 0f894910-6775-4caf-81f1-f1f101f35c1a--&gt;
&lt;!-- begin paragraph 491e54d0-ae5b-4716-87d0-6da422e75838--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-491e54d0-ae5b-4716-87d0-6da422e75838&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 491e54d0-ae5b-4716-87d0-6da422e75838--&gt;
&lt;!-- begin paragraph 9d45e08f-a144-4c8b-af55-7ae8dd71f787--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9d45e08f-a144-4c8b-af55-7ae8dd71f787&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 9d45e08f-a144-4c8b-af55-7ae8dd71f787--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:5f2626f0203243989cc0f13643c479e4</id>
    <title>ScalaIO 2018 - Le retour de l&#039;équipe Univalence</title>
    <updated>2018-11-21T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/scalaio-2018-le-retour-de-l-equipe-univalence.html"/>
    <!--summary -->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="ScalaIO"></category>    <category term="Scala"></category>    <category term="Spark"></category>    <category term="Dotty"></category>    <category term="ZIO"></category>    <category term="ScalaZ"></category>    <category term="Magnolia"></category>    <category term="Fury"></category>    <category term="Matryoshka"></category>    <category term="Programmation fonctionnelle"></category>    <content type="html">
&lt;!-- begin paragraph 5e8faeca-288a-4f73-9ad6-437a5e8834db--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5e8faeca-288a-4f73-9ad6-437a5e8834db&quot;&gt;&lt;a href=&quot;https://scala.io/&quot;&gt;ScalaIO&lt;/a&gt; est une conférence tournant autour du langage Scala, de son écosystème (Akka, Spark, Play...) et aussi autour de tout ce qui touche de près ou de loin à la programmation fonctionnelle, quelque soit le langage (Haskell, OCaml, JavaScript, TypeScript...). La session ScalaIO 2018 a eu lieu entre le 29 et 31 octobre au CPE à Lyon.&lt;/p&gt;
&lt;!-- end paragraph 5e8faeca-288a-4f73-9ad6-437a5e8834db--&gt;
&lt;!-- begin paragraph e214f42a-4f2e-4fc2-9ab1-9a7efac2d362--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e214f42a-4f2e-4fc2-9ab1-9a7efac2d362&quot;&gt;Pour l&amp;#39;occasion, l&amp;#39;équipe d&amp;#39;Univalence ont fait le déplacement. Ils reportent ici leurs impressions.&lt;/p&gt;
&lt;!-- end paragraph e214f42a-4f2e-4fc2-9ab1-9a7efac2d362--&gt;
&lt;!-- begin heading_1 74c9ce3f-105f-4907-896d-22369087b7e7--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-74c9ce3f-105f-4907-896d-22369087b7e7&quot;&gt;Les keynotes&lt;/h2&gt;
&lt;!-- end heading_1 74c9ce3f-105f-4907-896d-22369087b7e7--&gt;
&lt;!-- begin paragraph c1c66a90-33ca-4b67-b952-4977491f2da7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c1c66a90-33ca-4b67-b952-4977491f2da7&quot;&gt;Nous avons eu droit à trois keynotes :&lt;/p&gt;
&lt;!-- end paragraph c1c66a90-33ca-4b67-b952-4977491f2da7--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;i&gt;Towards Scala 3 - A Status Report&lt;/i&gt; par Martin Odersky, le deuxième jour en ouverture.&lt;/li&gt;&lt;li&gt;&lt;i&gt;ZIO: Next Generation Effects in Scala&lt;/i&gt; par John De Goes, le troisième jour en ouverture.&lt;/li&gt;&lt;li&gt;&lt;i&gt;Future of Scala&lt;/i&gt; par Jon Pretty, le troisième jour en fermeture.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 20b5f7d1-715b-4462-9da9-27242cef4da2--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-20b5f7d1-715b-4462-9da9-27242cef4da2&quot;&gt;&lt;i&gt;Towards Scala 3 - A Status Report&lt;/i&gt; par Martin Odersky&lt;/h3&gt;
&lt;!-- end heading_2 20b5f7d1-715b-4462-9da9-27242cef4da2--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph cd992e68-7a75-45a6-ab09-14dceb008dbc--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-cd992e68-7a75-45a6-ab09-14dceb008dbc&quot;&gt;Le talk d&amp;#39;Odersky fut en partie une redite de son talk présenté lors de ScalaDays Berlin en mai dernier (disponible sur &lt;a href=&quot;https://www.youtube.com/watch?v=1VDOhiFYW3Y&quot;&gt;Youtube&lt;/a&gt;) et lors de ScalaDays New York en juin dernier (disponible aussi sur &lt;a href=&quot;https://www.youtube.com/watch?v=nKZsHZIcReA&quot;&gt;Youtube&lt;/a&gt;). Si vous avez loupé ces sessions, sachez qu&amp;#39;avant l&amp;#39;arrivée de Scala 3 prévue pour 2020, il y aura une version 2.13 prévue pour février 2019. L&amp;#39;objectif sera d&amp;#39;orienter le langage vers plus de simplicité et de réduire les &lt;i&gt;puzzlers&lt;/i&gt; (comprenez des expressions Scala valides dont le résultat est contrintuitif).&lt;/p&gt;
&lt;!-- end paragraph cd992e68-7a75-45a6-ab09-14dceb008dbc--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/20181030_092627.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph 918c1cb5-0637-4508-9527-8bd987146cfb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-918c1cb5-0637-4508-9527-8bd987146cfb&quot;&gt;Dans ce qui va arriver :&lt;/p&gt;
&lt;!-- end paragraph 918c1cb5-0637-4508-9527-8bd987146cfb--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Une notation pour les types union &lt;code&gt;A | B&lt;/code&gt; (par exemple &lt;code&gt;String | Null&lt;/code&gt;) et une nouvelle notation pour les types intersection &lt;code&gt;A &amp;amp; B&lt;/code&gt; (le mot-clé &lt;code&gt;with&lt;/code&gt; disparaîtra progressivement).&lt;/li&gt;&lt;li&gt;Les types de fonction implicite &lt;code&gt;implicit A =&amp;gt; B&lt;/code&gt;, ce qui va permettre de créer des contextes de traitement.&lt;/li&gt;&lt;li&gt;Une syntaxe pour les &lt;i&gt;enums&lt;/i&gt; avec la possibilité de les paramétrer. Par exemple ci-dessous, nous voyons une autre façon d&amp;#39;écrire le type &lt;code&gt;Option&lt;/code&gt;.
&lt;!-- begin code 7aa04056-4879-4fa3-a414-2a4337c568d6--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-7aa04056-4879-4fa3-a414-2a4337c568d6&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;enum Option[+T] {
  case Some(x: T)
  case None

  def isDefined: Boolean = this match {
    case None =&amp;gt; false
    case some =&amp;gt; true
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7aa04056-4879-4fa3-a414-2a4337c568d6--&gt;&lt;/li&gt;&lt;li&gt;Nous pourrons mettre des paramètres dans les &lt;i&gt;traits&lt;/i&gt; &lt;code&gt;trait MyTrait(x: String)&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;La fin de la limite à 22 paramètres pour les fonctions et les tuples : nous pourrons mettre autant de paramètres que nécessaire. Les tuples seront réimplémentés afin d&amp;#39;avoir une vision plus proche des HList de Shapeless (&lt;code&gt;(a, b, c) == (a, (b, (c, ())))&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;L&amp;#39;égalité multiversale, c&amp;#39;est-à-dire la possibilité de comparer deux types différents que dans le cas où un implicite basé sur &lt;code&gt;Eq&lt;/code&gt; a été déclaré.&lt;/li&gt;&lt;li&gt;Une notation pour les types lambda &lt;code&gt;type T = [X] =&amp;gt; F[X]&lt;/code&gt; (qui équivaut à &lt;code&gt;type T[X] = F[X]&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph ba3c92c3-7221-4fec-837a-18ce4cd48a94--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-ba3c92c3-7221-4fec-837a-18ce4cd48a94&quot;&gt;Une volonté est marquée de séparer un peu plus Scala de Java. Dans ce cadre, Odersky propose TASTY. TASTY est un format de sérialisation d&amp;#39;AST (&lt;i&gt;Abstract Syntax Tree&lt;/i&gt;) typé. Il faut le voir comme un format intermédiaire généré juste après les premières phases de compilation et de &lt;i&gt;type checking&lt;/i&gt;. Libre ensuite aux phases suivantes de transcrire le format TASTY vers d&amp;#39;autres formats : Java byte code, Javascript, code machine natif, etc. Le format TASTY peut aussi être utilisé à destination des IDE pour une meilleure intégration du langage.&lt;/p&gt;
&lt;!-- end paragraph ba3c92c3-7221-4fec-837a-18ce4cd48a94--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/2018_10_3009_38officelens.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph f42f5ab4-e2c5-483f-a369-c6c3067731d6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f42f5ab4-e2c5-483f-a369-c6c3067731d6&quot;&gt;Si TASTY sera pleinement fonctionnel pour Scala 3 (il est développé dans le cadre de Dotty, qui représente en quelque sorte le futur de Scala), sa mise en place pour Scala 2.x est encore en cours d&amp;#39;étude. La plus grosse difficulté concerne actuellement les macros. Il y a deux types de macros :&lt;/p&gt;
&lt;!-- end paragraph f42f5ab4-e2c5-483f-a369-c6c3067731d6--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;i&gt;Blackbox&lt;/i&gt; : ce sont des macros qui interviennent après la phase de &lt;i&gt;type checking&lt;/i&gt;. Les informations sur les types sont complètement résolus.&lt;/li&gt;&lt;li&gt;&lt;i&gt;Whitebox&lt;/i&gt; : ce sont des macros qui interviennent avant la phase de &lt;i&gt;type checking&lt;/i&gt;. Les informations sur les types dans les signatures sont imprécises.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 45493615-3864-4209-9903-8b0d5f06c873--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-45493615-3864-4209-9903-8b0d5f06c873&quot;&gt;Or les macros &lt;i&gt;whitebox&lt;/i&gt; vont disparaître avec Scala 3. Pourtant, plusieurs frameworks Scala dépendent des macros &lt;i&gt;whitebox&lt;/i&gt;, comme par exemple SCio (présenté dans l&amp;#39;après-midi même). Odersky recherche actuellement une solution au niveau langage pour pallier la disparition des macros &lt;i&gt;whitebox&lt;/i&gt; (😢). &lt;/p&gt;
&lt;!-- end paragraph 45493615-3864-4209-9903-8b0d5f06c873--&gt;
&lt;!-- begin paragraph 7b6101c5-d02f-46b7-b777-51d55bc34a0d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7b6101c5-d02f-46b7-b777-51d55bc34a0d&quot;&gt;Pour terminer, Odersky nous propose à travers &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; une démonstration des nouvelles capacités de Dotty au niveau des types : par exemple la représentation de l&amp;#39;inversion d&amp;#39;un tuple avec de nouvelles fonctionnalités pour transformer les types (pattern matching au niveau du système de type).&lt;/p&gt;
&lt;!-- end paragraph 7b6101c5-d02f-46b7-b777-51d55bc34a0d--&gt;
&lt;!-- begin code 4b6d860a-6b5b-4b3b-9e68-357ba5bc2eb1--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4b6d860a-6b5b-4b3b-9e68-357ba5bc2eb1&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;type Append[+X &amp;lt;: Tuple, Y] &amp;lt;: Tuple = X match {
  case Unit      =&amp;gt; Y *: Unit
  case x1 *: xs1 =&amp;gt; x1 *: Append[xs1, Y]
}

type Reverse[+X &amp;lt;: Tuple] &amp;lt;: Tuple = X match {
  case Unit      =&amp;gt; Unit
  case x1 *: xs1 =&amp;gt; Append[Reverse[xs1], x1]
}

def reverse[T &amp;lt;: Tuple](xs: T): Reverse[T] = ???

def r: (Boolean, String, Int) = reverse((1, &amp;quot;&amp;quot;, false))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4b6d860a-6b5b-4b3b-9e68-357ba5bc2eb1--&gt;
&lt;!-- begin heading_1 76647010-9ca8-4723-ad30-d30ab42ebaa4--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-76647010-9ca8-4723-ad30-d30ab42ebaa4&quot;&gt;&lt;i&gt;ZIO: Next Generation Effects in Scala&lt;/i&gt; par John De Goes&lt;/h2&gt;
&lt;!-- end heading_1 76647010-9ca8-4723-ad30-d30ab42ebaa4--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph 07cc76dd-f814-465d-8ce6-857168fbdf9a--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-07cc76dd-f814-465d-8ce6-857168fbdf9a&quot;&gt;Pour l&amp;#39;occasion, John De Goes a renommé son talk en &amp;quot;Beautiful Scala&amp;quot;. Un talk démarrant par la Genèse revue et corrigée du point de vue du développeur fonctionnel !... Avec quelques trolls au passage :&lt;/p&gt;
&lt;!-- end paragraph 07cc76dd-f814-465d-8ce6-857168fbdf9a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-2&quot;&gt;&lt;li&gt;Sur les outils : &amp;quot;Il y a deux types de programmeurs : ceux qui utilisent les éditeurs de texte et ceux qui utilisent les beaux IDE&amp;quot;.&lt;/li&gt;&lt;li&gt;Et surtout sur la programmation objet : lors d&amp;#39;une discussion entre le serpent et &lt;del&gt;l&amp;#39;homme&lt;/del&gt; le développeur : &amp;quot;An apple is a plant which has seeds and is the receiver of being-eaten&amp;quot;.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/jdg.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5f2626f0-2032-4398-9cc0-f13643c479e4/img_20181031_091905.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph e9df58b9-4160-4bfe-9dce-31f8b76f54e1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e9df58b9-4160-4bfe-9dce-31f8b76f54e1&quot;&gt;Là dessus, John fait une transition à l&amp;#39;ancien testament (toujours revu et corrigé) en présentant les tables de la loi du développeur et ses 10 commandements, tout en parlant de son framework &lt;a href=&quot;https://github.com/scalaz/scalaz-zio&quot;&gt;scalaz-zio&lt;/a&gt; (ou ZIO).&lt;/p&gt;
&lt;!-- end paragraph e9df58b9-4160-4bfe-9dce-31f8b76f54e1--&gt;&lt;ol class=&quot;block-numbered_list_item level-0&quot;&gt;&lt;li&gt;Une fonction doit être totale, déterministe et sans effet de bord.&lt;/li&gt;&lt;li&gt;Les effets doivent être réifiés. Il faut donc utiliser la notation &lt;code&gt;IO[E, A]&lt;/code&gt; de ZIO. &lt;code&gt;IO&lt;/code&gt; représente tout processus impliquant possiblement un effet. C&amp;#39;est-à-dire une étape de calcul dans laquelle la modification du système est observable. Dans &lt;code&gt;IO[E, A]&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt; représente le type de la valeur attendu par le processus et &lt;code&gt;E&lt;/code&gt; représente le type retourné en cas de problème.&lt;/li&gt;&lt;li&gt;Il ne faut pas bloquer les threads (pour des raisons de performance). C&amp;#39;est tout l&amp;#39;intérêt des &lt;a href=&quot;https://en.wikipedia.org/wiki/Fiber_(computer_science)&quot;&gt;fibres&lt;/a&gt; qui peuvent être vues comme un thread léger et se retrouvent à la base de ZIO. Les fibres dans ZIO sont notées &lt;code&gt;Fiber[E, A]&lt;/code&gt;. Elles permettent d&amp;#39;éviter le &lt;i&gt;callback hell&lt;/i&gt; et peuvent être gérées par le GC, même si la fibre contient une boucle infinie.&lt;/li&gt;&lt;li&gt;Des ressources managées. ZIO propose deux opérations &lt;code&gt;bracket&lt;/code&gt; et &lt;code&gt;managed&lt;/code&gt; qui permettent de gérer des zones de code à la manière de &lt;i&gt;try with resource&lt;/i&gt; en Java dans lesquelles les ressources comme les fichiers ou les connexions à un service sont gérées sans que le développeur n&amp;#39;ait besoin de rentrer dans les détails techniques.&lt;/li&gt;&lt;li&gt;Ne pas avoir peur de la concurrence. L&amp;#39;objectif est de mettre en place une abstraction donnant une perception claire de la concurrence. Pour cela, ZIO propose deux opérations : &lt;code&gt;parAll&lt;/code&gt; pour récupérer le résultat de toutes les fibres exécuter en même temps et &lt;code&gt;raceAll&lt;/code&gt; pour récupérer le résultat de la fibre ayant répondu en premier parmi plusieurs, sachant que les autres fibres sont &lt;i&gt;&lt;b&gt;automatiquement arrêtées &lt;/b&gt;&lt;/i&gt;(difficilement réalisable sans une réification des différentes tâches des processus comme dans les fibres de ZIO)&lt;i&gt;&lt;b&gt; &lt;/b&gt;&lt;/i&gt;.&lt;/li&gt;&lt;li&gt;Reposez-vous autant que possible (la paresse est importante). La création de fibres dans ZIO se fait en utilisant l&amp;#39;opération &lt;code&gt;fork&lt;/code&gt;. Il est possible alors soit de se mettre en attente de son terme avec &lt;code&gt;join&lt;/code&gt; ou de l&amp;#39;interrompre avec l&amp;#39;opération &lt;code&gt;interrupt&lt;/code&gt;. Sachant que &lt;code&gt;interrupt&lt;/code&gt; agit immédiatement.&lt;/li&gt;&lt;li&gt;Honorer les transactions. ZIO fournit &lt;code&gt;Ref[A]&lt;/code&gt; et &lt;code&gt;RefM[A]&lt;/code&gt;, qui sont des zones mémoires de type &lt;a href=&quot;https://fr.wikipedia.org/wiki/M%C3%A9moire_transactionnelle_logicielle&quot;&gt;STM&lt;/a&gt; (&lt;i&gt;Software Transactional Memory&lt;/i&gt;), c&amp;#39;est-à-dire avec des transactions (comme dans une base de données). Ces zones mémoires sont mutables, mais les opérations effectuées sur celles-ci sont atomiques. &lt;code&gt;RefM[A]&lt;/code&gt; permet en plus de gérer les effets (par exemple, afficher un message à l&amp;#39;écran).&lt;/li&gt;&lt;li&gt;Respecter les promesses. ZIO fournit &lt;code&gt;Promise[E, A]&lt;/code&gt;. Ce type représente des variables qui ne peuvent être assignées qu&amp;#39;une seule fois avec une valeur de type &lt;code&gt;A&lt;/code&gt;. Cette valeur sera récupérée par une autre fibre, mise en attente de l&amp;#39;assignation. Il est possible aussi de remonter une erreur &lt;code&gt;E&lt;/code&gt; à travers cette variable. &lt;code&gt;Promise[E, A]&lt;/code&gt; permet de coordonner plusieurs fibres en échangeant des valeurs entre-elles.&lt;/li&gt;&lt;li&gt;Ne pas abandonner. ZIO propose des ordonnanceurs composables avec &lt;code&gt;Schedule[A, B]&lt;/code&gt;, c&amp;#39;est-à-dire un outil permettant de spécifier la récurrence d&amp;#39;une tâche (en nombre de répétitions et/ou en intervalle de temps entre deux répétitions). &lt;code&gt;Schedule[A, B]&lt;/code&gt; consomme des éléments de type &lt;code&gt;A&lt;/code&gt; et produit des éléments de type &lt;code&gt;B&lt;/code&gt;. On peut ainsi représenter une nouvelle tentative (&lt;i&gt;retry&lt;/i&gt;) avec &lt;code&gt;Scheduler[E, A]&lt;/code&gt;, où &lt;code&gt;E&lt;/code&gt; représente le type d&amp;#39;une erreur : une tâche est répétée jusqu&amp;#39;à ce qu&amp;#39;il n&amp;#39;y ait plus d&amp;#39;erreur.&lt;/li&gt;&lt;li&gt;Soyez productif et multipliez la donnée. John De Goes parle à ce moment là d&amp;#39;un type Queue avec &lt;i&gt;back-pressure&lt;/i&gt; et aussi d&amp;#39;un nouveau type pour les flux de données &lt;code&gt;Stream[E, A]&lt;/code&gt; incluant là aussi la notion d&amp;#39;erreur avec &lt;code&gt;E&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;
&lt;!-- begin paragraph 2bf154a8-d51f-4cee-8f61-dc024ba3f8c8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2bf154a8-d51f-4cee-8f61-dc024ba3f8c8&quot;&gt;Le talk s&amp;#39;est terminé sur un appel à participer à la montée en compétence des développeurs en programmation fonctionnelle et en particulier avec ZIO.&lt;/p&gt;
&lt;!-- end paragraph 2bf154a8-d51f-4cee-8f61-dc024ba3f8c8--&gt;
&lt;!-- begin paragraph 2d44f7d3-9f6d-4a50-ace6-323602f70a6a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2d44f7d3-9f6d-4a50-ace6-323602f70a6a&quot;&gt;... Puis, avec l&amp;#39;intervention de l&amp;#39;organisation, John a posé quelques questions en demandant à ceux qui répondent par non (en toute sincérité) de venir sur la scène :&lt;/p&gt;
&lt;!-- end paragraph 2d44f7d3-9f6d-4a50-ace6-323602f70a6a--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&amp;quot;Savez-vous ce qu&amp;#39;est un Functor ?&amp;quot; Une dizaine de personnes hésitantes se sont alors déplacées sur la scène.&lt;/li&gt;&lt;li&gt;&amp;quot;Connaissez-vous un type de &amp;quot;type&amp;quot; &lt;code&gt;(*, *) -&amp;gt; *&lt;/code&gt; ?&amp;quot; Beaucoup de participants se sont regardés. Et petit à petit, les personnes se sont levées pour venir remplir l&amp;#39;estrade. Pendant quelques minutes, un flux continu et pratiquement interminable s&amp;#39;est emparé des escaliers de l&amp;#39;amphi en direction de la scène. Sur un amphi de 500 places bien rempli, il ne restait cette fois qu&amp;#39;une vingtaine de personnes assises. Le reste remplissant comme il le pouvait les rares places restant sur l&amp;#39;estrade. La photo était impressionnante, laissant dans l&amp;#39;embarras un instant l&amp;#39;organisation ! (Si vous vous vous demandez quelle réponse est valide, il faut comprendre que &lt;code&gt;(*, *) -&amp;gt; *&lt;/code&gt; (ou aussi &lt;code&gt;* -&amp;gt; * -&amp;gt; *&lt;/code&gt;) représente tous les constructeurs de types avec deux paramètres comme &lt;code&gt;Either[A, B]&lt;/code&gt;, &lt;code&gt;(A, B)&lt;/code&gt; ou &lt;code&gt;A =&amp;gt; B&lt;/code&gt; ; allez voir la notion de &lt;a href=&quot;https://en.wikipedia.org/wiki/Kind_(type_theory)&quot;&gt;&lt;i&gt;higher-kinded type&lt;/i&gt;&lt;/a&gt;.)&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/img_20181031_100035.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/img_20181031_100020.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&amp;quot;Qui ne comprend pas la programmation fonctionnelle ?&amp;quot; Le flux pris une marche inverse, remplissant à nouveau les sièges de l&amp;#39;amphi et ne laissant qu&amp;#39;une quarantaine de personnes sur l&amp;#39;estrade. Et pour ces personnes restant sur scène un exemplaire de &lt;a href=&quot;https://leanpub.com/fpmortals&quot;&gt;&lt;i&gt;Functional Programming for Mortals with ScalaZ&lt;/i&gt;&lt;/a&gt; de Sam Halliday leur était offert !&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5f2626f0-2032-4398-9cc0-f13643c479e4/20181031_100509.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin heading_1 922c5d30-5e16-4760-807a-0dfb29c495ff--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-922c5d30-5e16-4760-807a-0dfb29c495ff&quot;&gt;&lt;i&gt;Future of Scala&lt;/i&gt; par Jon Pretty&lt;/h2&gt;
&lt;!-- end heading_1 922c5d30-5e16-4760-807a-0dfb29c495ff--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph 2212fc31-dbab-4ffa-97b8-57ed33e0a6b3--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-2212fc31-dbab-4ffa-97b8-57ed33e0a6b3&quot;&gt;Alors que SBT domine sur l&amp;#39;ensemble des outils de gestion de build dédié à Scala, d&amp;#39;autres outils du même ordre voient le jour. C&amp;#39;est le cas pour CBT ou Mill. Dans ce talk, Jon Pretty nous présente Fury, qui est aussi un outil de build, avec un logo que Jon nous promet de voir plus souvent.&lt;/p&gt;
&lt;!-- end paragraph 2212fc31-dbab-4ffa-97b8-57ed33e0a6b3--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/20181031_163612.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph 1a7d3e7e-947f-4f1e-9bb2-aabfcbf71001--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1a7d3e7e-947f-4f1e-9bb2-aabfcbf71001&quot;&gt;Si le nom représenterait ce que Jon pense de SBT, la particularité de Fury est&lt;/p&gt;
&lt;!-- end paragraph 1a7d3e7e-947f-4f1e-9bb2-aabfcbf71001--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Qu&amp;#39;il s&amp;#39;utilise en ligne de commande (CLI).&lt;/li&gt;&lt;li&gt;Qu&amp;#39;il est rapide. Nous n&amp;#39;avons pas à attendre qu&amp;#39;il se charge comme SBT.&lt;/li&gt;&lt;li&gt;Qu&amp;#39;il propose un reporting semi-graphique (vue tabulaire sur les éléments du projet, vue arborescente et régulièrement mise à jour lors de la construction du projet)&lt;/li&gt;&lt;li&gt;Qu&amp;#39;il est déjà intégré avec d&amp;#39;autres outils, comme Git.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 8e443714-7775-4059-8d24-c2b717bc8eec--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8e443714-7775-4059-8d24-c2b717bc8eec&quot;&gt;Fury fournit une vision hiérarchique de vos projets. À la base se trouve le &lt;i&gt;workspace&lt;/i&gt;, qui comme son nom l&amp;#39;indique est l&amp;#39;espace de travail de Fury. Le &lt;i&gt;workspace&lt;/i&gt; représente aussi un ensemble de repo Git. Dans ce &lt;i&gt;workspace&lt;/i&gt;, nous allons avoir différents projets. Un projet représente une lib ou une application entière et correspond à un repo Git. Un projet sera ensuite décomposé en module(s) — typiquement &lt;i&gt;core&lt;/i&gt;, &lt;i&gt;macro&lt;/i&gt;, &lt;i&gt;bench&lt;/i&gt;... Aux différents projets, il est possible d&amp;#39;associer des &lt;i&gt;schema&lt;/i&gt;. Un &lt;i&gt;schema&lt;/i&gt; est une configuration qui permet de compiler un projet sous différentes versions (scala 2.12, scala 2.11, scalaJS...).&lt;/p&gt;
&lt;!-- end paragraph 8e443714-7775-4059-8d24-c2b717bc8eec--&gt;
&lt;!-- begin heading_1 93cb6947-a017-4162-98bd-9696fbafd045--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-93cb6947-a017-4162-98bd-9696fbafd045&quot;&gt;Quelques conférences de ScalaIO&lt;/h2&gt;
&lt;!-- end heading_1 93cb6947-a017-4162-98bd-9696fbafd045--&gt;
&lt;!-- begin heading_2 c77ec225-c722-4163-98ab-c42677c2c548--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-c77ec225-c722-4163-98ab-c42677c2c548&quot;&gt;&lt;i&gt;Scala Best Practices I Wish Someone&amp;#39;d Told Me About&lt;/i&gt; par Nicolas Rinaudo&lt;/h3&gt;
&lt;!-- end heading_2 c77ec225-c722-4163-98ab-c42677c2c548--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph f6068b8d-06fd-45eb-b1ba-6b93a32d15d7--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-f6068b8d-06fd-45eb-b1ba-6b93a32d15d7&quot;&gt;Nicolas Rinaudo a présenté un talk intitulé &amp;quot;Scala Best Practices I Wish Someone&amp;#39;d Told Me About&amp;quot; (en français). Cette présentation était une occasion unique à vivre. Ce n&amp;#39;est pas tant le contenu qui était exceptionnel. Celui-ci était de bonne qualité et j&amp;#39;ai eu l&amp;#39;occasion d&amp;#39;apprendre de bonnes et de mauvaises pratiques en Scala. Non, ce qui fut exceptionnel, c&amp;#39;était la présence non avertie de Martin Odersky, le créateur de Scala, dans cette session — mettant en émoi le speaker, qui effectivement ne s&amp;#39;y attendait pas.&lt;/p&gt;
&lt;!-- end paragraph f6068b8d-06fd-45eb-b1ba-6b93a32d15d7--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/20181030_140428.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 339579a7-1ccf-4728-b99f-b98cabf931e5--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-339579a7-1ccf-4728-b99f-b98cabf931e5&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 339579a7-1ccf-4728-b99f-b98cabf931e5--&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6a82eeb6-95a5-40e1-8371-09f0d850971a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6a82eeb6-95a5-40e1-8371-09f0d850971a&quot;&gt;Je tiens à rappeler que le talk était en français et en mode puzzler, donc interactif. J&amp;#39;ai donc découvert que Martin Odersky comprend bien le français et le parle aussi ! Et sa présence fait écho à ce qu&amp;#39;il avait annoncé dans sa keynote : réduire autant que possible les puzzlers du langage Scala.&lt;/p&gt;
&lt;!-- end paragraph 6a82eeb6-95a5-40e1-8371-09f0d850971a--&gt;
&lt;!-- begin paragraph 36e360bf-dde1-4fb7-8477-3f6a3429ea8c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-36e360bf-dde1-4fb7-8477-3f6a3429ea8c&quot;&gt;Et alors que Nicolas se permettait (tant qu&amp;#39;il pouvait) quelques critiques du langage Scala, l&amp;#39;auditoire a pu assister à des échanges entre le speaker et Martin. Dans les sujets des échanges, les &lt;i&gt;type class&lt;/i&gt;, leurs avantages et le fait d&amp;#39;avoir une syntaxe simplifiée dans les futures versions de Scala. Et aussi :&lt;/p&gt;
&lt;!-- end paragraph 36e360bf-dde1-4fb7-8477-3f6a3429ea8c--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Nicolas : Désolé Martin de faire cette critique 😬, mais je conseille d&amp;#39;éviter d&amp;#39;utiliser le type &lt;code&gt;Try&lt;/code&gt;. Préférez le type &lt;code&gt;Either&lt;/code&gt;, d&amp;#39;autant qu&amp;#39;avec ce type, il est possible de choisir le type pour les erreurs.&lt;/li&gt;&lt;li&gt;Martin : Je comprends, mais le type &lt;code&gt;Either&lt;/code&gt; est une abomination hérité d&amp;#39;Haskell ! &lt;code&gt;Try&lt;/code&gt; est un bon type et les exceptions sont une bonne représentation.&lt;/li&gt;&lt;li&gt;Nicolas : 😅&lt;/li&gt;&lt;li&gt;Quelqu&amp;#39;un dans l&amp;#39;auditoire : Oui mais les exceptions pose un problème de performance. Lancer une exception nécessite de réifier toute la pile d&amp;#39;appel !&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 948a869f-1fc6-424e-988f-1eb25ae2409a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-948a869f-1fc6-424e-988f-1eb25ae2409a&quot;&gt;Les puzzles vus ont été parcourus avec plus de temps que prévu par le speaker, si bien que seule la moitié des slides ont été présentées. Néanmoins, le déplacement valait le coup ! Pour terminer, afin de faire un &lt;i&gt;happy end&lt;/i&gt;, Martin a demandé à Nicolas hors séance de refaire le talk en anglais à ScalaDays !&lt;/p&gt;
&lt;!-- end paragraph 948a869f-1fc6-424e-988f-1eb25ae2409a--&gt;
&lt;!-- begin heading_2 1fe650c6-bf09-4f36-a62e-5c56e4304dab--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-1fe650c6-bf09-4f36-a62e-5c56e4304dab&quot;&gt;&lt;i&gt;Better, faster, stronger Coder. Improving data processing @Spotify&lt;/i&gt; par Julien Tournay&lt;/h3&gt;
&lt;!-- end heading_2 1fe650c6-bf09-4f36-a62e-5c56e4304dab--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph 56c74f9b-2b5b-451c-aecf-9d93fbdb3743--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-56c74f9b-2b5b-451c-aecf-9d93fbdb3743&quot;&gt;En dehors de ça, nous avons eu aussi des talks assez avancés. Ce fut le cas de l&amp;#39;excellent talk de Greg Pfeil intitulé &amp;quot;Category-parametric Programming&amp;quot;, sur la capacité à représenter la théorie des catégories à travers Scala. Ce fut le cas aussi de l&amp;#39;intervention de Julien Tournay de Spotify qui présentait SCio, une API en Scala et programmation fonctionnelle pour Apache Beam et Google Cloud Dataflow. Le talk s&amp;#39;intitulait &amp;quot;Better, faster, stronger Coder. Improving data processing @Spotify&amp;quot;. Il présentait les difficultés que posent la sérialisation en Java et le fait que ce soit... pire avec Scala.&lt;/p&gt;
&lt;!-- end paragraph 56c74f9b-2b5b-451c-aecf-9d93fbdb3743--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/img_20181030_150537.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph debdeb6f-e26d-4716-9554-3a65f30c51b8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-debdeb6f-e26d-4716-9554-3a65f30c51b8&quot;&gt;Julien nous a alors présenté Magnolia, une solution développée par Jon Pretty pour générer automatiquement des &lt;i&gt;typeclass&lt;/i&gt; pour &lt;i&gt;datatype&lt;/i&gt; décrits à partir de &lt;i&gt;case class&lt;/i&gt; et &lt;i&gt;sealed trait&lt;/i&gt;. Magnolia se base sur les macros Scala pour cette génération. Autrement dit, tout se passe à la compilation ! Ce qui permet de décharger la partie &lt;i&gt;runtime&lt;/i&gt;, contrairement à la réflexion. Julien utilise donc Magnolia pour enlever de la charge lors des phases de (dé)sérialisation. Il réussi à obtenir ainsi de meilleures performances au niveau de SCio. En aparté, une discussion s&amp;#39;est engagée entre Julien et Martin Odersky : Magnolia se base sur des macros &lt;i&gt;whitebox&lt;/i&gt;. Mais les versions futures de Scala verront leur disparition. Les versions futures de Scala représentent donc un risque pour SCio. Ce à quoi Martin promet de réfléchir.&lt;/p&gt;
&lt;!-- end paragraph debdeb6f-e26d-4716-9554-3a65f30c51b8--&gt;
&lt;!-- begin heading_2 b254f6d2-f75f-43b7-9e17-e093a5529b04--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-b254f6d2-f75f-43b7-9e17-e093a5529b04&quot;&gt;&lt;i&gt;High performance Privacy By Design using Matryoshka and Spark&lt;/i&gt; par Olivier Girardot et Wiem Zine Elabidine&lt;/h3&gt;
&lt;!-- end heading_2 b254f6d2-f75f-43b7-9e17-e093a5529b04--&gt;
&lt;!-- begin paragraph dd3b7176-a0f7-4bb8-8e2f-b5e54dbadcae--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dd3b7176-a0f7-4bb8-8e2f-b5e54dbadcae&quot;&gt;Du côté Data, il y avait un certain nombre de talks sur Spark et ainsi que les problématiques du &lt;i&gt;streaming&lt;/i&gt;. Notamment Frameless, présenté par Brian Clapper, un framework s&amp;#39;installant au-dessus de Spark et qui utilise Shapeless pour apporter plus de &lt;i&gt;type checking&lt;/i&gt; dans le code Spark. Il y avait aussi &amp;quot;7 conseils pour démarrer avec Spark&amp;quot; de Nastasia Saby et quelques retours d&amp;#39;expérience d&amp;#39;Ebiznext sur Spark et Kafka.&lt;/p&gt;
&lt;!-- end paragraph dd3b7176-a0f7-4bb8-8e2f-b5e54dbadcae--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph 58c8bed9-d61b-4904-81c2-efdccbe2d301--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-58c8bed9-d61b-4904-81c2-efdccbe2d301&quot;&gt;Il y avait enfin le talk d&amp;#39;Olivier Girardot et Wiem Zine Elabidine, intitulé &amp;quot;High performance Privacy By Design using Matryoshka and Spark&amp;quot; (sous la bienveillance de John De Goes). Si matryoshka fait référence à ces poupées russes qui s&amp;#39;emboitent les unes dans les autres, il était question ici du framework Scala implémentant les schémas de récursion (&lt;i&gt;recursion schemes&lt;/i&gt;), introduit par Erik Meijer &lt;i&gt;et al.&lt;/i&gt; dans un article intitulé &amp;quot;Generalized bananas, lenses and barbed wire&amp;quot;.&lt;/p&gt;
&lt;!-- end paragraph 58c8bed9-d61b-4904-81c2-efdccbe2d301--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/img_20181031_120857.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph 566fbb82-de76-46c5-9e01-82bd0beac373--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-566fbb82-de76-46c5-9e01-82bd0beac373&quot;&gt;Le contexte présenté par Olivier concerne la mise en place d&amp;#39;une approche &lt;i&gt;privacy by design&lt;/i&gt;, inscrit dans la réglementation européenne RGPD sur la protection des données privées et en application depuis le mois de mai de cette année. Dans un cadre BigData / Spark, il est nécessaire d&amp;#39;appliquer cette réglementation, en veillant à ce qu&amp;#39;une stratégie de &lt;i&gt;privacy&lt;/i&gt; soit appliquée aux colonnes qui le nécessitent, ce qui inclut l&amp;#39;anonymisation ou la pseudonymisation des données. Pour se faire, Olivier propose tout d&amp;#39;abord d&amp;#39;associer à chaque catégorie de colonnes représentée par un &lt;code&gt;Seq[String]&lt;/code&gt; une stratégie spécifique. Il représente alors ça à travers ce type : &lt;code&gt;Map[Seq[String], PrivacyStrategy]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 566fbb82-de76-46c5-9e01-82bd0beac373--&gt;
&lt;!-- begin paragraph 6fa01bc2-048a-409b-9055-0d2effa22cfe--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6fa01bc2-048a-409b-9055-0d2effa22cfe&quot;&gt;C&amp;#39;est pour mettre en pratique cette structure de données que l&amp;#39;équipe dans laquelle étaient Olivier et Wiem se sont intéressés à Matryoshka. Wiem nous a alors expliqué le principe derrière cette librairie et comment et dans quel cadre elle a été mise en place : la transformation des données intégrant la transformation de schéma en passant par un format pivot &lt;code&gt;SchemaF&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6fa01bc2-048a-409b-9055-0d2effa22cfe--&gt;
&lt;!-- begin heading_1 2d973f1f-9c1e-4d26-817d-ecea3431bb2d--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-2d973f1f-9c1e-4d26-817d-ecea3431bb2d&quot;&gt;Et Univalence ?&lt;/h2&gt;
&lt;!-- end heading_1 2d973f1f-9c1e-4d26-817d-ecea3431bb2d--&gt;
&lt;!-- begin paragraph b862ec2f-877f-4a60-aabb-eba10aaa69f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b862ec2f-877f-4a60-aabb-eba10aaa69f3&quot;&gt;Tout d&amp;#39;abord toute l&amp;#39;équipe Univalence était présente lors de ScalaIO. Un signe nous distinguait à ce moment : des t-shirts et des stickers avec les symboles suivants :&lt;/p&gt;
&lt;!-- end paragraph b862ec2f-877f-4a60-aabb-eba10aaa69f3--&gt;
&lt;!-- begin paragraph cd194fb1-a677-4e94-8fd8-20ed77d7ec9a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd194fb1-a677-4e94-8fd8-20ed77d7ec9a&quot;&gt;𝜆❤.&lt;br /&gt;
  (𝜆🐇.❤(🐇🐇))&lt;br /&gt;
  (𝜆🐇.❤(🐇🐇))&lt;/p&gt;
&lt;!-- end paragraph cd194fb1-a677-4e94-8fd8-20ed77d7ec9a--&gt;
&lt;!-- begin paragraph f40ab444-c1ae-444b-97b9-5f7677c1af90--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f40ab444-c1ae-444b-97b9-5f7677c1af90&quot;&gt;Nous vous laissons avec le mystère que cache ces symboles, mais comprenez qu&amp;#39;ils sont en rapport avec la programmation fonctionnelle et... les lapins !&lt;/p&gt;
&lt;!-- end paragraph f40ab444-c1ae-444b-97b9-5f7677c1af90--&gt;
&lt;!-- begin paragraph 5893ecdb-6fe9-4063-bebc-d249b22b9e23--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5893ecdb-6fe9-4063-bebc-d249b22b9e23&quot;&gt;Notre ressenti sur la conférence était globalement positif. Deux de nos data engineers ont d&amp;#39;ailleurs gagnés le livre de Sam Halliday &lt;a href=&quot;https://leanpub.com/fpmortals&quot;&gt;&lt;i&gt;Functional Programming for Mortals with ScalaZ&lt;/i&gt;&lt;/a&gt;&lt;i&gt;.&lt;/i&gt; Et notre CEO a aussi gagné grâce à Lunatech un mug Star Wars ^^. Néanmoins, pour la plupart d&amp;#39;entre-nous, il y avait à ScalaIO de la matière à discuter certes, mais aussi beaucoup de concepts et de pratiques à découvrir.&lt;/p&gt;
&lt;!-- end paragraph 5893ecdb-6fe9-4063-bebc-d249b22b9e23--&gt;
&lt;!-- begin paragraph e4da0be8-0af5-4867-accc-83c42af9b4b2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e4da0be8-0af5-4867-accc-83c42af9b4b2&quot;&gt;Et en tant que speaker, nous avons animé deux sessions :&lt;/p&gt;
&lt;!-- end paragraph e4da0be8-0af5-4867-accc-83c42af9b4b2--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Le workshop &amp;quot;Dans s&amp;#39;cas là mettons les mains dans le code&amp;quot; avec &lt;a href=&quot;https://www.linkedin.com/in/jeanhelou/&quot;&gt;Jean Helou&lt;/a&gt; (de Codamens) et
&lt;!-- begin mention --&gt;&lt;em class=&quot;block-mention&quot;&gt;@Jonathan Winandy&lt;/em&gt;
&lt;!-- end mention --&gt; .&lt;/li&gt;&lt;li&gt;Le short talk &amp;quot;Back to school : des exercices pour monter en compétence en FP&amp;quot; avec 
&lt;!-- begin mention --&gt;&lt;em class=&quot;block-mention&quot;&gt;@François Sarradin&lt;/em&gt;
&lt;!-- end mention --&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/5f2626f0-2032-4398-9cc0-f13643c479e4/20181030_133516.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 117d6de2-1a5e-4279-889b-c8746abf79d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-117d6de2-1a5e-4279-889b-c8746abf79d3&quot;&gt;Ces deux sessions sont destinées aux débutants en Scala et en programmation fonctionnelle. Elles tournent autour de la montée en compétence des novices et du &lt;i&gt;mentoring&lt;/i&gt; pour ces sujets.&lt;/p&gt;
&lt;!-- end paragraph 117d6de2-1a5e-4279-889b-c8746abf79d3--&gt;
&lt;!-- begin paragraph 5fffe9e8-202b-41f3-8eb2-a8813544748a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5fffe9e8-202b-41f3-8eb2-a8813544748a&quot;&gt;Car, même si nous avons une expertise en Scala, Spark et en programmation fonctionnelle, la montée en compétence dans ce domaine est un sujet qui nous anime, sachant que devons régulièrement y faire face aussi bien en interne, en particulier pour nos profils juniors, qu&amp;#39;auprès des équipes auprès desquelles nous intervenons ou de la communauté en général.&lt;/p&gt;
&lt;!-- end paragraph 5fffe9e8-202b-41f3-8eb2-a8813544748a--&gt;
&lt;!-- begin heading_1 378b5825-1443-4654-bac2-65669b0a5664--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-378b5825-1443-4654-bac2-65669b0a5664&quot;&gt;Autres points&lt;/h2&gt;
&lt;!-- end heading_1 378b5825-1443-4654-bac2-65669b0a5664--&gt;
&lt;!-- begin paragraph d344b2d0-d84a-4836-ba60-160eea42bfb1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d344b2d0-d84a-4836-ba60-160eea42bfb1&quot;&gt;N&amp;#39;oublions pas que ScalaIO est avant tout une conférence basée sur le &lt;b&gt;bénévolat&lt;/b&gt;, dont une bonne partie des frais sont principalement payés par les billets achetés et les sponsors. De plus, les membres de l&amp;#39;organisation sont avant tout des développeurs.&lt;/p&gt;
&lt;!-- end paragraph d344b2d0-d84a-4836-ba60-160eea42bfb1--&gt;
&lt;!-- begin paragraph 5b32ebbd-4540-459c-942d-f2268b4adf5a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5b32ebbd-4540-459c-942d-f2268b4adf5a&quot;&gt;Le lieu est assez simple d&amp;#39;accès, situé sur un campus avec le tramway pas trop loin. Néanmoins il faut prévoir un peu de marche sorti du tramway. En ce qui concerne le CPE, pas de problème particulier non plus. Les salles sont biens. Il y a juste eu quelques surprises avec le matériel de projections. Par contre, le réseau mobile Free y est capricieux et il ne fallait pas trop compter sur le Wifi (apparemment, c&amp;#39;était plus un problème d&amp;#39;infra au CPE et indépendant de l&amp;#39;organisation).&lt;/p&gt;
&lt;!-- end paragraph 5b32ebbd-4540-459c-942d-f2268b4adf5a--&gt;
&lt;!-- begin paragraph adfa9e56-5b5d-4c35-b40b-8ee4d8b0d314--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-adfa9e56-5b5d-4c35-b40b-8ee4d8b0d314&quot;&gt;Niveau intendance, rien à dire, avec la présence d&amp;#39;un traiteur (proposant des plats frais et la présence de plats végétariens) et d&amp;#39;un stand crêpe. Un effort était fait pour proposer des gobelets solides (du genre qui ne s&amp;#39;écrase pas lorsqu&amp;#39;on appuie dessus) et réutilisables.&lt;/p&gt;
&lt;!-- end paragraph adfa9e56-5b5d-4c35-b40b-8ee4d8b0d314--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph d53e3ebc-d806-421a-b2d5-48760027b6ef--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-d53e3ebc-d806-421a-b2d5-48760027b6ef&quot;&gt;Du point de vue stand, les sponsors étaient principalement des sponsors français, mais représentant assez bien la communauté Scala (eBizNext, NeoLynk, Teads, Criteo...). Le sponsor principal, Lunatech, a assuré une bonne animation avec des prix à gagner et une décoration spéciale pour &amp;quot;Helloween&amp;quot;.&lt;/p&gt;
&lt;!-- end paragraph d53e3ebc-d806-421a-b2d5-48760027b6ef--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/img_20181031_114313.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6d0af643-b5cb-4735-aa29-c7304eceaf97--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d0af643-b5cb-4735-aa29-c7304eceaf97&quot;&gt;Enfin, la &lt;i&gt;community party&lt;/i&gt; dans un ancien pétrolier réaménagé en lieu de réception fut très appréciable.&lt;/p&gt;
&lt;!-- end paragraph 6d0af643-b5cb-4735-aa29-c7304eceaf97--&gt;
&lt;!-- begin paragraph 8185d1a4-4e7d-428f-9c88-bf0f23ef44ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8185d1a4-4e7d-428f-9c88-bf0f23ef44ea&quot;&gt;Enfin, pour la première fois de son existence, l&amp;#39;organisation a ajouté un jour dédié aux workshops. Nous insistons sur l&amp;#39;aspect première fois, car il y a eu quelques couacs autour de l&amp;#39;organisation de ces workshops. En espérant, que l&amp;#39;année prochaine sera plus favorable sur point.&lt;/p&gt;
&lt;!-- end paragraph 8185d1a4-4e7d-428f-9c88-bf0f23ef44ea--&gt;
&lt;!-- begin heading_1 6a00f9c5-731b-4be5-9b87-7928c98fb6ee--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-6a00f9c5-731b-4be5-9b87-7928c98fb6ee&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 6a00f9c5-731b-4be5-9b87-7928c98fb6ee--&gt;&lt;div class=&quot;block-column_list level-0&quot;&gt;&lt;div class=&quot;container-fluid&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-6&quot;&gt;
&lt;!-- begin paragraph 8e931af8-bf61-46a5-a1eb-66964e5b71b5--&gt;
&lt;p class=&quot;block-paragraph level-2&quot; id=&quot;p-8e931af8-bf61-46a5-a1eb-66964e5b71b5&quot;&gt;L&amp;#39;un des avantages de ScalaIO, lorsqu&amp;#39;on s&amp;#39;intéresse à l&amp;#39;écosystème Scala et/ou à la programmation fonctionnelle, c&amp;#39;est de se sentir d&amp;#39;autant moins reclus. Pas de jugement, pas de réelles mauvaises critiques, la communauté a plus envie de faire avancer les sujets et est prête à accompagner et guider ceux qui souhaitent aller de l&amp;#39;avant.&lt;/p&gt;
&lt;!-- end paragraph 8e931af8-bf61-46a5-a1eb-66964e5b71b5--&gt;&lt;/div&gt;&lt;div class=&quot;col-6&quot;&gt;&lt;div class=&quot;block-image level-2&quot;&gt;&lt;img src=&quot;/assets/img_20181031_134031.jpg&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- begin paragraph 262c2135-7568-451a-ad0f-73ef7f289802--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-262c2135-7568-451a-ad0f-73ef7f289802&quot;&gt;De plus, au travers des talks mais aussi au travers des discussions de &amp;quot;couloir&amp;quot; que vous pouvez avoir, l&amp;#39;état d&amp;#39;esprit de toutes les personnes présentes à ScalaIO (dont nous pouvions compter beaucoup de contributeurs à l&amp;#39;écosystème Scala en France et dans le monde) font que cette conférence est une bonne expérience. Et c&amp;#39;est toujours un peu triste de voir ScalaIO se terminer, néanmoins avec une certaine fierté d&amp;#39;être membre de cette communauté.&lt;/p&gt;
&lt;!-- end paragraph 262c2135-7568-451a-ad0f-73ef7f289802--&gt;
&lt;!-- begin paragraph f51b5360-aa4a-450d-bcf9-4cd2f53d94cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f51b5360-aa4a-450d-bcf9-4cd2f53d94cc&quot;&gt;Merci à tous ceux qui ont fait ScalaIO 2018 et à l&amp;#39;année prochaine !&lt;/p&gt;
&lt;!-- end paragraph f51b5360-aa4a-450d-bcf9-4cd2f53d94cc--&gt;
&lt;!-- begin paragraph e509cb4d-2acf-47b2-8869-cfee025dcab5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e509cb4d-2acf-47b2-8869-cfee025dcab5&quot;&gt;&lt;i&gt;Merci &lt;/i&gt;
&lt;!-- begin mention --&gt;&lt;em class=&quot;block-mention&quot;&gt;@Bernarith Men&lt;/em&gt;
&lt;!-- end mention --&gt;&lt;i&gt; &lt;/i&gt;
&lt;!-- begin mention --&gt;&lt;em class=&quot;block-mention&quot;&gt;@Anonymous&lt;/em&gt;
&lt;!-- end mention --&gt;&lt;i&gt; &lt;/i&gt;
&lt;!-- begin mention --&gt;&lt;em class=&quot;block-mention&quot;&gt;@François Sarradin&lt;/em&gt;
&lt;!-- end mention --&gt;&lt;i&gt; et &lt;/i&gt;
&lt;!-- begin mention --&gt;&lt;em class=&quot;block-mention&quot;&gt;@Jonathan Winandy&lt;/em&gt;
&lt;!-- end mention --&gt;&lt;i&gt; pour les photos. En ajoutant &lt;/i&gt;
&lt;!-- begin mention --&gt;&lt;em class=&quot;block-mention&quot;&gt;@Anonymous&lt;/em&gt;
&lt;!-- end mention --&gt;&lt;i&gt; et @Brahim Boukalit pour leur participation.&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph e509cb4d-2acf-47b2-8869-cfee025dcab5--&gt;
&lt;!-- begin paragraph ff4e742b-3e07-458f-b42d-ccdea7104e26--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ff4e742b-3e07-458f-b42d-ccdea7104e26&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ff4e742b-3e07-458f-b42d-ccdea7104e26--&gt;
&lt;!-- begin paragraph 498952b0-9052-4d5f-96fa-983dd2998eb4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-498952b0-9052-4d5f-96fa-983dd2998eb4&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 498952b0-9052-4d5f-96fa-983dd2998eb4--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:42c9c9efd45c45e79ff344d31492c4a4</id>
    <title>Implicit Encoder / SparkSession / Configuration, quelques astuces pour structurer du code Spark</title>
    <updated>2018-11-20T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/implicit-encoder-sparksession-configuration-quelques-astuces-pour-structurer-du-code-spark.html"/>
    <!--summary -->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="Spark"></category>    <category term="Scala"></category>    <category term="Implicit"></category>    <content type="html">&lt;div class=&quot;block-video level-0&quot;&gt;&lt;figure class=&quot;figure d-block&quot;&gt;&lt;div class=&quot;player giphy&quot;&gt;&lt;iframe src=&quot;https://giphy.com/embed/QmH4MuISBE1dyPmTAy&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;Un post bateau, mais cool !&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;!-- begin divider f9bf7fc9-7f79-4d84-8eac-5957ede70b08--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-f9bf7fc9-7f79-4d84-8eac-5957ede70b08&quot;&gt;
&lt;!-- end divider f9bf7fc9-7f79-4d84-8eac-5957ede70b08--&gt;
&lt;!-- begin paragraph 9f3eb5cb-f1e9-4ef6-93f7-0c62e7ecc62d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9f3eb5cb-f1e9-4ef6-93f7-0c62e7ecc62d&quot;&gt;Parfois on veut refactorer du code avec Spark et on finit assez rapidement par : &lt;/p&gt;
&lt;!-- end paragraph 9f3eb5cb-f1e9-4ef6-93f7-0c62e7ecc62d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;soit avoir une SparkSession en implicit :
&lt;!-- begin code d8fe74b7-b471-462e-be3d-815aa7bd555a--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-d8fe74b7-b471-462e-be3d-815aa7bd555a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def superFonction(df: DataFrame)(implicit sparkSession: SparkSession): DataFrame = {
  ???
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d8fe74b7-b471-462e-be3d-815aa7bd555a--&gt;&lt;/li&gt;&lt;li&gt;soit se retrouver avec d&amp;#39;autres implicits partout :
&lt;!-- begin code 6c01560e-99be-4186-a619-300b7c1627e7--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-6c01560e-99be-4186-a619-300b7c1627e7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def superFonction2(df: DataFrame)(implicit sparkSession: SparkSession,
                                            config: com.typesafe.config.Config): DataFrame = {
  ???
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6c01560e-99be-4186-a619-300b7c1627e7--&gt;&lt;/li&gt;&lt;li&gt;soit des erreurs d&amp;#39;implicit quand on réarrange le code  : 
&lt;!-- begin code feed9b76-28b5-4486-94b0-1c01e9b57de7--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-feed9b76-28b5-4486-94b0-1c01e9b57de7&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def groupByKeyRemoveNull[A, K](dataset: Dataset[A])(f: A =&amp;gt; Option[K]): Dataset[(K, Seq[A])] = {
      dataset
        .flatMap(x =&amp;gt; f(x).map(y =&amp;gt; (y, x)))
        .groupByKey(_._1)
        .mapGroups({ case (k, it) =&amp;gt; (k, it.map(_._2).toSeq) })
    }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code feed9b76-28b5-4486-94b0-1c01e9b57de7--&gt;
&lt;!-- begin code 887913c2-d902-4061-91a7-d770ce8ded6c--&gt;
&lt;pre class=&quot;block-code level-1&quot; id=&quot;c-887913c2-d902-4061-91a7-d770ce8ded6c&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;Unable to find encoder for type (K, A). An implicit Encoder[(K, A)] is needed to store (K, A) instances in a Dataset. Primitive types (Int, String, etc) and Product types (case classes) are supported by importing spark.implicits._  Support for serializing other types will be added in future releases.
        .flatMap(x =&amp;gt; f(x).map(y =&amp;gt; (y, x)))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 887913c2-d902-4061-91a7-d770ce8ded6c--&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 331d659b-2002-43c6-ba20-6644f7237e79--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-331d659b-2002-43c6-ba20-6644f7237e79&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 331d659b-2002-43c6-ba20-6644f7237e79--&gt;
&lt;!-- begin paragraph 7b19ec46-5d37-456d-92c0-fe69d0bf5cf0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7b19ec46-5d37-456d-92c0-fe69d0bf5cf0&quot;&gt;On va voir quelques astuces pour améliorer tout ça !&lt;/p&gt;
&lt;!-- end paragraph 7b19ec46-5d37-456d-92c0-fe69d0bf5cf0--&gt;
&lt;!-- begin heading_1 22cfc60d-f95d-49bf-b8b7-d5a53daa432c--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-22cfc60d-f95d-49bf-b8b7-d5a53daa432c&quot;&gt;1re astuce : SparkSession est un membre de Dataframe/Dataset&lt;/h2&gt;
&lt;!-- end heading_1 22cfc60d-f95d-49bf-b8b7-d5a53daa432c--&gt;
&lt;!-- begin paragraph 40b62551-d578-4faa-9ad8-9e8e9fe34f15--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-40b62551-d578-4faa-9ad8-9e8e9fe34f15&quot;&gt;On a rarement besoin d&amp;#39;avoir la SparkSession en implicit, elle est disponible sur les dataframes ou datasets en paramètre : &lt;/p&gt;
&lt;!-- end paragraph 40b62551-d578-4faa-9ad8-9e8e9fe34f15--&gt;
&lt;!-- begin code 9a935011-a03f-412f-9396-707e19c7435e--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9a935011-a03f-412f-9396-707e19c7435e&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def superFonction(df: DataFrame): DataFrame = {
   val ss = df.sparkSession
   ???
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9a935011-a03f-412f-9396-707e19c7435e--&gt;
&lt;!-- begin heading_1 73faaec2-581e-4336-806e-1887a6dbd308--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-73faaec2-581e-4336-806e-1887a6dbd308&quot;&gt;2e astuce : Et si on faisait une classe ?&lt;/h2&gt;
&lt;!-- end heading_1 73faaec2-581e-4336-806e-1887a6dbd308--&gt;
&lt;!-- begin paragraph b862c8fe-4fc0-4282-b8d5-d8bc7cc7d4d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b862c8fe-4fc0-4282-b8d5-d8bc7cc7d4d3&quot;&gt;Dans beaucoup de base de code en Scala/Spark, pour passer/injecter les éléments de configuration supplémentaire, on peut être tenté d&amp;#39;utiliser le mécanisme des implicits, si cela revient trop souvent, cela va être plus cohérent d&amp;#39;utiliser une &lt;code&gt;classe&lt;/code&gt; à la place. &lt;/p&gt;
&lt;!-- end paragraph b862c8fe-4fc0-4282-b8d5-d8bc7cc7d4d3--&gt;
&lt;!-- begin paragraph afe928cd-4252-4467-9eb2-0b7f64e88026--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-afe928cd-4252-4467-9eb2-0b7f64e88026&quot;&gt;Au lieu de faire :&lt;/p&gt;
&lt;!-- end paragraph afe928cd-4252-4467-9eb2-0b7f64e88026--&gt;
&lt;!-- begin code ebe7ce79-0994-46a5-89d0-e2f4882ae452--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ebe7ce79-0994-46a5-89d0-e2f4882ae452&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;object MonJob {
    def superFonction2(df: DataFrame)(implicit config: com.typesafe.config.Config): DataFrame = ???

    def superFonction3(df:DataFrame, i:Int)(implicit config: com.typesafe.config.Config):DataFrame = ???
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ebe7ce79-0994-46a5-89d0-e2f4882ae452--&gt;
&lt;!-- begin paragraph cf8190b0-1074-4572-a89d-6046888cc452--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cf8190b0-1074-4572-a89d-6046888cc452&quot;&gt;on va avoir :&lt;/p&gt;
&lt;!-- end paragraph cf8190b0-1074-4572-a89d-6046888cc452--&gt;
&lt;!-- begin code 2c7c483c-31c9-4991-96df-a49cb3b11e0a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2c7c483c-31c9-4991-96df-a49cb3b11e0a&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;class MonJob(config: com.typesafe.config.Config) {
    def superFonction2(df: DataFrame): DataFrame = ???

    def superFonction3(df:DataFrame, i:Int):DataFrame = ???
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2c7c483c-31c9-4991-96df-a49cb3b11e0a--&gt;
&lt;!-- begin heading_1 994250cc-85da-441e-b719-75d522e756da--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-994250cc-85da-441e-b719-75d522e756da&quot;&gt;2e bis : c&amp;#39;est mieux de typer un peu ses configurations&lt;/h2&gt;
&lt;!-- end heading_1 994250cc-85da-441e-b719-75d522e756da--&gt;
&lt;!-- begin paragraph a2e9b7a8-cf05-4136-952c-848504bad9be--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a2e9b7a8-cf05-4136-952c-848504bad9be&quot;&gt;Les configurations Spark ou HOCON sont des formes de Json peu typé. Pour faciliter la maintenance et rendre le runtime plus robuste, cela peut valoir le coup de passer en mode structuré typé :&lt;/p&gt;
&lt;!-- end paragraph a2e9b7a8-cf05-4136-952c-848504bad9be--&gt;
&lt;!-- begin code 06ef62e1-b1ae-41c4-b4a7-a37dd3e6f55b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-06ef62e1-b1ae-41c4-b4a7-a37dd3e6f55b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class MonJobConfig(config1: String, config2: Int)

object MonJobConfig {
    def fromConfig(config: com.typesafe.config.Config): Try[MonJobConfig] = ???
}

class MonJob(config: MonJobConfig) {
    def superFonction2(df: DataFrame): DataFrame = ???

    def superFonction3(df: DataFrame; i:Int): DataFrame = ???
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 06ef62e1-b1ae-41c4-b4a7-a37dd3e6f55b--&gt;
&lt;!-- begin paragraph 1cac0fb1-5e77-4096-b636-c4839682c49e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1cac0fb1-5e77-4096-b636-c4839682c49e&quot;&gt;Aussi cela permet de valider la configuration du programme avant de démarrer les traitements (via MonJobConfig.fromConfig(...) qui renvoit un Try) et aussi de voir clairement quelle partie du programme utilise telle configuration. (alt+F7 sur IntelliJ, find usages)&lt;/p&gt;
&lt;!-- end paragraph 1cac0fb1-5e77-4096-b636-c4839682c49e--&gt;
&lt;!-- begin paragraph 3cde03fe-89f1-4b74-9513-e0ba55d799d9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3cde03fe-89f1-4b74-9513-e0ba55d799d9&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 3cde03fe-89f1-4b74-9513-e0ba55d799d9--&gt;
&lt;!-- begin heading_1 dbcf8898-02e8-458e-8fb2-739f5bb4cd07--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-dbcf8898-02e8-458e-8fb2-739f5bb4cd07&quot;&gt;3e : allo, il manque des encodeurs&lt;/h2&gt;
&lt;!-- end heading_1 dbcf8898-02e8-458e-8fb2-739f5bb4cd07--&gt;
&lt;!-- begin paragraph b8adc932-3141-475d-bb7a-fd5cf67fb359--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b8adc932-3141-475d-bb7a-fd5cf67fb359&quot;&gt;Quand vous allez vouloir générifier du code en spark, il va vous manquer des implicits. Contrairement aux erreurs d&amp;#39;implicit classiques qui consistent à importer &lt;code&gt;ss.implicits._&lt;/code&gt;, ici il faut récupérer les implicits manquants.&lt;/p&gt;
&lt;!-- end paragraph b8adc932-3141-475d-bb7a-fd5cf67fb359--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/42c9c9ef-d45c-45e7-9ff3-44d31492c4a4/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 672dcb5c-0afd-44ef-bb5c-6bdd0d8e0b89--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-672dcb5c-0afd-44ef-bb5c-6bdd0d8e0b89&quot;&gt;Dans la dernière version d&amp;#39;intelliJ, on peut voir les implicits &amp;quot;Show implicit hints&amp;quot;&lt;/p&gt;
&lt;!-- end paragraph 672dcb5c-0afd-44ef-bb5c-6bdd0d8e0b89--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/42c9c9ef-d45c-45e7-9ff3-44d31492c4a4/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 9e4a6296-f060-4402-a515-6b319775e9c1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9e4a6296-f060-4402-a515-6b319775e9c1&quot;&gt;IntelliJ est alors assez clair sur ce qui manque. On va ouvrir un troisième groupe d&amp;#39;arguments pour les récupérer &lt;code&gt;(implicit xxx)&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 9e4a6296-f060-4402-a515-6b319775e9c1--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/42c9c9ef-d45c-45e7-9ff3-44d31492c4a4/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 43515c81-9b4e-480f-a524-eb532b353713--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-43515c81-9b4e-480f-a524-eb532b353713&quot;&gt;Puis rajouter tous les implicits manquant à ce groupe :&lt;/p&gt;
&lt;!-- end paragraph 43515c81-9b4e-480f-a524-eb532b353713--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/42c9c9ef-d45c-45e7-9ff3-44d31492c4a4/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph b9e59ef4-9f81-4dce-8120-f6a9e3e794d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b9e59ef4-9f81-4dce-8120-f6a9e3e794d2&quot;&gt;Une fois que c&amp;#39;est fini, on peut enlever le mode X-Ray-Implicit d&amp;#39;intelliJ et passer aux tests sur nos fonctions polymorphiques (&lt;code&gt;[A,K]&lt;/code&gt;) de plus haut niveau (&lt;code&gt;f: A ⇒ Option[K]&lt;/code&gt;) :&lt;/p&gt;
&lt;!-- end paragraph b9e59ef4-9f81-4dce-8120-f6a9e3e794d2--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/42c9c9ef-d45c-45e7-9ff3-44d31492c4a4/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 6cfe34ef-108b-4829-b251-06350592a252--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6cfe34ef-108b-4829-b251-06350592a252&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 6cfe34ef-108b-4829-b251-06350592a252--&gt;
&lt;!-- begin heading_1 e7f272c7-2239-455a-8a7a-4cba7ad45bad--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e7f272c7-2239-455a-8a7a-4cba7ad45bad&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 e7f272c7-2239-455a-8a7a-4cba7ad45bad--&gt;
&lt;!-- begin paragraph d306b3ca-b15e-4e0f-81a4-048947cb91c9--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d306b3ca-b15e-4e0f-81a4-048947cb91c9&quot;&gt;On a présenté deux trois astuces pour remettre au carré du code Scala qui utilise Spark, cela suffit à faire du code beaucoup plus propre dans de nombreux cas. Pour les autres cas, il faut souvent passer par la manipulation de schema avec spark, on va laisser ça pour d&amp;#39;autres articles sur le sujet !&lt;/p&gt;
&lt;!-- end paragraph d306b3ca-b15e-4e0f-81a4-048947cb91c9--&gt;
&lt;!-- begin paragraph f1ddbfab-0a9e-485b-b633-3a2f12fdd553--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f1ddbfab-0a9e-485b-b633-3a2f12fdd553&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph f1ddbfab-0a9e-485b-b633-3a2f12fdd553--&gt;
&lt;!-- begin paragraph 85e04ff2-98bb-4057-8896-3a0b17dec60f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-85e04ff2-98bb-4057-8896-3a0b17dec60f&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 85e04ff2-98bb-4057-8896-3a0b17dec60f--&gt;
&lt;!-- begin paragraph 7865dca3-94b9-4b6f-b935-298387518150--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7865dca3-94b9-4b6f-b935-298387518150&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 7865dca3-94b9-4b6f-b935-298387518150--&gt;
&lt;!-- begin paragraph c7459cc0-30ba-4d2a-9c61-d0296b4f18a6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c7459cc0-30ba-4d2a-9c61-d0296b4f18a6&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c7459cc0-30ba-4d2a-9c61-d0296b4f18a6--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:4c229b2072c247b1b8a1d17c592faa2b</id>
    <title>Alignement de schémas (union)</title>
    <updated>2018-11-18T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/alignement-de-schemas-union.html"/>
    <!--summary -->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="Spark"></category>    <category term="Scala"></category>    <content type="html">
&lt;!-- begin paragraph f9036001-8d7a-4ef5-a1c4-53bd90ec0da8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f9036001-8d7a-4ef5-a1c4-53bd90ec0da8&quot;&gt;Il est parfois nécessaire de prendre des données qui viennent de sources différentes et de les combiner en une seule source. En SQL, pour ça nous utilisons UNION ou UNION ALL.&lt;/p&gt;
&lt;!-- end paragraph f9036001-8d7a-4ef5-a1c4-53bd90ec0da8--&gt;
&lt;!-- begin paragraph 1c594006-2753-4555-9dfe-ccd7814f54cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1c594006-2753-4555-9dfe-ccd7814f54cf&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 1c594006-2753-4555-9dfe-ccd7814f54cf--&gt;&lt;div class=&quot;block-image level-0&quot;&gt;&lt;img src=&quot;/blog/assets/4c229b20-72c2-47b1-b8a1-d17c592faa2b/untitled.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;!-- begin paragraph 2c6cf4c9-d506-4be0-ac25-32154b769f11--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2c6cf4c9-d506-4be0-ac25-32154b769f11&quot;&gt;En Spark, nous pouvons faire de même. Néanmoins, cela ne marche pas toujours comme nous le pensons. L&amp;#39;union en Spark &lt;code&gt;df1.union(df2)&lt;/code&gt; a le même comportement qu&amp;#39;en SQL, ce qui peut poser des interrogations lorsque l&amp;#39;on utilise les datasets (le mode &amp;quot;typé-scala&amp;quot; de Spark) : le code compile, mais nous pouvons nous retrouver avec une erreur au &lt;i&gt;runtime&lt;/i&gt; (dans le driver) ou pire, avec une erreur silencieuse pour les plus chanceux.&lt;/p&gt;
&lt;!-- end paragraph 2c6cf4c9-d506-4be0-ac25-32154b769f11--&gt;
&lt;!-- begin heading_1 d61c25b2-351a-4ba3-82f3-6f8c646c2eb0--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d61c25b2-351a-4ba3-82f3-6f8c646c2eb0&quot;&gt;En exemple&lt;/h2&gt;
&lt;!-- end heading_1 d61c25b2-351a-4ba3-82f3-6f8c646c2eb0--&gt;
&lt;!-- begin paragraph 68f7db7d-b732-4384-a643-8fe5a59e67f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-68f7db7d-b732-4384-a643-8fe5a59e67f3&quot;&gt;Nous allons manipuler des données qui ressemblent à &lt;code&gt;A&lt;/code&gt; (avec deux champs &lt;code&gt;b&lt;/code&gt; et &lt;code&gt;c&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph 68f7db7d-b732-4384-a643-8fe5a59e67f3--&gt;
&lt;!-- begin code 96e713a1-8555-49ce-a706-46b09bda2aae--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-96e713a1-8555-49ce-a706-46b09bda2aae&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class A(b: String, c: Long)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 96e713a1-8555-49ce-a706-46b09bda2aae--&gt;
&lt;!-- begin paragraph 82a537d2-3aa9-4c53-abb4-5aa250e6ac00--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-82a537d2-3aa9-4c53-abb4-5aa250e6ac00&quot;&gt;Créons deux &lt;code&gt;Dataset[A]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 82a537d2-3aa9-4c53-abb4-5aa250e6ac00--&gt;
&lt;!-- begin code a436f1f7-059e-4cb0-91b0-e7a94ec664d2--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a436f1f7-059e-4cb0-91b0-e7a94ec664d2&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val ds1 = smallDs(A(&amp;quot;1&amp;quot;, 2), A(&amp;quot;3&amp;quot;, 4))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a436f1f7-059e-4cb0-91b0-e7a94ec664d2--&gt;
&lt;!-- begin paragraph ad533be9-3d67-488c-a9bf-b1f9e14204ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ad533be9-3d67-488c-a9bf-b1f9e14204ed&quot;&gt;Ici &lt;code&gt;smallDs&lt;/code&gt;, qui derrière fait appel à &lt;code&gt;session.sparkContext.parallelize(data, 1).toDS&lt;/code&gt;. Un &lt;i&gt;show&lt;/i&gt; sur &lt;code&gt;ds1&lt;/code&gt; donne :&lt;/p&gt;
&lt;!-- end paragraph ad533be9-3d67-488c-a9bf-b1f9e14204ed--&gt;
&lt;!-- begin code 901ed0cb-a48e-48a2-8d25-7263eea4b3eb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-901ed0cb-a48e-48a2-8d25-7263eea4b3eb&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+---+
|b  |c  |
+---+---+
|1  |2  |
|3  |4  |
+---+---+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 901ed0cb-a48e-48a2-8d25-7263eea4b3eb--&gt;
&lt;!-- begin paragraph 87d36d8c-7f10-42e0-bfdc-cc8f32dc7ae7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-87d36d8c-7f10-42e0-bfdc-cc8f32dc7ae7&quot;&gt;Voici le deuxième &lt;i&gt;dataset&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 87d36d8c-7f10-42e0-bfdc-cc8f32dc7ae7--&gt;
&lt;!-- begin code 337e4d34-2419-4eab-9307-6d0370fdacbd--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-337e4d34-2419-4eab-9307-6d0370fdacbd&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val ds2: Dataset[A] = dfFromJson(&amp;quot;{b:&amp;#39;5&amp;#39;,c:6, d:7}&amp;quot;, &amp;quot;{b:&amp;#39;8&amp;#39;,c:9, d:10}&amp;quot;).as[A]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 337e4d34-2419-4eab-9307-6d0370fdacbd--&gt;
&lt;!-- begin code 24bf42f3-78fe-4e5f-98ba-462c91123d82--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-24bf42f3-78fe-4e5f-98ba-462c91123d82&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---+---+---+
|b  |c  |d  |
+---+---+---+
|5  |6  |7  |
|8  |9  |10 |
+---+---+---+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 24bf42f3-78fe-4e5f-98ba-462c91123d82--&gt;
&lt;!-- begin paragraph 008d6b95-9360-499e-9d05-4881c76ac475--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-008d6b95-9360-499e-9d05-4881c76ac475&quot;&gt;Nous pouvons noter déjà que &lt;code&gt;ds2&lt;/code&gt; n&amp;#39;a pas le même schéma que &lt;code&gt;ds1&lt;/code&gt; alors qu&amp;#39;il sont du même type. Si nous demandons à Spark de lire de la donnée et de nous retourner un type &lt;code&gt;A&lt;/code&gt; en Scala, il vérifie juste si c&amp;#39;est possible de le faire au moment venu.&lt;br /&gt;
&lt;br /&gt;
Par contre, si nous tentons l&amp;#39;union entre les deux &lt;i&gt;datasets&lt;/i&gt;, cela ne va pas passer.&lt;/p&gt;
&lt;!-- end paragraph 008d6b95-9360-499e-9d05-4881c76ac475--&gt;
&lt;!-- begin code 3047583e-18b2-4c5f-9394-775d8187e089--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3047583e-18b2-4c5f-9394-775d8187e089&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;assertDsEqual(ds1.union(ds2), A(&amp;quot;1&amp;quot;, 2), A(&amp;quot;3&amp;quot;, 4), A(&amp;quot;5&amp;quot;, 6), A(&amp;quot;8&amp;quot;, 9))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3047583e-18b2-4c5f-9394-775d8187e089--&gt;
&lt;!-- begin code 3e857a81-937e-4e37-9c91-f5a0de93c0af--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3e857a81-937e-4e37-9c91-f5a0de93c0af&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Union can only be performed on tables with the same number of columns, but the first table has 2 columns and the second table has 3 columns&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3e857a81-937e-4e37-9c91-f5a0de93c0af--&gt;
&lt;!-- begin paragraph 6d179320-23d4-4dc9-b885-cd52dd08eed5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6d179320-23d4-4dc9-b885-cd52dd08eed5&quot;&gt;Une façon assez simple de régler le problème, dans ce cas spécifique, consiste d&amp;#39;enlever le champ en trop dans &lt;code&gt;ds2&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 6d179320-23d4-4dc9-b885-cd52dd08eed5--&gt;
&lt;!-- begin code 83da0c30-b08b-4e8d-9917-57f2f662425b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-83da0c30-b08b-4e8d-9917-57f2f662425b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val ds3: Dataset[A] = ds2.drop(&amp;quot;d&amp;quot;).as[A]
assertDsEqual(ds1.union(ds3), A(&amp;quot;1&amp;quot;, 2), A(&amp;quot;3&amp;quot;, 4), A(&amp;quot;5&amp;quot;, 6), A(&amp;quot;8&amp;quot;, 9))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 83da0c30-b08b-4e8d-9917-57f2f662425b--&gt;
&lt;!-- begin paragraph e3c07122-e4b7-4b0b-8650-f6e7db551855--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e3c07122-e4b7-4b0b-8650-f6e7db551855&quot;&gt;Ça marchera, mais sans que nous n&amp;#39;ayons de véritable garantie sur l&amp;#39;ordre des champs restants.&lt;/p&gt;
&lt;!-- end paragraph e3c07122-e4b7-4b0b-8650-f6e7db551855--&gt;
&lt;!-- begin paragraph 61c5d5e2-3a0a-4ef6-93ca-b5b39e8ac47a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-61c5d5e2-3a0a-4ef6-93ca-b5b39e8ac47a&quot;&gt;Par exemple, si on tente ceci :&lt;/p&gt;
&lt;!-- end paragraph 61c5d5e2-3a0a-4ef6-93ca-b5b39e8ac47a--&gt;
&lt;!-- begin code 6f296ce2-0e7b-4251-b722-ebc52885ba8b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-6f296ce2-0e7b-4251-b722-ebc52885ba8b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val ds3: Dataset[A] = ds2.select(&amp;quot;c&amp;quot;,&amp;quot;b&amp;quot;).as[A]
assertDsEqual(ds1.union(ds3), A(&amp;quot;1&amp;quot;, 2), A(&amp;quot;3&amp;quot;, 4), A(&amp;quot;5&amp;quot;, 6), A(&amp;quot;8&amp;quot;, 9))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 6f296ce2-0e7b-4251-b722-ebc52885ba8b--&gt;
&lt;!-- begin paragraph a43103c2-0d65-4727-924d-0c761a852038--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a43103c2-0d65-4727-924d-0c761a852038&quot;&gt;ça compile, mais on se retrouve avec une erreur dans le driver :&lt;/p&gt;
&lt;!-- end paragraph a43103c2-0d65-4727-924d-0c761a852038--&gt;
&lt;!-- begin code af4f6d5f-238f-492d-9246-0f80f24c6cc8--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-af4f6d5f-238f-492d-9246-0f80f24c6cc8&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Cannot up cast `c` from string to bigint ...&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code af4f6d5f-238f-492d-9246-0f80f24c6cc8--&gt;
&lt;!-- begin paragraph 29273ea6-c939-42db-b3d3-a69412ef6ea8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-29273ea6-c939-42db-b3d3-a69412ef6ea8&quot;&gt;Néanmoins, si les &lt;i&gt;datasets&lt;/i&gt; ont déjà le même nombre de champ, nous pouvons utiliser &lt;code&gt;unionByName&lt;/code&gt; (depuis Spark 2.3.0) :&lt;/p&gt;
&lt;!-- end paragraph 29273ea6-c939-42db-b3d3-a69412ef6ea8--&gt;
&lt;!-- begin code 027610cf-a7e2-43dc-81b8-3035b8d374b9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-027610cf-a7e2-43dc-81b8-3035b8d374b9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val ds3: Dataset[A] = ds2.drop(&amp;quot;d&amp;quot;).as[A]
assertDsEqual(ds1.unionByName(ds3), A(&amp;quot;1&amp;quot;, 2), A(&amp;quot;3&amp;quot;, 4), A(&amp;quot;5&amp;quot;, 6), A(&amp;quot;8&amp;quot;, 9))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 027610cf-a7e2-43dc-81b8-3035b8d374b9--&gt;
&lt;!-- begin heading_1 57a4828d-0889-46e4-abb8-0aec128244fe--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-57a4828d-0889-46e4-abb8-0aec128244fe&quot;&gt;Réglons le problème définitivement&lt;/h2&gt;
&lt;!-- end heading_1 57a4828d-0889-46e4-abb8-0aec128244fe--&gt;
&lt;!-- begin paragraph 7a4bfbd5-feb4-41f8-8f89-c3187a063a33--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7a4bfbd5-feb4-41f8-8f89-c3187a063a33&quot;&gt;Les opérateurs d&amp;#39;union existant sur les &lt;i&gt;datasets&lt;/i&gt; ne sont pas suffisants aujourd&amp;#39;hui pour aligner des schémas. Ils nécessitent trop de manipulations manuelles.&lt;/p&gt;
&lt;!-- end paragraph 7a4bfbd5-feb4-41f8-8f89-c3187a063a33--&gt;
&lt;!-- begin paragraph 9dbd2d82-ceed-412b-8b29-c382265852c8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9dbd2d82-ceed-412b-8b29-c382265852c8&quot;&gt;En fait, ce que nous souhaitons, c&amp;#39;est une fonction de la forme : &lt;/p&gt;
&lt;!-- end paragraph 9dbd2d82-ceed-412b-8b29-c382265852c8--&gt;
&lt;!-- begin code 2eb6e139-50c2-478f-b337-04af22a6e7ab--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2eb6e139-50c2-478f-b337-04af22a6e7ab&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def betterDatasetUnion[A](ds1:Dataset[A],ds2:Dataset[A]):Dataset[A]
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2eb6e139-50c2-478f-b337-04af22a6e7ab--&gt;
&lt;!-- begin paragraph cd13a239-d993-4f71-bdba-1be87d4a55f3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cd13a239-d993-4f71-bdba-1be87d4a55f3&quot;&gt;Pour cette implémentation, nous allons nous baser sur le schéma de &lt;code&gt;A&lt;/code&gt;, qui se récupère assez facilement via un &lt;code&gt;Encoder[A]&lt;/code&gt;, membre de &lt;a href=&quot;https://github.com/apache/spark/blob/034ae305c33b1990b3c1a284044002874c343b4d/sql/core/src/main/scala/org/apache/spark/sql/Dataset.scala#L208&quot;&gt;&lt;code&gt;Dataset[A]&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph cd13a239-d993-4f71-bdba-1be87d4a55f3--&gt;
&lt;!-- begin code 60d90d50-8262-492f-974e-d97b623b4615--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-60d90d50-8262-492f-974e-d97b623b4615&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val schema:StructType = ds1.exprEnc.schema&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 60d90d50-8262-492f-974e-d97b623b4615--&gt;
&lt;!-- begin paragraph 2cbdec9b-e45d-4a62-b7a1-ee42fba18c5b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2cbdec9b-e45d-4a62-b7a1-ee42fba18c5b&quot;&gt;Le schéma nous donne les champs disponibles en utilisant &lt;code&gt;schema.fieldnames&lt;/code&gt;. Ce sont en fait les champs de la case classe &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 2cbdec9b-e45d-4a62-b7a1-ee42fba18c5b--&gt;
&lt;!-- begin code e28b303e-786b-434e-bd49-fe7b7dafb3cb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e28b303e-786b-434e-bd49-fe7b7dafb3cb&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val fieldnames = schema.fieldnames&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e28b303e-786b-434e-bd49-fe7b7dafb3cb--&gt;
&lt;!-- begin paragraph 52378c1b-c9ee-4607-ba10-37f8ffe88ff6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-52378c1b-c9ee-4607-ba10-37f8ffe88ff6&quot;&gt;Nous pouvons ensuite filtrer les &lt;i&gt;dataframes&lt;/i&gt; avec les champs de la &lt;i&gt;case class&lt;/i&gt; avant d&amp;#39;utiliser &lt;code&gt;unionByName&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 52378c1b-c9ee-4607-ba10-37f8ffe88ff6--&gt;
&lt;!-- begin code 78c4bfa3-a7a7-4477-8793-95f984fbb5c4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-78c4bfa3-a7a7-4477-8793-95f984fbb5c4&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;ds1.select(fieldnames).unionByName(ds2.select(fieldnames))&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 78c4bfa3-a7a7-4477-8793-95f984fbb5c4--&gt;
&lt;!-- begin paragraph b081902c-1870-414e-8fef-048b373ce173--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b081902c-1870-414e-8fef-048b373ce173&quot;&gt;Moyennant quelques détails d&amp;#39;implémentations :&lt;/p&gt;
&lt;!-- end paragraph b081902c-1870-414e-8fef-048b373ce173--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;exprEnc&lt;/code&gt; est privé, nous utilisons alors un &lt;i&gt;implicit&lt;/i&gt; pour le récupérer même s&amp;#39;il est déjà présent dans &lt;code&gt;ds1&lt;/code&gt;, &lt;/li&gt;&lt;li&gt;&lt;code&gt;select&lt;/code&gt; prend une liste de colonne.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph 10041d2b-0545-4e76-8812-12b8f73c30aa--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-10041d2b-0545-4e76-8812-12b8f73c30aa&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 10041d2b-0545-4e76-8812-12b8f73c30aa--&gt;
&lt;!-- begin code 8f289fd7-ded4-438a-b4db-20b35879bb55--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8f289fd7-ded4-438a-b4db-20b35879bb55&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def datasetUnion[A: Encoder](ds1: Dataset[A], ds2: Dataset[A]): Dataset[A] = {
  val exprEnc: Encoder[A] = implicitly[Encoder[A]]
  val name :: names = exprEnc.schema.fieldNames.toList
  ds1.select(name, names:_*).union(ds2.select(name, names:_*)).as[A]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8f289fd7-ded4-438a-b4db-20b35879bb55--&gt;
&lt;!-- begin paragraph 45cec21c-bd2a-4019-9d8d-e70042dbf3a1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-45cec21c-bd2a-4019-9d8d-e70042dbf3a1&quot;&gt;Cette fonction nous permet de faire directement &lt;code&gt;datasetUnion(ds1, ds2)&lt;/code&gt; sans autres manipulations.&lt;/p&gt;
&lt;!-- end paragraph 45cec21c-bd2a-4019-9d8d-e70042dbf3a1--&gt;
&lt;!-- begin heading_1 bfd405db-0ed6-4e81-8ef8-1640a7f62bac--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-bfd405db-0ed6-4e81-8ef8-1640a7f62bac&quot;&gt;La suite&lt;/h2&gt;
&lt;!-- end heading_1 bfd405db-0ed6-4e81-8ef8-1640a7f62bac--&gt;
&lt;!-- begin paragraph 50040ad1-74b5-46c8-94b8-a8b36fe765ea--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-50040ad1-74b5-46c8-94b8-a8b36fe765ea&quot;&gt;Malheureusement, cela ne marche pas complètement, dès que nous utilisons des structures complexes, nous avons des erreurs comme celle-ci : &lt;/p&gt;
&lt;!-- end paragraph 50040ad1-74b5-46c8-94b8-a8b36fe765ea--&gt;
&lt;!-- begin code 9614e143-147f-46c0-9213-f5863f276ad5--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9614e143-147f-46c0-9213-f5863f276ad5&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;Union can only be performed on tables with the compatible column types. array&amp;lt;struct&amp;lt;b:string,c:bigint,d:bigint&amp;gt;&amp;gt; &amp;lt;&amp;gt; array&amp;lt;struct&amp;lt;b:string,c:bigint&amp;gt;&amp;gt; at the first column of the second table;;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9614e143-147f-46c0-9213-f5863f276ad5--&gt;
&lt;!-- begin paragraph 3706d881-243e-464d-8935-af317a6e86d7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3706d881-243e-464d-8935-af317a6e86d7&quot;&gt;Par exemple, si nous utilisons la &lt;i&gt;case class&lt;/i&gt; &lt;code&gt;E&lt;/code&gt; ci-dessous :&lt;/p&gt;
&lt;!-- end paragraph 3706d881-243e-464d-8935-af317a6e86d7--&gt;
&lt;!-- begin code a004ebac-37e6-4f25-bceb-7de75c36707b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a004ebac-37e6-4f25-bceb-7de75c36707b&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;case class E(as: Seq[A], f: String)

val e1              = E(Seq(A(&amp;quot;1&amp;quot;, 2)), &amp;quot;3&amp;quot;)
val ds1: Dataset[E] = smallDs[E](e1)
val ds2: Dataset[E] = dfFromJson(&amp;quot;{as:[{b:&amp;#39;5&amp;#39;,c:6, d:7}, {b:&amp;#39;8&amp;#39;,c:9, d:10}],f:&amp;#39;11&amp;#39;}&amp;quot;).as[E]

val res: Dataset[E] = datasetUnion(ds1, ds2)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a004ebac-37e6-4f25-bceb-7de75c36707b--&gt;
&lt;!-- begin paragraph 876af038-66bc-4579-aaed-654e3eeb8baf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-876af038-66bc-4579-aaed-654e3eeb8baf&quot;&gt;Pour aller plus loin, nous pouvons utiliser ce que nous avons fait dans &lt;a href=&quot;https://github.com/UNIVALENCE/schema-utils&quot;&gt;schema-utils&lt;/a&gt;, qui permet d&amp;#39;aller beaucoup plus loin et d&amp;#39;aligner une &lt;i&gt;dataframe&lt;/i&gt; / &lt;i&gt;dataset&lt;/i&gt; sur un schéma existant :&lt;/p&gt;
&lt;!-- end paragraph 876af038-66bc-4579-aaed-654e3eeb8baf--&gt;
&lt;!-- begin code 13752fc7-a8cc-40d5-990a-171532afe5fa--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-13752fc7-a8cc-40d5-990a-171532afe5fa&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def deepUnion[A:Encoder](ds1:Dataset[A], ds2:Dataset[A]):Dataset[A] = {
  import io.univalence.schemautils.AlignDataframe 
  val schema = implicitly[Encoder[A]].schema 
  AlignDataframe(ds1,schema).union(AlignDataframe(ds2,schema)).as[A]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 13752fc7-a8cc-40d5-990a-171532afe5fa--&gt;
&lt;!-- begin paragraph 21b3e6d2-da2b-43cb-975c-c170866839e5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-21b3e6d2-da2b-43cb-975c-c170866839e5&quot;&gt;ou&lt;/p&gt;
&lt;!-- end paragraph 21b3e6d2-da2b-43cb-975c-c170866839e5--&gt;
&lt;!-- begin code 0e9e3b2d-22a6-45bc-9b34-cc213baaa64d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-0e9e3b2d-22a6-45bc-9b34-cc213baaa64d&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def deepUnion(ds1:Dataset[A], ds2:Dataset[A]):Dataset[A] = {
  import io.univalence.schemautils.AlignDataset 
  AlignDataset(ds1).union(AlignDataset(ds2))
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 0e9e3b2d-22a6-45bc-9b34-cc213baaa64d--&gt;
&lt;!-- begin paragraph aa3322f2-5181-442a-a8f2-89c3be60905f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-aa3322f2-5181-442a-a8f2-89c3be60905f&quot;&gt;&lt;i&gt;(Schema-utils utilise Spark 2.4. Néanmoins, cette manipulation est possible en Spark 1.6 ou en Spark 2.x, la version 2.4 est par contre beaucoup plus efficace.)&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph aa3322f2-5181-442a-a8f2-89c3be60905f--&gt;
&lt;!-- begin heading_1 4e092501-f1f4-4e36-8c2e-9116b462dab9--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-4e092501-f1f4-4e36-8c2e-9116b462dab9&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 4e092501-f1f4-4e36-8c2e-9116b462dab9--&gt;
&lt;!-- begin paragraph 37733e8b-2869-462d-8efd-945351651d63--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-37733e8b-2869-462d-8efd-945351651d63&quot;&gt;Ce genre de problème n&amp;#39;est pas forcément complexe à résoudre, néanmoins cela rend l&amp;#39;utilisation de Spark moins intuitive.&lt;/p&gt;
&lt;!-- end paragraph 37733e8b-2869-462d-8efd-945351651d63--&gt;
&lt;!-- begin paragraph 92d3f3c5-0a1c-40ff-84fc-c78906e9a3ed--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-92d3f3c5-0a1c-40ff-84fc-c78906e9a3ed&quot;&gt;Au &lt;a href=&quot;https://www.meetup.com/fr-FR/Paris-Scala-User-Group-PSUG/events/254988502/&quot;&gt;Paris Scala User Group de septembre&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/raphaelluta?lang=en&quot;&gt;Raphaël Luta&lt;/a&gt; et &lt;a href=&quot;https://twitter.com/choucrifahed&quot;&gt;Choucri Fahed&lt;/a&gt; nous avaient fait un retour sur certains des problèmes qu&amp;#39;ils ont rencontrés en utilisant Spark au jour le jour, dont celui que nous venons de voir (union sur les RDD vs Dataset).&lt;/p&gt;
&lt;!-- end paragraph 92d3f3c5-0a1c-40ff-84fc-c78906e9a3ed--&gt;
&lt;!-- begin paragraph 67eef509-15c7-403b-8c24-66975c32878d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-67eef509-15c7-403b-8c24-66975c32878d&quot;&gt;En attendant que la vidéo soit disponible sur la &lt;a href=&quot;https://www.youtube.com/channel/UCzRgovSmXzR2exnlWrawesA/featured&quot;&gt;chaîne Youtube du &lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/channel/UCzRgovSmXzR2exnlWrawesA/featured&quot;&gt;&lt;i&gt;user group&lt;/i&gt;&lt;/a&gt;, les slides de cette session sont disponibles :&lt;/p&gt;
&lt;!-- end paragraph 67eef509-15c7-403b-8c24-66975c32878d--&gt;
&lt;!-- begin bookmark 5360b79c-fcad-4192-aeb4-18c4b8f99675--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-5360b79c-fcad-4192-aeb4-18c4b8f99675&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://drive.google.com/file/d/1dAkhMzk5UGzS0AmcyTxI5Fc_dR4PvqvV/view&quot;&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Spark Dataset.pdf&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;/blog/assets/favicon_b078cb962140bcbfde199198b403bb61.png&quot;&gt; https://drive.google.com/file/d/1dAkhMzk5UGzS0AmcyTxI5Fc_dR4PvqvV/view&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 5360b79c-fcad-4192-aeb4-18c4b8f99675--&gt;
&lt;!-- begin divider 7aeccdbe-f25b-4e00-9bdf-4feb359a6cd9--&gt;
&lt;hr class=&quot;block-divider&quot; id=&quot;d-7aeccdbe-f25b-4e00-9bdf-4feb359a6cd9&quot;&gt;
&lt;!-- end divider 7aeccdbe-f25b-4e00-9bdf-4feb359a6cd9--&gt;
&lt;!-- begin bookmark 6678bc16-7da6-48e2-a2e9-92d92e074476--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-6678bc16-7da6-48e2-a2e9-92d92e074476&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://www.meetup.com/paris-scala-user-group-psug/events/254988502/&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://secure.meetupstatic.com/photos/event/b/2/8/f/600_435105711.jpeg&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;PSUG #91 - Spark Dataset: je t&#039;aime... moi non plus &amp; Poupées Russes, Thu, Sep 27, 2018, 7:00 PM | Meetup&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Bonjour à tou•te•s

Pour ce meetup de septembre, Ebiznext nous accueille pour parler de spark avec Raphaël Luta &amp; Choucri Fahed et de recursion schemes avec Valentin Kasas.&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://www.meetup.com/paris-scala-user-group-psug/events/254988502/&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 6678bc16-7da6-48e2-a2e9-92d92e074476--&gt;
&lt;!-- begin bookmark 7990b48f-8fac-401e-86c8-ff071e8dfd61--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-7990b48f-8fac-401e-86c8-ff071e8dfd61&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://www.youtube.com/channel/UCzRgovSmXzR2exnlWrawesA/featured?cbrd=1&amp;ucbcb=1&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://yt3.googleusercontent.com/ytc/AIdro_lUEJdyhC1xwIeaGZKrMrZNPowGollZWTww1Y8K4K5EBw=s900-c-k-c0x00ffffff-no-rj&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;Paris Scala User Group&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Partagez vos vidéos avec vos amis, vos proches et le monde entier&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://www.youtube.com/channel/UCzRgovSmXzR2exnlWrawesA/featured?cbrd=1&amp;ucbcb=1&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark 7990b48f-8fac-401e-86c8-ff071e8dfd61--&gt;
&lt;!-- begin paragraph e299a9d9-8d09-4de8-ab15-e43300a6d515--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e299a9d9-8d09-4de8-ab15-e43300a6d515&quot;&gt;Raphaël Luta&lt;/p&gt;
&lt;!-- end paragraph e299a9d9-8d09-4de8-ab15-e43300a6d515--&gt;
&lt;!-- begin paragraph 934b023c-e4fd-458d-9fa6-252100b20637--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-934b023c-e4fd-458d-9fa6-252100b20637&quot;&gt;&lt;a href=&quot;https://twitter.com/raphaelluta?lang=en&quot;&gt;https://twitter.com/raphaelluta&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 934b023c-e4fd-458d-9fa6-252100b20637--&gt;
&lt;!-- begin paragraph 1ce5b4db-1ade-49be-8cc8-5aeb69804481--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-1ce5b4db-1ade-49be-8cc8-5aeb69804481&quot;&gt;Choucri FAHED&lt;/p&gt;
&lt;!-- end paragraph 1ce5b4db-1ade-49be-8cc8-5aeb69804481--&gt;
&lt;!-- begin paragraph 8d96a5c8-adca-4f39-964c-336c28025af7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8d96a5c8-adca-4f39-964c-336c28025af7&quot;&gt;&lt;a href=&quot;https://twitter.com/choucrifahed?lang=en&quot;&gt;https://twitter.com/choucrifahed&lt;/a&gt;&lt;/p&gt;
&lt;!-- end paragraph 8d96a5c8-adca-4f39-964c-336c28025af7--&gt;
&lt;!-- begin paragraph de1d0182-1546-40c1-a623-ac5517da3036--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-de1d0182-1546-40c1-a623-ac5517da3036&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph de1d0182-1546-40c1-a623-ac5517da3036--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:11a6c4bda9a8450c962622e0d86fe5e9</id>
    <title>Deuxième article : Array ⇒ Denormalized #1</title>
    <updated>2018-11-13T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/deuxieme-article-array-denormalized-1.html"/>
    <!--summary La dernière fois, nous avions volontairement omis les arrays lors d&#039;une conversion entre nested et unested structures alors il est grand temps de s&#039;en charger !-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="Spark"></category>    <category term="JSON"></category>    <category term="CSV"></category>    <category term="Scala"></category>    <category term="SparkSQL"></category>    <content type="html">
&lt;!-- begin paragraph 94f18173-698c-4404-b102-1bbdf4cb01cc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-94f18173-698c-4404-b102-1bbdf4cb01cc&quot;&gt;Dans le précédent &lt;a href=&quot;https://blog.univalence.io/json-csv-en-spark-premiere-approche/&quot;&gt;article&lt;/a&gt;, nous avons vu une première approche pour convertir un JSON en CSV avec Spark. Néanmoins, nous nous étions arrêtés aux structures imbriquées, mais en évitant le cas des &lt;i&gt;array&lt;/i&gt;. Cette fois, nous allons nous attaquer à ce type de donnée.&lt;/p&gt;
&lt;!-- end paragraph 94f18173-698c-4404-b102-1bbdf4cb01cc--&gt;
&lt;!-- begin paragraph c02690ed-fd47-431f-a38b-a9f7a388c570--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c02690ed-fd47-431f-a38b-a9f7a388c570&quot;&gt;Dans cet article, nous verrons progressivement une approche pour retirer les &lt;i&gt;array&lt;/i&gt; des structures de données. Puis nous découvrirons un algorithme plus générique, fonctionnant dans les cas où sont présents plusieurs niveaux d&amp;#39;imbrication entre &lt;i&gt;struct&lt;/i&gt; et &lt;i&gt;array&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph c02690ed-fd47-431f-a38b-a9f7a388c570--&gt;
&lt;!-- begin heading_1 5cceee25-b80d-4755-b96d-f52154cf0944--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5cceee25-b80d-4755-b96d-f52154cf0944&quot;&gt;Partons d&amp;#39;un cas simple&lt;/h2&gt;
&lt;!-- end heading_1 5cceee25-b80d-4755-b96d-f52154cf0944--&gt;
&lt;!-- begin paragraph c282d463-b893-49f5-96e3-762e803efcf5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c282d463-b893-49f5-96e3-762e803efcf5&quot;&gt;Des structures de données contenant des &lt;i&gt;array&lt;/i&gt; sont assez courantes. Par exemple :&lt;/p&gt;
&lt;!-- end paragraph c282d463-b893-49f5-96e3-762e803efcf5--&gt;
&lt;!-- begin code 8880022e-bb80-4a1f-b8e7-5ece7cbd2178--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8880022e-bb80-4a1f-b8e7-5ece7cbd2178&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;firstname&amp;quot;: &amp;quot;John&amp;quot;,
  &amp;quot;lastname&amp;quot;: &amp;quot;Doe&amp;quot;,
  &amp;quot;contact&amp;quot;: [
    { &amp;quot;type&amp;quot;: &amp;quot;email&amp;quot;, &amp;quot;value&amp;quot;: &amp;quot;jdoe@mycompany.com&amp;quot; },
    { &amp;quot;type&amp;quot;: &amp;quot;twitter&amp;quot;, &amp;quot;value&amp;quot;: &amp;quot;jdoe87&amp;quot; }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8880022e-bb80-4a1f-b8e7-5ece7cbd2178--&gt;
&lt;!-- begin paragraph 0e939eb4-81cd-4319-954d-9f48efb4d3e6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0e939eb4-81cd-4319-954d-9f48efb4d3e6&quot;&gt;Vu de Spark, le document ci-dessus possède la schéma suivant (en se basant sur une description simplifiée en &lt;a href=&quot;https://github.com/edn-format/edn&quot;&gt;EDN&lt;/a&gt;) :&lt;/p&gt;
&lt;!-- end paragraph 0e939eb4-81cd-4319-954d-9f48efb4d3e6--&gt;
&lt;!-- begin code 9402cbf4-25a4-4f9a-a445-da24ddf52e7f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9402cbf4-25a4-4f9a-a445-da24ddf52e7f&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:firstname string
 :lastname  string
 :contact   [{:type  string
              :value string}]}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9402cbf4-25a4-4f9a-a445-da24ddf52e7f--&gt;
&lt;!-- begin paragraph 42f8c80e-1215-47a8-bdc9-740831d10bcb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-42f8c80e-1215-47a8-bdc9-740831d10bcb&quot;&gt;Notre objectif consistera à retirer la partie &lt;i&gt;array&lt;/i&gt; de &lt;code&gt;contact&lt;/code&gt; pour remonter le &lt;i&gt;struct&lt;/i&gt; sous-jacent. Pour cela nous allons utiliser la fonction &lt;a href=&quot;https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.functions$@explode(e:org.apache.spark.sql.Column):org.apache.spark.sql.Column&quot;&gt;&lt;code&gt;explode&lt;/code&gt;&lt;/a&gt;&lt;a href=&quot;https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.functions$@explode(e:org.apache.spark.sql.Column):org.apache.spark.sql.Column&quot;&gt; de Spark&lt;/a&gt;. Cette fonction permet de partir d&amp;#39;une ligne contenant un &lt;i&gt;array&lt;/i&gt; et de la découper en autant de ligne qu&amp;#39;il y a d&amp;#39;élément dans le tableau.&lt;/p&gt;
&lt;!-- end paragraph 42f8c80e-1215-47a8-bdc9-740831d10bcb--&gt;
&lt;!-- begin paragraph f1c640ac-7c07-466c-a43c-8fc022bcac43--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f1c640ac-7c07-466c-a43c-8fc022bcac43&quot;&gt;En appliquant &lt;code&gt;explode&lt;/code&gt; au champ &lt;code&gt;contact&lt;/code&gt;, nous obtenons le schéma suivant :&lt;/p&gt;
&lt;!-- end paragraph f1c640ac-7c07-466c-a43c-8fc022bcac43--&gt;
&lt;!-- begin code 833a3d45-1dc6-4aaa-9901-7adae5ac1072--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-833a3d45-1dc6-4aaa-9901-7adae5ac1072&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:firstname string
 :lastname  string
 :contact   {:type  string
             :value string}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 833a3d45-1dc6-4aaa-9901-7adae5ac1072--&gt;
&lt;!-- begin paragraph 9b716202-ab04-41a4-9680-0366482d3b9e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9b716202-ab04-41a4-9680-0366482d3b9e&quot;&gt;Et en reconstruisant la structure à la façon de notre précédent article, nous avons le CSV suivant :&lt;/p&gt;
&lt;!-- end paragraph 9b716202-ab04-41a4-9680-0366482d3b9e--&gt;
&lt;!-- begin code 7037e260-2b34-4cac-aebe-3cdbc4675acc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7037e260-2b34-4cac-aebe-3cdbc4675acc&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;firstname, lastname, contact_type, contact_value
John,      Doe,      email,        jdoe@mycompany.com
John,      Doe,      twitter       jdoe87&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7037e260-2b34-4cac-aebe-3cdbc4675acc--&gt;
&lt;!-- begin heading_1 212ab2bd-188a-40c1-84ab-274346083120--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-212ab2bd-188a-40c1-84ab-274346083120&quot;&gt;Imbrication &lt;i&gt;array&lt;/i&gt; et structure&lt;/h2&gt;
&lt;!-- end heading_1 212ab2bd-188a-40c1-84ab-274346083120--&gt;
&lt;!-- begin paragraph 2803631f-7291-4ae8-bb03-983273c49798--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2803631f-7291-4ae8-bb03-983273c49798&quot;&gt;Partons maintenant du document suivant et cherchons à le désimbriquer :&lt;/p&gt;
&lt;!-- end paragraph 2803631f-7291-4ae8-bb03-983273c49798--&gt;
&lt;!-- begin code d529845e-d794-446a-9829-8f8c8c4a587a--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d529845e-d794-446a-9829-8f8c8c4a587a&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;firstname&amp;quot;: &amp;quot;John&amp;quot;,
  &amp;quot;lastname&amp;quot;: &amp;quot;Doe&amp;quot;,
  &amp;quot;contact&amp;quot;: [
    { &amp;quot;type&amp;quot;: &amp;quot;email&amp;quot;, &amp;quot;value&amp;quot;: [{ &amp;quot;id&amp;quot;: &amp;quot;jdoe@mycompany.com&amp;quot; }] },
    { &amp;quot;type&amp;quot;: &amp;quot;internal&amp;quot;, &amp;quot;value&amp;quot;: [
        { &amp;quot;id&amp;quot;: &amp;quot;jdoe-dev&amp;quot; },
        { &amp;quot;dept&amp;quot;: &amp;quot;ops&amp;quot;, &amp;quot;id&amp;quot;: &amp;quot;jdoe-ops&amp;quot; }
      ] }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d529845e-d794-446a-9829-8f8c8c4a587a--&gt;
&lt;!-- begin paragraph b7cbf71a-20a1-4975-a83c-99875106de14--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b7cbf71a-20a1-4975-a83c-99875106de14&quot;&gt;Le document ci-dessus possède du point de vue de Spark le schéma suivant :&lt;/p&gt;
&lt;!-- end paragraph b7cbf71a-20a1-4975-a83c-99875106de14--&gt;
&lt;!-- begin code 3400ff7f-1af7-4aa9-9d46-edd3758e1d89--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3400ff7f-1af7-4aa9-9d46-edd3758e1d89&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:firstname string
 :lastname  string
 :contact   [{:type  string
              :value [{:dept string
                       :id   string}]}]}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3400ff7f-1af7-4aa9-9d46-edd3758e1d89--&gt;
&lt;!-- begin paragraph ba3970aa-a95c-4fcc-9c9e-2f5a8bbd348d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ba3970aa-a95c-4fcc-9c9e-2f5a8bbd348d&quot;&gt;Il y a ici deux difficultés : 1/ nous sommes en présence d&amp;#39;un schéma imbriquant structure et tableau à plusieurs niveaux, 2/ le champ &lt;code&gt;value&lt;/code&gt; n&amp;#39;a pas exactement la même structure d&amp;#39;une ligne à l&amp;#39;autre dans &lt;code&gt;contact&lt;/code&gt;. Pour le second point, le champ &lt;code&gt;dept&lt;/code&gt; n&amp;#39;est en effet présent que dans la dernière ligne.&lt;/p&gt;
&lt;!-- end paragraph ba3970aa-a95c-4fcc-9c9e-2f5a8bbd348d--&gt;
&lt;!-- begin paragraph 09225877-f9d8-43e2-b3ce-23465cd20a25--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-09225877-f9d8-43e2-b3ce-23465cd20a25&quot;&gt;Pour le premier point, il s&amp;#39;agira de détecter récursivement la présence de &lt;i&gt;array&lt;/i&gt; et d&amp;#39;y appliquer le même principe vu précédemment (retirer le &lt;i&gt;array&lt;/i&gt; et remonter le &lt;i&gt;struct&lt;/i&gt; sous-jacent) en se basant sur &lt;code&gt;explode&lt;/code&gt;. Pour le second point, Spark aligne automatiquement le schéma pour l&amp;#39;ensemble du document en recherchant un dénominateur commun. C&amp;#39;est ce que nous voyons dans le schéma précédent, où tous les champs &lt;code&gt;value&lt;/code&gt; possède le champ &lt;code&gt;dept&lt;/code&gt; (par défaut ce champ vaut &lt;code&gt;null&lt;/code&gt;).&lt;/p&gt;
&lt;!-- end paragraph 09225877-f9d8-43e2-b3ce-23465cd20a25--&gt;
&lt;!-- begin paragraph bcd85e1b-83f5-4876-a74d-fa9d433d9ceb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bcd85e1b-83f5-4876-a74d-fa9d433d9ceb&quot;&gt;Voici le schéma de ce document vu par Spark :&lt;/p&gt;
&lt;!-- end paragraph bcd85e1b-83f5-4876-a74d-fa9d433d9ceb--&gt;
&lt;!-- begin code 170f5c12-51b9-4e80-a15d-db5a0901a7a4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-170f5c12-51b9-4e80-a15d-db5a0901a7a4&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:firstname string
 :lastname  string
 :contact   {:type  string
             :value {:dept string
                     :id   string}}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 170f5c12-51b9-4e80-a15d-db5a0901a7a4--&gt;
&lt;!-- begin paragraph 593e7c0b-4168-4d42-95c3-91068adcf84f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-593e7c0b-4168-4d42-95c3-91068adcf84f&quot;&gt;Du coup, un &lt;code&gt;df.show(truncate = false)&lt;/code&gt; donne :&lt;/p&gt;
&lt;!-- end paragraph 593e7c0b-4168-4d42-95c3-91068adcf84f--&gt;
&lt;!-- begin code 9a4e8c95-c6b4-41b9-b749-e814c409eb7d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9a4e8c95-c6b4-41b9-b749-e814c409eb7d&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;+---------+--------+-------------------------------+
|firstname|lastname|contact                        |
+---------+--------+-------------------------------+
|John     |Doe     |[email, [, jdoe@mycompany.com]]|
|John     |Doe     |[internal, [, jdoe-dev]]       |
|John     |Doe     |[internal, [ops, jdoe-ops]]    |
+---------+--------+-------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9a4e8c95-c6b4-41b9-b749-e814c409eb7d--&gt;
&lt;!-- begin paragraph 549bb561-984f-407e-bb03-bdc6b0dd5c60--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-549bb561-984f-407e-bb03-bdc6b0dd5c60&quot;&gt;Et en convertissant en JSON :&lt;/p&gt;
&lt;!-- end paragraph 549bb561-984f-407e-bb03-bdc6b0dd5c60--&gt;
&lt;!-- begin code d0832b35-3ec2-4cf2-8483-95135bd40075--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d0832b35-3ec2-4cf2-8483-95135bd40075&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{&amp;quot;firstname&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastname&amp;quot;:&amp;quot;Doe&amp;quot;,&amp;quot;contact&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;email&amp;quot;,&amp;quot;value&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;jdoe@mycompany.com&amp;quot;}}}
{&amp;quot;firstname&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastname&amp;quot;:&amp;quot;Doe&amp;quot;,&amp;quot;contact&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;internal&amp;quot;,&amp;quot;value&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;jdoe-dev&amp;quot;}}}
{&amp;quot;firstname&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastname&amp;quot;:&amp;quot;Doe&amp;quot;,&amp;quot;contact&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;internal&amp;quot;,&amp;quot;value&amp;quot;:{&amp;quot;dept&amp;quot;:&amp;quot;ops&amp;quot;,&amp;quot;id&amp;quot;:&amp;quot;jdoe-ops&amp;quot;}}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d0832b35-3ec2-4cf2-8483-95135bd40075--&gt;
&lt;!-- begin paragraph 5ad7b05a-23b2-490d-b9f4-9a9b5e1b430e--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5ad7b05a-23b2-490d-b9f4-9a9b5e1b430e&quot;&gt;C&amp;#39;est en partant de cette idée que nous allons généraliser notre approche afin de faire disparaître les &lt;i&gt;array&lt;/i&gt; dans un schéma.&lt;/p&gt;
&lt;!-- end paragraph 5ad7b05a-23b2-490d-b9f4-9a9b5e1b430e--&gt;
&lt;!-- begin heading_1 72916c9f-02e3-41b2-94bf-2a4f450bbea6--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-72916c9f-02e3-41b2-94bf-2a4f450bbea6&quot;&gt;Généralisation et algorithme&lt;/h2&gt;
&lt;!-- end heading_1 72916c9f-02e3-41b2-94bf-2a4f450bbea6--&gt;
&lt;!-- begin paragraph daa54db5-770a-4ede-9176-def050a25a4d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-daa54db5-770a-4ede-9176-def050a25a4d&quot;&gt;Pour présenter notre algorithme plus détailler, rentrons cette fois dans un cadre plus complexe avec plusieurs &lt;i&gt;array&lt;/i&gt; définit dans le schéma au même niveau. Partons pour cela du schéma suivant :&lt;/p&gt;
&lt;!-- end paragraph daa54db5-770a-4ede-9176-def050a25a4d--&gt;
&lt;!-- begin code 9bc3244b-e599-44c7-898c-c53f89d4d2d4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9bc3244b-e599-44c7-898c-c53f89d4d2d4&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:a {:b {:c string
         :d [string]}}
 :e [{:f string
      :g [{:h string}]}]}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9bc3244b-e599-44c7-898c-c53f89d4d2d4--&gt;
&lt;!-- begin paragraph 32aa2137-e1cb-42a0-bade-0bc3239cdcf4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-32aa2137-e1cb-42a0-bade-0bc3239cdcf4&quot;&gt;En première, nous allons renommer les champs avec des &amp;quot;symboles&amp;quot; différents et constituer une table de symboles. Cette permet d&amp;#39;éviter des cas de collision dans le cas où un même nom de champ se retrouverait dans deux sous-structures différentes. Utiliser des symboles différents permet de décider au dernier moment de la stratégie de gestion des collisions lors de la reconstitution des noms de champ.&lt;/p&gt;
&lt;!-- end paragraph 32aa2137-e1cb-42a0-bade-0bc3239cdcf4--&gt;
&lt;!-- begin code bc17230d-4882-42e0-b9e1-7860c3494c3d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-bc17230d-4882-42e0-b9e1-7860c3494c3d&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:a :field1
 :b :field2
 :c :field3
 :d :array4
 :e :array5
 :f :field6
 :g :array7
 :h :field8}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code bc17230d-4882-42e0-b9e1-7860c3494c3d--&gt;
&lt;!-- begin paragraph c19bbea0-1675-4aee-a8ee-ddc9c4f0204a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c19bbea0-1675-4aee-a8ee-ddc9c4f0204a&quot;&gt;En se basant sur l&amp;#39;algorithme vu dans l&amp;#39;article précédent, nous aplatir une première fois les structures. Les &lt;i&gt;arrays&lt;/i&gt; ne sont pas modifiés.&lt;/p&gt;
&lt;!-- end paragraph c19bbea0-1675-4aee-a8ee-ddc9c4f0204a--&gt;
&lt;!-- begin code e6e8df4d-49e3-435f-8560-2e353fccb4d4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e6e8df4d-49e3-435f-8560-2e353fccb4d4&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;select a.b.c as field1,
       a.b.d as array4,
       e     as array5
from $input&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e6e8df4d-49e3-435f-8560-2e353fccb4d4--&gt;
&lt;!-- begin code eb25311a-934b-41fa-a228-c2e8e5d5ed2b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-eb25311a-934b-41fa-a228-c2e8e5d5ed2b&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:field3 string
 :array4 [string]
 :array5 [{:f string
           :g [{:h string}]}]}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code eb25311a-934b-41fa-a228-c2e8e5d5ed2b--&gt;
&lt;!-- begin paragraph 7345ca42-d637-472a-bcb4-5197a39cdd1b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-7345ca42-d637-472a-bcb4-5197a39cdd1b&quot;&gt;Nous utilisons ensuite &lt;code&gt;explode_out&lt;/code&gt; pour faire disparaître le premier &lt;i&gt;array&lt;/i&gt; de la structure. À la différence d&amp;#39;&lt;code&gt;explode&lt;/code&gt;, &lt;code&gt;explode_out&lt;/code&gt; permet de covserver les lignes où les &lt;i&gt;array&lt;/i&gt; sont vides. La fonction met alors &lt;code&gt;null&lt;/code&gt; comme valeur.&lt;/p&gt;
&lt;!-- end paragraph 7345ca42-d637-472a-bcb4-5197a39cdd1b--&gt;
&lt;!-- begin code 8d35e29d-ef76-4846-b53e-d92effd0f365--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8d35e29d-ef76-4846-b53e-d92effd0f365&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;select                           field3,
        explode_outer(array4) as array4,
                                 array5
from $input&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8d35e29d-ef76-4846-b53e-d92effd0f365--&gt;
&lt;!-- begin code 22865afe-60c9-48b8-b36f-3db1f27b503d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-22865afe-60c9-48b8-b36f-3db1f27b503d&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:field3 string
 :array4 string
 :array5 [{:f string
           :g [{:h string}]}]}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 22865afe-60c9-48b8-b36f-3db1f27b503d--&gt;
&lt;!-- begin paragraph 73cad52f-749c-4172-bdd2-5c145e6a5d2c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-73cad52f-749c-4172-bdd2-5c145e6a5d2c&quot;&gt;Nous recommençons cette dernière opération jusqu&amp;#39;à ce qu&amp;#39;il n&amp;#39;y ait plus de &lt;i&gt;array&lt;/i&gt; au premier niveau de la structure.&lt;/p&gt;
&lt;!-- end paragraph 73cad52f-749c-4172-bdd2-5c145e6a5d2c--&gt;
&lt;!-- begin code 948db584-f9ce-4902-b394-dc3e4fae05dc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-948db584-f9ce-4902-b394-dc3e4fae05dc&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;select                           field3,
                                 array4,
        explode_outer(array5) as array5
from $input&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 948db584-f9ce-4902-b394-dc3e4fae05dc--&gt;
&lt;!-- begin code 5a46d85e-5412-4fb4-a136-8be55c6770dc--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-5a46d85e-5412-4fb4-a136-8be55c6770dc&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:field3 string
 :array4 string
 :array5 {:f string
          :g [{:h string}]}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 5a46d85e-5412-4fb4-a136-8be55c6770dc--&gt;
&lt;!-- begin paragraph 06fcb4b2-24c0-410c-a9a8-2a433fa4b9cb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06fcb4b2-24c0-410c-a9a8-2a433fa4b9cb&quot;&gt;Puis, sachant qu&amp;#39;il y a encore une sous-structure, nous recommençons un aplatissement.&lt;/p&gt;
&lt;!-- end paragraph 06fcb4b2-24c0-410c-a9a8-2a433fa4b9cb--&gt;
&lt;!-- begin code 1f32ee40-39e6-4f35-9762-c7a65ac123ce--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-1f32ee40-39e6-4f35-9762-c7a65ac123ce&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;select              field3,
                    array4,
        array5.f as field6,
        array5.g as array7
from $input&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 1f32ee40-39e6-4f35-9762-c7a65ac123ce--&gt;
&lt;!-- begin code 17e948ee-b848-4add-a1de-75a8dffe1681--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-17e948ee-b848-4add-a1de-75a8dffe1681&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:field3 string
 :array4 string
 :field6 string
 :array7 [{:h string}]} &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 17e948ee-b848-4add-a1de-75a8dffe1681--&gt;
&lt;!-- begin paragraph cc29e3f7-ec12-48f8-8205-7f93dfa78012--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-cc29e3f7-ec12-48f8-8205-7f93dfa78012&quot;&gt;Et la suppression des &lt;i&gt;array&lt;/i&gt; du niveau supérieur.&lt;/p&gt;
&lt;!-- end paragraph cc29e3f7-ec12-48f8-8205-7f93dfa78012--&gt;
&lt;!-- begin code 7ed9d623-83cd-45a4-8228-4ee9a73fc387--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-7ed9d623-83cd-45a4-8228-4ee9a73fc387&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;select              field3,
                    array4,
                    field6,
        exlode_outer(array7) as array7
from $input&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 7ed9d623-83cd-45a4-8228-4ee9a73fc387--&gt;
&lt;!-- begin code 62305f38-6233-410e-bddf-62cb8f8a4ccb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-62305f38-6233-410e-bddf-62cb8f8a4ccb&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:field3 string
 :array4 string
 :field6 string
 :array7 {:h string}} &lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 62305f38-6233-410e-bddf-62cb8f8a4ccb--&gt;
&lt;!-- begin paragraph f04e3579-3e06-4bae-9ea3-04cff25571f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f04e3579-3e06-4bae-9ea3-04cff25571f2&quot;&gt;Et ainsi de suite...&lt;/p&gt;
&lt;!-- end paragraph f04e3579-3e06-4bae-9ea3-04cff25571f2--&gt;
&lt;!-- begin code 3a47601e-369d-44cc-8e50-0297c8b173f4--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-3a47601e-369d-44cc-8e50-0297c8b173f4&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;select              field3,
                    array4,
                    field6,
        array7.h as field8
from $input&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 3a47601e-369d-44cc-8e50-0297c8b173f4--&gt;
&lt;!-- begin code 01d1b1d6-9abd-44b0-bc2f-dee4371fed61--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-01d1b1d6-9abd-44b0-bc2f-dee4371fed61&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:field3 string,
 :array4 string,
 :field6 string,
 :field8 string}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 01d1b1d6-9abd-44b0-bc2f-dee4371fed61--&gt;
&lt;!-- begin paragraph fda39eab-ea3f-4d4e-976d-e005b727d21c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fda39eab-ea3f-4d4e-976d-e005b727d21c&quot;&gt;Dès lors qu&amp;#39;il n&amp;#39;y a ni sous-structure ni &lt;i&gt;array&lt;/i&gt;, nous arrêtons l&amp;#39;itération.&lt;/p&gt;
&lt;!-- end paragraph fda39eab-ea3f-4d4e-976d-e005b727d21c--&gt;
&lt;!-- begin paragraph 8082e79f-db9c-4c74-8b7d-5338d5cc01c0--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8082e79f-db9c-4c74-8b7d-5338d5cc01c0&quot;&gt;En utilisant la table de symboles, nous reconstruisons alors la structure.&lt;/p&gt;
&lt;!-- end paragraph 8082e79f-db9c-4c74-8b7d-5338d5cc01c0--&gt;
&lt;!-- begin code a3af7d94-4460-42ab-bca2-781dae2d8dee--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-a3af7d94-4460-42ab-bca2-781dae2d8dee&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;select struct(struct(field3 as c, array4 as d) as b) as a,
       struct(struct(field6 as f, struct(field8 as h) as g) as e
from $input&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code a3af7d94-4460-42ab-bca2-781dae2d8dee--&gt;
&lt;!-- begin code d5a351ec-c31d-4cbc-ba84-f49ef16bd58c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d5a351ec-c31d-4cbc-ba84-f49ef16bd58c&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:a {:b {:c string
         :d string}}
 :e {:f string
     :g {:h string}}}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d5a351ec-c31d-4cbc-ba84-f49ef16bd58c--&gt;
&lt;!-- begin heading_1 1f65fc04-e1c8-41b1-8003-574f1746de27--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-1f65fc04-e1c8-41b1-8003-574f1746de27&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 1f65fc04-e1c8-41b1-8003-574f1746de27--&gt;
&lt;!-- begin paragraph a4052b6a-85a1-4d65-84c3-27bea6b765fc--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-a4052b6a-85a1-4d65-84c3-27bea6b765fc&quot;&gt;Nous venons de voir de manière détaillée comment l&amp;#39;algorithme derrière notre implémentation &lt;a href=&quot;https://github.com/UNIVALENCE/schema-utils/blob/master/src/main/scala/io/univalence/schemautils/FlattenNested.scala&quot;&gt;FlattenedNested&lt;/a&gt; fonctionne.&lt;/p&gt;
&lt;!-- end paragraph a4052b6a-85a1-4d65-84c3-27bea6b765fc--&gt;
&lt;!-- begin paragraph c1e56467-5a46-4c14-be6d-70cc32761139--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c1e56467-5a46-4c14-be6d-70cc32761139&quot;&gt;Dans l&amp;#39;&lt;a href=&quot;https://blog.univalence.io/json-csv-en-spark-premiere-approche/&quot;&gt;article précédent&lt;/a&gt;, nous avons vu comment aplatir un &lt;i&gt;dataframe&lt;/i&gt; en forme de JSON (&lt;i&gt;Jsonoid&lt;/i&gt;) vers un &lt;i&gt;dataframe&lt;/i&gt; en forme de table, mais sans prendre en compte les tableaux. Nous pouvons combiner ces deux techniques pour transférer l&amp;#39;intégralité d&amp;#39;un &lt;i&gt;dataframe&lt;/i&gt; vers un système plus traditionnel (SGBD).&lt;/p&gt;
&lt;!-- end paragraph c1e56467-5a46-4c14-be6d-70cc32761139--&gt;
&lt;!-- begin paragraph 00cb590b-c8a9-49a6-95a5-3678fc4e32f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-00cb590b-c8a9-49a6-95a5-3678fc4e32f2&quot;&gt;Dans les articles suivants, nous allons voir des versions configurables de ces algorithmes, afin d&amp;#39;aligner les structures de donnée entre différents systèmes. &lt;/p&gt;
&lt;!-- end paragraph 00cb590b-c8a9-49a6-95a5-3678fc4e32f2--&gt;
&lt;!-- begin paragraph ef0f2acb-348c-498f-9dc5-623d29d8377c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ef0f2acb-348c-498f-9dc5-623d29d8377c&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph ef0f2acb-348c-498f-9dc5-623d29d8377c--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:53c71db6920844d48efe374b1e6c5eba</id>
    <title>Spark 2.4 : #TheWaitIsOver Tour d&#039;horizon de la nouvelle version</title>
    <updated>2018-11-07T23:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/spark-2-4-thewaitisover-tour-d-horizon-de-la-nouvelle-version.html"/>
    <!--summary Spark 2.4 est enfin sorti accompagné de son lot de nouveautés tels que le support Scala 2.12 et bien d&#039;autres améliorations détaillées dans cet article !-->    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>        <category term="Spark"></category>    <category term="Avro"></category>    <category term="Kafka"></category>    <category term="Kubernetes"></category>    <category term="Scala"></category>    <category term="SparkSQL"></category>    <content type="html">
&lt;!-- begin paragraph b7f57ae4-aaf0-4eda-9271-e9e8119a836d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b7f57ae4-aaf0-4eda-9271-e9e8119a836d&quot;&gt;La version 2.4 d&amp;#39;&lt;a href=&quot;https://github.com/apache/spark/releases/tag/v2.4.0&quot;&gt;Apache Spark&lt;/a&gt; est sortie fin octobre 2018. Cette version intègre de nouvelles fonctionnalités et son lot de correctifs. Ce qui inclut notamment la mise à jour des dépendances (Scala 2.12, Kafka 2.0), un support natif d&amp;#39;Avro, une amélioration du support de Kubernetes, de nouvelles opérations pour gérer les collections et autres types complexes en Spark SQL, une amélioration de l&amp;#39;API data source v2, ainsi que d&amp;#39;autres nouveautés.&lt;/p&gt;
&lt;!-- end paragraph b7f57ae4-aaf0-4eda-9271-e9e8119a836d--&gt;
&lt;!-- begin paragraph 07819bfa-e1d0-4950-a32d-81a38dc8c552--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-07819bfa-e1d0-4950-a32d-81a38dc8c552&quot;&gt;Nous allons dans cet article passer en revue les nouveautés de la version 2.4. Nous verrons notamment que ces nouveautés apportent non seulement de meilleures performances, mais qu&amp;#39;elles offrent plus de flexibilités en termes de traitement des données.&lt;/p&gt;
&lt;!-- end paragraph 07819bfa-e1d0-4950-a32d-81a38dc8c552--&gt;
&lt;!-- begin heading_1 68fb8940-0461-452d-b209-9fd5c84278d9--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-68fb8940-0461-452d-b209-9fd5c84278d9&quot;&gt;Support de Scala 2.12&lt;/h2&gt;
&lt;!-- end heading_1 68fb8940-0461-452d-b209-9fd5c84278d9--&gt;
&lt;!-- begin paragraph 8165a824-3c50-48c8-b9a3-d1ce245b896b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8165a824-3c50-48c8-b9a3-d1ce245b896b&quot;&gt;Spark 2.4 vient déjà avec le support très attendu de Scala 2.12. Cette version de Scala sortie en version stable il y a 2 ans a été mise à jour régulièrement : nous en sommes à la 2.12.7 depuis fin septembre (avec un bug fix spécifique pour Spark 2.4). Pour information, il est prévu que Scala 2.13 sorte prochainement en version stable.&lt;/p&gt;
&lt;!-- end paragraph 8165a824-3c50-48c8-b9a3-d1ce245b896b--&gt;
&lt;!-- begin paragraph e72a0baf-2684-4691-a8e5-3026acb6f17c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e72a0baf-2684-4691-a8e5-3026acb6f17c&quot;&gt;Scala 2.12 inclut une meilleure intégration avec les nouveautés apportées avec Java 8 (correspondance directe entre &lt;i&gt;trait&lt;/i&gt; côté Scala et &lt;i&gt;interface&lt;/i&gt; côté Java, utilisation des &lt;a href=&quot;http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html&quot;&gt;types SAM&lt;/a&gt;, utilisation d&amp;#39;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/technotes/guides/vm/multiple-language-support.html#invokedynamic&quot;&gt;&lt;i&gt;invokedynamic&lt;/i&gt;&lt;/a&gt; pour les lambda expressions), de nouvelles optimisations (&lt;i&gt;inlining&lt;/i&gt;, allocations des &lt;i&gt;closures&lt;/i&gt;, détection de code mort, suppression plus efficace des &lt;i&gt;(un)boxing&lt;/i&gt;...) et pour ceux qui ont fait du &lt;i&gt;typechecking&lt;/i&gt; leur &lt;del&gt;chemin de croix&lt;/del&gt; cheval de bataille, la correction du bug &lt;a href=&quot;https://issues.scala-lang.org/browse/SI-2712&quot;&gt;SI-2712&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph e72a0baf-2684-4691-a8e5-3026acb6f17c--&gt;
&lt;!-- begin paragraph fdd4aaba-ca4c-45b6-b786-90be3f8807a7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fdd4aaba-ca4c-45b6-b786-90be3f8807a7&quot;&gt;Vous trouverez plus de détails sur ces fonctionnalités sur le blog d&amp;#39;Ippon Technologies : &lt;a href=&quot;https://blog.ippon.fr/2016/11/21/scala-2-12-tour-dhorizon-des-nouveautes/&quot;&gt;partie 1&lt;/a&gt;, &lt;a href=&quot;https://blog.ippon.fr/2016/11/23/scala-2-12-unification-interface-et-trait/&quot;&gt;partie 2&lt;/a&gt; et &lt;a href=&quot;https://blog.ippon.fr/2016/11/30/scala-2-12-lambda-expression/&quot;&gt;partie 3&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph fdd4aaba-ca4c-45b6-b786-90be3f8807a7--&gt;
&lt;!-- begin heading_1 5cfc4953-63af-4857-88fd-2f98c1458ee2--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-5cfc4953-63af-4857-88fd-2f98c1458ee2&quot;&gt;Spark sur Kubernetes&lt;/h2&gt;
&lt;!-- end heading_1 5cfc4953-63af-4857-88fd-2f98c1458ee2--&gt;
&lt;!-- begin paragraph 26514d30-7a60-4a30-80fb-919bb13a0524--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-26514d30-7a60-4a30-80fb-919bb13a0524&quot;&gt;Depuis la version 2.3, les utilisateurs de Spark ont la possibilité d&amp;#39;utiliser Kubernetes comme orchestrateur, en alternative par rapport à Yarn, Mesos ou en mode &lt;i&gt;standalone&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 26514d30-7a60-4a30-80fb-919bb13a0524--&gt;
&lt;!-- begin paragraph f2e89ff4-8710-4511-9385-d9efe3b56680--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-f2e89ff4-8710-4511-9385-d9efe3b56680&quot;&gt;Le support de Kubernetes dans Spark permet aux développeurs de livrer des images Docker, à la place du traditionnel JAR déployé avec spark-submit. De plus, Kubernetes offre un grand nombre de facilités dans la gestion, l&amp;#39;orchestration et le monitoring d&amp;#39;un cluster, en le comparant avec Yarn. Il permet notamment la mise en place de quota sur les ressources du cluster, la gestion de &lt;i&gt;namespace&lt;/i&gt; (pour avoir du multi-tenant sur le même cluster, par exemple), etc.&lt;/p&gt;
&lt;!-- end paragraph f2e89ff4-8710-4511-9385-d9efe3b56680--&gt;
&lt;!-- begin paragraph 02bea95e-cdfd-4969-bbe5-1a0577fd3ebb--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-02bea95e-cdfd-4969-bbe5-1a0577fd3ebb&quot;&gt;La version 2.4 de Spark ajoute d&amp;#39;autres possibilités. Tout d&amp;#39;abord, un support de PySpark (pour Python 2.x et 3.x) et un support de SparkR. Des &lt;a href=&quot;https://github.com/apache/spark/tree/branch-2.4/resource-managers/kubernetes/docker/src/main/dockerfiles/spark/bindings&quot;&gt;templates de Dockerfile&lt;/a&gt; sont mis à disposition pour ces deux langages.&lt;/p&gt;
&lt;!-- end paragraph 02bea95e-cdfd-4969-bbe5-1a0577fd3ebb--&gt;
&lt;!-- begin paragraph 65111b93-df3b-49cf-b4ee-9cafdb6d0625--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-65111b93-df3b-49cf-b4ee-9cafdb6d0625&quot;&gt;Un mode client est aussi proposé. Il s&amp;#39;agit de faciliter l&amp;#39;exécution d&amp;#39;outils interactifs, comme spark-shell ou les notebooks, depuis un &lt;i&gt;pod&lt;/i&gt; géré par un cluster Kubernetes ou depuis une machine cliente hors du cluster Spark.&lt;/p&gt;
&lt;!-- end paragraph 65111b93-df3b-49cf-b4ee-9cafdb6d0625--&gt;
&lt;!-- begin heading_1 86745c08-0f3a-4dc0-bf14-293498693db8--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-86745c08-0f3a-4dc0-bf14-293498693db8&quot;&gt;Support intégré d&amp;#39;Avro&lt;/h2&gt;
&lt;!-- end heading_1 86745c08-0f3a-4dc0-bf14-293498693db8--&gt;
&lt;!-- begin paragraph c25ccae0-9480-46d6-86ad-5ae784fa5b1a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c25ccae0-9480-46d6-86ad-5ae784fa5b1a&quot;&gt;Avro est un format binaire de sérialisation de données très utilisé dans le monde Big Data, avec Hadoop et Spark, mais en particulier avec Kafka pour les données temps réel.&lt;/p&gt;
&lt;!-- end paragraph c25ccae0-9480-46d6-86ad-5ae784fa5b1a--&gt;
&lt;!-- begin paragraph be0240f1-de4e-488a-896c-1ac98dfa8103--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-be0240f1-de4e-488a-896c-1ac98dfa8103&quot;&gt;Jusqu&amp;#39;à présent l&amp;#39;intégration d&amp;#39;Avro se faisait en passant par un &lt;a href=&quot;https://github.com/databricks/spark-avro&quot;&gt;&lt;i&gt;addon&lt;/i&gt;&lt;/a&gt; mis à disposition par Databricks. Depuis la version 2.4 de Spark, Avro a été ajouté aux formats supportés par défaut (JSON, Parquet, JDBC, CSV, texte), en améliorant au passage les performances.&lt;/p&gt;
&lt;!-- end paragraph be0240f1-de4e-488a-896c-1ac98dfa8103--&gt;
&lt;!-- begin paragraph 06531ca9-50ec-41e2-bea1-7d10934c70ca--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-06531ca9-50ec-41e2-bea1-7d10934c70ca&quot;&gt;En plus de la lecture et l&amp;#39;écriture des données en Avro, deux fonctions sont mises à disposition pour Avro : &lt;code&gt;from_avro&lt;/code&gt; et &lt;code&gt;to_avro&lt;/code&gt;. Ces fonctions sont les équivalentes de &lt;code&gt;from_jon&lt;/code&gt; et &lt;code&gt;to_json&lt;/code&gt;. Elles permettent de respectivement lire (désencapsulation) et écrire des données Avro (encapsulation) dans les champs d&amp;#39;un &lt;i&gt;dataframe&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 06531ca9-50ec-41e2-bea1-7d10934c70ca--&gt;
&lt;!-- begin paragraph 761220b7-797f-4063-9016-9b93435b0a11--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-761220b7-797f-4063-9016-9b93435b0a11&quot;&gt;Spark 2.4 supporte une version plus moderne d&amp;#39;Avro, la version 1.8.2. Cette version majeure d&amp;#39;Avro qui est apparue 2 ans après la version 1.7 (début 2016) a ajouté le support des &lt;a href=&quot;https://avro.apache.org/docs/1.8.2/spec.html#Logical+Types&quot;&gt;types logiques&lt;/a&gt;(*), avec par défaut les décimales, les dates et les timestamps.&lt;/p&gt;
&lt;!-- end paragraph 761220b7-797f-4063-9016-9b93435b0a11--&gt;
&lt;!-- begin paragraph 74af6f84-8ce0-44b0-a346-638d2ab41214--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-74af6f84-8ce0-44b0-a346-638d2ab41214&quot;&gt;&lt;i&gt;(* Il s&amp;#39;agit d&amp;#39;une mécanique similaire à d&amp;#39;autres formats de sérialisation comme &lt;/i&gt;&lt;a href=&quot;https://github.com/edn-format/edn&quot;&gt;&lt;i&gt;EDN&lt;/i&gt;&lt;/a&gt;&lt;i&gt; avec les &lt;/i&gt;tagged elements&lt;i&gt;.)&lt;/i&gt;&lt;/p&gt;
&lt;!-- end paragraph 74af6f84-8ce0-44b0-a346-638d2ab41214--&gt;
&lt;!-- begin heading_1 d1d4388d-19cc-4d3e-ad70-964f36700363--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-d1d4388d-19cc-4d3e-ad70-964f36700363&quot;&gt;Support de Kafka 2.0.0&lt;/h2&gt;
&lt;!-- end heading_1 d1d4388d-19cc-4d3e-ad70-964f36700363--&gt;
&lt;!-- begin paragraph 77895587-1f8b-4a03-a3ff-34c5fa8e4eba--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77895587-1f8b-4a03-a3ff-34c5fa8e4eba&quot;&gt;La version du client Kafka est mis à jour et passe de la version 0.10.0.1 à la version 2.0.0.&lt;/p&gt;
&lt;!-- end paragraph 77895587-1f8b-4a03-a3ff-34c5fa8e4eba--&gt;
&lt;!-- begin paragraph 3ce26ce5-389d-422c-9889-a5b01af0de1f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ce26ce5-389d-422c-9889-a5b01af0de1f&quot;&gt;Le support des nouvelles versions de Kafka permet la gestion des transactions de Kafka dans Spark Streaming [&lt;a href=&quot;https://jira.apache.org/jira/browse/SPARK-25005&quot;&gt;SPARK-25005&lt;/a&gt;]. Ce support permet de choisir de lire :&lt;/p&gt;
&lt;!-- end paragraph 3ce26ce5-389d-422c-9889-a5b01af0de1f--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;soit les messages non transactionnels et les messages dans les transactions commitées, &lt;/li&gt;&lt;li&gt;soit de lire tous les messages disponibles, même ceux qui sont dans des transactions encore ouvertes ou interrompues. &lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin paragraph d593b8e0-a701-4bd0-800d-55a4c1e0b20a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d593b8e0-a701-4bd0-800d-55a4c1e0b20a&quot;&gt;Cette fonctionnalité est activée dans Spark par la propriété &lt;code&gt;kafka.isolation.level&lt;/code&gt;. Les valeurs possibles sont &lt;code&gt;read_committed&lt;/code&gt; pour prendre uniquement en compte les données commitées et &lt;code&gt;read_uncommited&lt;/code&gt;, qui est la valeur par défaut.&lt;/p&gt;
&lt;!-- end paragraph d593b8e0-a701-4bd0-800d-55a4c1e0b20a--&gt;
&lt;!-- begin paragraph 987092af-e778-42d7-8128-e13e4aa31ee5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-987092af-e778-42d7-8128-e13e4aa31ee5&quot;&gt;Voici un exemple de code permettant de lire un flux sur un &lt;i&gt;topic&lt;/i&gt; Kafka. Nous ne considérons que les offset commités. Pour chaque message récupéré, nous tentons de convertir la valeur en &lt;code&gt;Int&lt;/code&gt;.&lt;/p&gt;
&lt;!-- end paragraph 987092af-e778-42d7-8128-e13e4aa31ee5--&gt;
&lt;!-- begin paragraph c5182279-1022-4b2a-8455-8ae2f0a45155--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c5182279-1022-4b2a-8455-8ae2f0a45155&quot;&gt;Il reste néanmoins le support des headers Kafka à intégrer dans Spark. Cette fonctionnalité est en cours de développement &lt;a href=&quot;https://jira.apache.org/jira/browse/SPARK-23539&quot;&gt;[SPARK-23539]&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph c5182279-1022-4b2a-8455-8ae2f0a45155--&gt;
&lt;!-- begin code 41d91c28-8b16-491b-ba6e-8d823cd81155--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-41d91c28-8b16-491b-ba6e-8d823cd81155&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;spark
 .readStream
 .format(&amp;quot;kafka&amp;quot;)
 .option(&amp;quot;kafka.bootstrap.servers&amp;quot;, brokerAddress)
 .option(&amp;quot;kafka.isolation.level&amp;quot;, &amp;quot;read_committed&amp;quot;)
 // from the beginning of the topic
 .option(&amp;quot;startingOffsets&amp;quot;, &amp;quot;earliest&amp;quot;)
 .option(&amp;quot;subscribe&amp;quot;, topic)
 .load()
 .selectExpr(&amp;quot;CAST(key AS STRING)&amp;quot;, &amp;quot;CAST(value AS STRING)&amp;quot;)
 .as[(String, String)]
 .flatMap({case (k,v) =&amp;gt; Try(v.toInt).toOption})&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 41d91c28-8b16-491b-ba6e-8d823cd81155--&gt;
&lt;!-- begin heading_1 af41de6f-ebfe-4994-a611-7ce73cc7b582--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-af41de6f-ebfe-4994-a611-7ce73cc7b582&quot;&gt;Opération sur les collections&lt;/h2&gt;
&lt;!-- end heading_1 af41de6f-ebfe-4994-a611-7ce73cc7b582--&gt;
&lt;!-- begin paragraph 6ca1b7d9-1ee8-4082-8141-3efbb5cbcdc4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6ca1b7d9-1ee8-4082-8141-3efbb5cbcdc4&quot;&gt;Une bonne partie des données à traiter à travers Spark transparaissent sous forme de structures imbriquées, incluant des collections (&lt;i&gt;array&lt;/i&gt;) et autres types complexes (&lt;i&gt;struct&lt;/i&gt;). c&amp;#39;est le cas en particulier dès lors que nous devons traiter des documents JSON, XML, certaines données remontées par Cassandra...&lt;/p&gt;
&lt;!-- end paragraph 6ca1b7d9-1ee8-4082-8141-3efbb5cbcdc4--&gt;
&lt;!-- begin paragraph 61486dd3-1f50-49c5-aa68-6951ef2f2c95--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-61486dd3-1f50-49c5-aa68-6951ef2f2c95&quot;&gt;Jusque-là, Spark n&amp;#39;offrait pas de fonctions permettant de traiter le cas des collections. Il était alors nécessaire de passer par des UDF créés à la main pour transformer des colonnes dès lors qu&amp;#39;elles contiennent des listes ou des tables de propriétés, par exemple.&lt;/p&gt;
&lt;!-- end paragraph 61486dd3-1f50-49c5-aa68-6951ef2f2c95--&gt;
&lt;!-- begin paragraph fc0ad221-0b24-4b51-8841-a8b4208600d2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-fc0ad221-0b24-4b51-8841-a8b4208600d2&quot;&gt;Depuis Spark 2.4, cela n&amp;#39;est plus une nécessité. Cette version fournit tout un ensemble de fonctions au niveau SQL et DataFrame pour gérer les types collection et les types complexes. En plus de faciliter la gestion des collections, ces fonctions sont aussi très performantes.&lt;/p&gt;
&lt;!-- end paragraph fc0ad221-0b24-4b51-8841-a8b4208600d2--&gt;
&lt;!-- begin paragraph 8cecb06b-11df-4808-bdf3-fa10b5f8394a--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8cecb06b-11df-4808-bdf3-fa10b5f8394a&quot;&gt;Enfin, cette fonctionnalité devrait faciliter d&amp;#39;autant plus l&amp;#39;adoption de formats contenant des structures imbriquées (disponibles à travers JSON, Avro, Cassandra...) et réduire très fortement le besoin de passer par des structures en Scala, Java ou Python pour traiter les données dîtes complexes.&lt;/p&gt;
&lt;!-- end paragraph 8cecb06b-11df-4808-bdf3-fa10b5f8394a--&gt;
&lt;!-- begin heading_2 e8c1a624-b5d9-4362-ae10-7ace8d137a4c--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-e8c1a624-b5d9-4362-ae10-7ace8d137a4c&quot;&gt;Fonction d&amp;#39;ordre supérieur&lt;/h3&gt;
&lt;!-- end heading_2 e8c1a624-b5d9-4362-ae10-7ace8d137a4c--&gt;
&lt;!-- begin paragraph 997d96d9-6da2-4c43-bbb9-9888456d5ef5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-997d96d9-6da2-4c43-bbb9-9888456d5ef5&quot;&gt;Parmi ces nouvelles fonctions, nous avons la possibilité de pouvoir utiliser des fonctions dîtes d&amp;#39;ordre supérieur dans les requêtes SQL. Pour faire simple, une fonction d&amp;#39;ordre supérieur est une fonction qui prend en entrée une autre fonction. Ceci permet d&amp;#39;avoir des opérations génériques qui laissent au développeur le soin d&amp;#39;y intégrer un traitement spécifique par composition.&lt;/p&gt;
&lt;!-- end paragraph 997d96d9-6da2-4c43-bbb9-9888456d5ef5--&gt;
&lt;!-- begin paragraph 049eee4a-c30a-41e5-83b3-13688f8ca0b5--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-049eee4a-c30a-41e5-83b3-13688f8ca0b5&quot;&gt;Par exemple, supposons que nous cherchons à incrémenter les entiers du dataset &lt;code&gt;nested_data&lt;/code&gt; ci-dessous :&lt;/p&gt;
&lt;!-- end paragraph 049eee4a-c30a-41e5-83b3-13688f8ca0b5--&gt;
&lt;!-- begin code fbcfbb15-a943-4c9c-918f-049d319721ce--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-fbcfbb15-a943-4c9c-918f-049d319721ce&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{ key: &amp;quot;a&amp;quot;, values: [2,3,4] }
{ key: &amp;quot;b&amp;quot;, values: [5,6,7] }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code fbcfbb15-a943-4c9c-918f-049d319721ce--&gt;
&lt;!-- begin paragraph 95ccc1fe-a96d-4c3d-80f9-8e524dd0bcb4--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-95ccc1fe-a96d-4c3d-80f9-8e524dd0bcb4&quot;&gt;Avec Spark 2.4, nous pouvons écrire notre transformation directement dans la requête SQL en utilisant la fonction d&amp;#39;ordre supérieur &lt;code&gt;transform&lt;/code&gt; :&lt;/p&gt;
&lt;!-- end paragraph 95ccc1fe-a96d-4c3d-80f9-8e524dd0bcb4--&gt;
&lt;!-- begin code e5909d5b-6568-4dcd-b81e-d07359e8edfb--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-e5909d5b-6568-4dcd-b81e-d07359e8edfb&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT  key,
        values,
        TRANSFORM(values, value -&amp;gt; value + 1) AS values_plus_one
FROM    nested_data&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code e5909d5b-6568-4dcd-b81e-d07359e8edfb--&gt;
&lt;!-- begin paragraph ef30131f-3d47-4809-a10a-bf37f6e456f1--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ef30131f-3d47-4809-a10a-bf37f6e456f1&quot;&gt;&lt;code&gt;transform&lt;/code&gt; prend en entrée deux paramètres : &lt;code&gt;values&lt;/code&gt; qui correspond à la collection à transformer et &lt;code&gt;value → value + 1&lt;/code&gt; qui représente le traitement spécifique à appliquer à chaque élément de la collection.&lt;/p&gt;
&lt;!-- end paragraph ef30131f-3d47-4809-a10a-bf37f6e456f1--&gt;
&lt;!-- begin paragraph 71c4770e-7fe3-4145-864c-47f7b98c88f2--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-71c4770e-7fe3-4145-864c-47f7b98c88f2&quot;&gt;Avec l&amp;#39;exemple ci-dessous, nous obtenons donc :&lt;/p&gt;
&lt;!-- end paragraph 71c4770e-7fe3-4145-864c-47f7b98c88f2--&gt;
&lt;!-- begin code 4b62345c-2fa0-4527-a669-bfd51f2bd20b--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-4b62345c-2fa0-4527-a669-bfd51f2bd20b&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{ key: &amp;quot;a&amp;quot;, values: [2,3,4], values_plus_one: [3,4,5] }
{ key: &amp;quot;b&amp;quot;, values: [5,6,7], values_plus_one: [6,7,8] }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 4b62345c-2fa0-4527-a669-bfd51f2bd20b--&gt;
&lt;!-- begin paragraph bf435862-4ca7-409c-b3bb-4c2486a798e7--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-bf435862-4ca7-409c-b3bb-4c2486a798e7&quot;&gt;Pour faire la même chose auparavant, il fallait passer par une UDF :&lt;/p&gt;
&lt;!-- end paragraph bf435862-4ca7-409c-b3bb-4c2486a798e7--&gt;
&lt;!-- begin code ed1c338f-9f1a-4ba0-9085-e6005576bfd9--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-ed1c338f-9f1a-4ba0-9085-e6005576bfd9&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;val aPlusOne: Seq[Int] =&amp;gt; Seq[Int] =
  values =&amp;gt; values.map(value =&amp;gt; value + 1)

sparkSession.udf.register(&amp;quot;aPlusOne&amp;quot;,aPlusOne)&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code ed1c338f-9f1a-4ba0-9085-e6005576bfd9--&gt;
&lt;!-- begin paragraph 9474e42d-2001-4cc0-8e8f-6e2faf406a10--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-9474e42d-2001-4cc0-8e8f-6e2faf406a10&quot;&gt;Et pour la requête SQL :&lt;/p&gt;
&lt;!-- end paragraph 9474e42d-2001-4cc0-8e8f-6e2faf406a10--&gt;
&lt;!-- begin code 2e01474e-53db-4804-9bc1-e24b76e42d7d--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-2e01474e-53db-4804-9bc1-e24b76e42d7d&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT  key,
        values,
        aPlusOne(values) AS values_plus_one
FROM    nested_data&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 2e01474e-53db-4804-9bc1-e24b76e42d7d--&gt;
&lt;!-- begin paragraph 24d3a0bf-2b92-4862-a37a-87b573fd387d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-24d3a0bf-2b92-4862-a37a-87b573fd387d&quot;&gt;D&amp;#39;autres opérations d&amp;#39;ordre supérieur sont mises à dispositions :&lt;/p&gt;
&lt;!-- end paragraph 24d3a0bf-2b92-4862-a37a-87b573fd387d--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; : pour ne conserver que les éléments satisfaisant un prédicat.&lt;/li&gt;&lt;li&gt;&lt;code&gt;aggregate&lt;/code&gt; : pour agréger les éléments d&amp;#39;une collection selon une fonction d&amp;#39;agrégation (à la manière de &lt;code&gt;foldLeft&lt;/code&gt; en Scala).&lt;/li&gt;&lt;li&gt;&lt;code&gt;zip_with&lt;/code&gt; : associe les éléments de deux collections de même indice et applique à ces éléments la fonction passée en paramètre pour les &amp;quot;fusionner&amp;quot;.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_2 7f0d1004-f9a1-43b6-9935-4e54a20fec49--&gt;
&lt;h3 class=&quot;block-heading_2 level-0&quot; id=&quot;h-7f0d1004-f9a1-43b6-9935-4e54a20fec49&quot;&gt;Un peu plus pour les tableaux et les maps !&lt;/h3&gt;
&lt;!-- end heading_2 7f0d1004-f9a1-43b6-9935-4e54a20fec49--&gt;
&lt;!-- begin paragraph 0cf05330-b2b4-48e3-869c-64202706b341--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-0cf05330-b2b4-48e3-869c-64202706b341&quot;&gt;Cette version de Spark intègre aussi de nouvelles fonctions au niveau SQL et Dataframe pour la manipulation de tableaux et des maps.&lt;/p&gt;
&lt;!-- end paragraph 0cf05330-b2b4-48e3-869c-64202706b341--&gt;
&lt;!-- begin code f3f8fe56-0a60-419b-896a-1e681e27529f--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-f3f8fe56-0a60-419b-896a-1e681e27529f&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;array_union([1,2,3], [4,5,6])          = [1,2,3,4,5,6]
array_except([1,2,3], [4,3,2])         = [1]
flatten([[1,2,3], [4,5], [6]])         = [1,2,3,4,5,6]
array_repeat(2, 3)                     = [2,2,2]
arrays_zip([1,2] as i, [&amp;#39;a&amp;#39;,&amp;#39;b&amp;#39;] as j) = [{i:1, j:&amp;#39;a&amp;#39;}, {i:2, j:&amp;#39;b&amp;#39;}]
map_entries(map([1, 2], [&amp;#39;x&amp;#39;, &amp;#39;y&amp;#39;]))   = [ROW(1, &amp;#39;x&amp;#39;), ROW(2, &amp;#39;y&amp;#39;)]&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code f3f8fe56-0a60-419b-896a-1e681e27529f--&gt;
&lt;!-- begin paragraph 2b593194-7794-4cf0-9ece-1a2939e16b58--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-2b593194-7794-4cf0-9ece-1a2939e16b58&quot;&gt;La liste complète des fonctions ajoutées dans Spark est dans le ticket JIRA &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-23899&quot;&gt;[SPARK-23899]&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph 2b593194-7794-4cf0-9ece-1a2939e16b58--&gt;
&lt;!-- begin heading_1 fc2771ca-d1d1-4677-a81a-995848ec2b09--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-fc2771ca-d1d1-4677-a81a-995848ec2b09&quot;&gt;Data Source API v2&lt;/h2&gt;
&lt;!-- end heading_1 fc2771ca-d1d1-4677-a81a-995848ec2b09--&gt;
&lt;!-- begin paragraph dd424408-01a1-43ca-831d-5c4f85c0a65d--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-dd424408-01a1-43ca-831d-5c4f85c0a65d&quot;&gt;L&amp;#39;API Data Source est tout ce qui permet de lire ou écrire des données à travers Spark sur des systèmes de persistance (base de données, HDFS, Elasticsearch, Kafka...). La version précédente est simple et fonctionne dans la plupart des cas d&amp;#39;utilisation. Mais, elle est aussi fortement couplée avec l&amp;#39;API Spark de niveau supérieur (SparkContext, RDD, DataFrame), ce qui réduit la flexibilité de l&amp;#39;API Data Source en termes d’évolution. Elle ne permet pas non plus de bénéficier d&amp;#39;optimisations au niveau Spark, sachant qu&amp;#39;elle ne remonte pas d&amp;#39;informations du stockage physique comme le partitionnement ou l&amp;#39;organisation des données. Enfin, elle ne permet pas d&amp;#39;ajouter simplement de nouvelles opérations empilables (eg. filtrage, limitation de la quantité de données, échantillonnage...), elle n&amp;#39;offre pas de lecture en colonne et ne supporte le &lt;i&gt;streaming&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph dd424408-01a1-43ca-831d-5c4f85c0a65d--&gt;
&lt;!-- begin paragraph 5450fad4-10c1-468d-b186-cf6fa2ad8954--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5450fad4-10c1-468d-b186-cf6fa2ad8954&quot;&gt;Dans sa version actuelle Data source API v2 est avant tout un refactoring de la v1 qui permettra au fur et à mesure de faire évoluer plus rapidement l&amp;#39;API elle-même. Ainsi, la nouvelle API ne se base plus sur des &lt;i&gt;trait&lt;/i&gt; Scala, mais sur des &lt;i&gt;interface&lt;/i&gt; Java, pour des raisons d&amp;#39;interopérabilité. La nouvelle API s&amp;#39;affranchit de dépendances de l&amp;#39;API Spark et introduit la notion de &lt;i&gt;InternalRow&lt;/i&gt;, pour des manipulations propres à l&amp;#39;API Data Source avant de passer au type &lt;i&gt;Row&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 5450fad4-10c1-468d-b186-cf6fa2ad8954--&gt;
&lt;!-- begin paragraph ec11ff18-9bb7-4ec8-96a6-37a884e3203b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-ec11ff18-9bb7-4ec8-96a6-37a884e3203b&quot;&gt;Les évolutions de l&amp;#39;API sont à suivre dans le ticket Jira suivant : &lt;a href=&quot;https://issues.apache.org/jira/browse/SPARK-22386&quot;&gt;[SPARK-22386]&lt;/a&gt;.&lt;/p&gt;
&lt;!-- end paragraph ec11ff18-9bb7-4ec8-96a6-37a884e3203b--&gt;
&lt;!-- begin heading_1 e6c8c7c1-787e-470b-8663-713ee083779b--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-e6c8c7c1-787e-470b-8663-713ee083779b&quot;&gt;Et aussi…&lt;/h2&gt;
&lt;!-- end heading_1 e6c8c7c1-787e-470b-8663-713ee083779b--&gt;
&lt;!-- begin paragraph 231bd23f-0e43-4031-b9c8-a9c17fe398cf--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-231bd23f-0e43-4031-b9c8-a9c17fe398cf&quot;&gt;Spark 2.4 inclut en plus :&lt;/p&gt;
&lt;!-- end paragraph 231bd23f-0e43-4031-b9c8-a9c17fe398cf--&gt;&lt;ul class=&quot;block-bulleted_list_item level-0&quot;&gt;&lt;li&gt;Des améliorations dans PySpark, notamment en intégrant d&amp;#39;autant plus des UDFs Panda, déjà introduit dans la version 2.3, et aussi à travers la mise en place d&amp;#39;un modèle d&amp;#39;évaluation gloutonne (&lt;i&gt;eager&lt;/i&gt;) pour les &lt;i&gt;dataframe&lt;/i&gt; pour une meilleure expérience utilisateur dans le cadre par exemple de Jupyter (en utilisant la propriété &lt;code&gt;spark.sql.repl.eagerEval.enabled&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;Structured Streaming : les données de chaque micro-batch sont encodées dans un DataFrame et plus seulement dans un RDD.&lt;/li&gt;&lt;li&gt;Image Source : une fonction de lecture des images a été ajoutée dans Spark 2.3. En 2.4, il est possible de charger une image depuis &lt;code&gt;spark.read&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Améliorations dans SQL : optimisations des drivers et amélioration des performances.&lt;/li&gt;&lt;li&gt;Parquet : passage à la version &lt;a href=&quot;https://github.com/apache/parquet-mr/blob/master/CHANGES.md#improvement&quot;&gt;1.10.0.&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Lecteur ORC intégré et passage à la version &lt;a href=&quot;https://orc.apache.org/news/2018/06/29/ORC-1.5.2/&quot;&gt;1.5.2&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Le &lt;b&gt;Barrier Scheduling&lt;/b&gt; (qui fera à lui seul l&amp;#39;objet d&amp;#39;un autre article de blog, expliquant l&amp;#39;intérêt de cette fonctionnalité et son fonctionnement).&lt;/li&gt;&lt;/ul&gt;
&lt;!-- begin heading_1 7caf8ed1-8881-4836-8c22-11a9b2bbf81a--&gt;
&lt;h2 class=&quot;block-heading_1 level-0&quot; id=&quot;h-7caf8ed1-8881-4836-8c22-11a9b2bbf81a&quot;&gt;Conclusion&lt;/h2&gt;
&lt;!-- end heading_1 7caf8ed1-8881-4836-8c22-11a9b2bbf81a--&gt;
&lt;!-- begin paragraph 18d4b72b-5ce0-40f5-ad39-d2f723a5eb0f--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-18d4b72b-5ce0-40f5-ad39-d2f723a5eb0f&quot;&gt;En mettant beaucoup de composants à jour (Avro, Kafka, Scala), les gains en performance et l&amp;#39;amélioration du support des types complexes. Cette version est définitivement à déployer très rapidement sur les clusters.&lt;/p&gt;
&lt;!-- end paragraph 18d4b72b-5ce0-40f5-ad39-d2f723a5eb0f--&gt;
&lt;!-- begin paragraph 77d78696-8a5e-4083-be94-c6020de72b35--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-77d78696-8a5e-4083-be94-c6020de72b35&quot;&gt;Certaines de ces fonctionnalités changent vraiment la donne et peuvent grandement améliorer les projets sur Spark.&lt;/p&gt;
&lt;!-- end paragraph 77d78696-8a5e-4083-be94-c6020de72b35--&gt;
&lt;!-- begin paragraph 013004ad-5430-4430-ad5d-dec6d6da9b5c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-013004ad-5430-4430-ad5d-dec6d6da9b5c&quot;&gt;Néanmoins, cette version est encore un peu jeune. Il convient de ne déployer que certains projets sur cette version en mettant à jour progressivement les autres projets dès que la 2.4.1 sera sortie.&lt;/p&gt;
&lt;!-- end paragraph 013004ad-5430-4430-ad5d-dec6d6da9b5c--&gt;
&lt;!-- begin paragraph c78865b5-88dc-44a7-ab54-290e9cbc9882--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-c78865b5-88dc-44a7-ab54-290e9cbc9882&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph c78865b5-88dc-44a7-ab54-290e9cbc9882--&gt;</content>
  </entry>
    <entry xml:lang="fr">
    <id>urn:notion:eb4e79cc7be842569dc7f261dad3574c</id>
    <title>JSON ⇒ CSV en Spark - Première approche</title>
    <updated>2018-05-10T22:00:00Z</updated>
    <link href="https://univalence.io/blog/articles/json-csv-en-spark-premiere-approche.html"/>
    <!--summary Il arrive de vouloir convertir des structures imbriquées en structures non imbriquées notamment pour convertir du JSON en CSV, voici comment faire avec Spark! ⚡-->    <author>
      <name>Jonathan Winandy</name>
      <uri>https://univalence.io/blog/auteurs/jonathan-winandy.html</uri>
    </author>    <author>
      <name>François Sarradin</name>
      <uri>https://univalence.io/blog/auteurs/francois-sarradin.html</uri>
    </author>        <category term="Spark"></category>    <category term="JSON"></category>    <category term="CSV"></category>    <category term="Scala"></category>    <category term="Data"></category>    <content type="html">
&lt;!-- begin paragraph e312073d-90f1-4893-b491-38a5c9b7625c--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-e312073d-90f1-4893-b491-38a5c9b7625c&quot;&gt;Dans quelle mesure est-il possible de convertir une structure imbriquée (&lt;i&gt;nested structure&lt;/i&gt;) pour obtenir une structure non-imbriquée (&lt;i&gt;unnested structure&lt;/i&gt;) ? Cette question revient généralement lorsque nous essayons par exemple de convertir des données JSON en CSV. C&amp;#39;est un sujet qui peut se présenter dans le cadre de Spark.&lt;/p&gt;
&lt;!-- end paragraph e312073d-90f1-4893-b491-38a5c9b7625c--&gt;
&lt;!-- begin paragraph b3bda5d6-4103-4558-b9a2-38226c67960b--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b3bda5d6-4103-4558-b9a2-38226c67960b&quot;&gt;Par exemple, nous pouvons avoir ce JSON représentant un employé :&lt;/p&gt;
&lt;!-- end paragraph b3bda5d6-4103-4558-b9a2-38226c67960b--&gt;
&lt;!-- begin code 50c9a2f1-fb4a-472a-bb38-8f091ba3d483--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-50c9a2f1-fb4a-472a-bb38-8f091ba3d483&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  id: &amp;quot;ac23&amp;quot;,
  person: {
    firstname: &amp;quot;John&amp;quot;,
    lastname: &amp;quot;Doe&amp;quot;,
  },
  job: &amp;quot;data engineer&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 50c9a2f1-fb4a-472a-bb38-8f091ba3d483--&gt;
&lt;!-- begin paragraph d7244f10-9650-4436-9c58-b59fa61a4902--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d7244f10-9650-4436-9c58-b59fa61a4902&quot;&gt;Son équivalent en CSV pourrait être :&lt;/p&gt;
&lt;!-- end paragraph d7244f10-9650-4436-9c58-b59fa61a4902--&gt;
&lt;!-- begin code d4031d00-a9c0-4bf8-8ae5-4d2f5bbfa91c--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-d4031d00-a9c0-4bf8-8ae5-4d2f5bbfa91c&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;id,   person_firstname, person_lastname, job
ac23, John,             Doe,             data engineer&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code d4031d00-a9c0-4bf8-8ae5-4d2f5bbfa91c--&gt;
&lt;!-- begin paragraph 5acef66c-3428-4133-834a-a948c5206dc8--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-5acef66c-3428-4133-834a-a948c5206dc8&quot;&gt;Nous utilisons ici une simple concaténation des noms des champs permettant d&amp;#39;accès à un sous-niveau.&lt;/p&gt;
&lt;!-- end paragraph 5acef66c-3428-4133-834a-a948c5206dc8--&gt;
&lt;!-- begin paragraph 275ae95a-5600-45f4-b3e5-a7e567277e15--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-275ae95a-5600-45f4-b3e5-a7e567277e15&quot;&gt;Si nous utilisons des Dataframe Spark, nous allons devoir en étudier la structure (le schéma) pour établir la conversion nécessaire. La particularité d&amp;#39;une structure imbriquée est que son schéma apparaît sous la forme d&amp;#39;un arbre. Pour retrouver le nom des champs, sachant que les valeurs se trouvent au niveau des feuilles, nous devrons donc effectuer un parcours en profondeur pour constitué le nom des champs de la structure non-imbriquée.&lt;/p&gt;
&lt;!-- end paragraph 275ae95a-5600-45f4-b3e5-a7e567277e15--&gt;
&lt;!-- begin paragraph 72776cdf-9006-4569-93b0-b8e080b7eb14--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-72776cdf-9006-4569-93b0-b8e080b7eb14&quot;&gt;Par exemple, la structure ci-dessous :&lt;/p&gt;
&lt;!-- end paragraph 72776cdf-9006-4569-93b0-b8e080b7eb14--&gt;
&lt;!-- begin code 40f3872f-1569-4e22-a6ff-276eab9cbeec--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-40f3872f-1569-4e22-a6ff-276eab9cbeec&quot;&gt;&lt;code class=&quot;language-plain text&quot;&gt;* a
| * b
| | * d
| | | * e
| | | * f
| | * g
* h&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 40f3872f-1569-4e22-a6ff-276eab9cbeec--&gt;
&lt;!-- begin paragraph b2c4e2b6-32b1-4115-b0da-ba0505a6e642--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-b2c4e2b6-32b1-4115-b0da-ba0505a6e642&quot;&gt;Peut donner les champs suivant :&lt;/p&gt;
&lt;!-- end paragraph b2c4e2b6-32b1-4115-b0da-ba0505a6e642--&gt;
&lt;!-- begin paragraph 07daa1f1-77c8-4d23-b192-56b53c1edcb6--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-07daa1f1-77c8-4d23-b192-56b53c1edcb6&quot;&gt;&lt;code&gt;a_b_d_e, a_b_d_f, a_b_g, h&lt;/code&gt;&lt;/p&gt;
&lt;!-- end paragraph 07daa1f1-77c8-4d23-b192-56b53c1edcb6--&gt;
&lt;!-- begin paragraph 6abd0b1b-2dfb-40b0-a05d-123b5a4a94c3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-6abd0b1b-2dfb-40b0-a05d-123b5a4a94c3&quot;&gt;Si nous utilisons &lt;code&gt;_&lt;/code&gt; pour joindre le nom des champs.&lt;/p&gt;
&lt;!-- end paragraph 6abd0b1b-2dfb-40b0-a05d-123b5a4a94c3--&gt;
&lt;!-- begin paragraph 3ed02eab-4d6c-48cf-b789-c6b86cc4c418--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-3ed02eab-4d6c-48cf-b789-c6b86cc4c418&quot;&gt;Voici le code permettant de parcourir la structure imbriquée :&lt;/p&gt;
&lt;!-- end paragraph 3ed02eab-4d6c-48cf-b789-c6b86cc4c418--&gt;
&lt;!-- begin code 8c50fe1d-db24-49c7-a0e6-2de2b1385b33--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-8c50fe1d-db24-49c7-a0e6-2de2b1385b33&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;def walk(structure: StructType,
         prefix: Vector[String] = Vector.empty): Seq[Vector[String]] =
  for {
    field &amp;lt;- structure.fields
    fieldPath &amp;lt;- field.dataType match {
      case subStruct: StructType =&amp;gt; walk(subStruct, prefix :+ field.name)
      case _: ArrayType          =&amp;gt; Nil
      case _                     =&amp;gt; List(prefix :+ field.name)
    }
  } yield fieldPath&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 8c50fe1d-db24-49c7-a0e6-2de2b1385b33--&gt;
&lt;!-- begin paragraph 08f4ed86-db7f-476d-9483-3494451f3e89--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-08f4ed86-db7f-476d-9483-3494451f3e89&quot;&gt;À noter, que dans cette version, nous nous intéressons qu&amp;#39;aux &lt;i&gt;StructType&lt;/i&gt; (équivalent Dataframe des objets JSON) et nous supprimons les &lt;i&gt;ArrayType&lt;/i&gt;.&lt;/p&gt;
&lt;!-- end paragraph 08f4ed86-db7f-476d-9483-3494451f3e89--&gt;
&lt;!-- begin paragraph 8c531189-7022-4a37-834b-54390a0bb138--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-8c531189-7022-4a37-834b-54390a0bb138&quot;&gt;La conversion de structure imbriquée en structure non-imbriquée est faite donc en appelant notre fonction &lt;code&gt;walk&lt;/code&gt; sur le schéma du Dataframe récupéré. Les différents noms de champ sont joints par un &lt;code&gt;.&lt;/code&gt; pour le parcours dans le JSON et par un &lt;code&gt;_&lt;/code&gt; pour obtenir leur équivalent en nom de colonne CSV.&lt;/p&gt;
&lt;!-- end paragraph 8c531189-7022-4a37-834b-54390a0bb138--&gt;
&lt;!-- begin code 9c1a4624-da00-49ba-9ab3-50e288006565--&gt;
&lt;pre class=&quot;block-code level-0&quot; id=&quot;c-9c1a4624-da00-49ba-9ab3-50e288006565&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;import org.apache.spark.sql.functions.expr

for {
  fieldPath &amp;lt;- walk(schema)
} yield {
  val fieldref: Column = expr(fieldPath.names.mkString(&amp;quot;.&amp;quot;))
  val name: String     = fieldPath.names.mkString(&amp;quot;_&amp;quot;)

  fieldref.as(name)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- end code 9c1a4624-da00-49ba-9ab3-50e288006565--&gt;
&lt;!-- begin paragraph d8e04ec2-e7c9-4297-98f4-72db505703d3--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-d8e04ec2-e7c9-4297-98f4-72db505703d3&quot;&gt;Nous avons vu ici une approche permettant de convertir une structure imbriquée (de type JSON) en structure non-imbriqué (de type CSV). Une simple analyse structurelle permet de réaliser cette conversion. Néanmoins, nous ne traitons pas ici de certains aspect comme la gestion des &lt;i&gt;array&lt;/i&gt; et nous laissons Catalyst inférer le DataType. Ces points seront traités dans un autre article.&lt;/p&gt;
&lt;!-- end paragraph d8e04ec2-e7c9-4297-98f4-72db505703d3--&gt;
&lt;!-- begin paragraph 4d5d8a9f-5b32-4104-b5ee-a3205c2e0c80--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4d5d8a9f-5b32-4104-b5ee-a3205c2e0c80&quot;&gt;Cet article se base sur le code donné dans ce repo :&lt;/p&gt;
&lt;!-- end paragraph 4d5d8a9f-5b32-4104-b5ee-a3205c2e0c80--&gt;
&lt;!-- begin bookmark cace149a-605b-4de2-a20e-4152193a2ba2--&gt;
&lt;section class=&quot;block-bookmark&quot; id=&quot;b-cace149a-605b-4de2-a20e-4152193a2ba2&quot;&gt;&lt;a class=&quot;bookmark-container&quot; href=&quot;https://github.com/UNIVALENCE/schema-utils/blob/master/src/main/scala/io/univalence/schemautils/JsonoidToTable.scala&quot;&gt;    &lt;div class=&quot;bookmark-cover&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/90a51df46eb1df45ae2ac5d87685583cb90649d0960efd80bd4418a373b516b8/univalence/schema-utils&quot;&gt;&lt;/div&gt;    &lt;div class=&quot;bookmark-preview&quot;&gt;
      &lt;div class=&quot;bookmark-title&quot;&gt;schema-utils/src/main/scala/io/univalence/schemautils/JsonoidToTable.scala at master · univalence/schema-utils&lt;/div&gt;
      &lt;div class=&quot;bookmark-description&quot;&gt;Experiences avec Spark sur les schémas. Contribute to univalence/schema-utils development by creating an account on GitHub.&lt;/div&gt;
      &lt;div class=&quot;bookmark-link&quot;&gt;&lt;img class=&quot;bookmark-favicon&quot; src=&quot;null.png&quot;&gt; https://github.com/UNIVALENCE/schema-utils/blob/master/src/main/scala/io/univalence/schemautils/JsonoidToTable.scala&lt;/div&gt;
    &lt;/div&gt;&lt;/a&gt;&lt;/section&gt;
&lt;!-- end bookmark cace149a-605b-4de2-a20e-4152193a2ba2--&gt;
&lt;!-- begin paragraph 4fbf87ae-2d78-4342-9c7c-7cef1cac2243--&gt;
&lt;p class=&quot;block-paragraph level-0&quot; id=&quot;p-4fbf87ae-2d78-4342-9c7c-7cef1cac2243&quot;&gt;&lt;/p&gt;
&lt;!-- end paragraph 4fbf87ae-2d78-4342-9c7c-7cef1cac2243--&gt;</content>
  </entry>
  </feed>