Retour vers le turfu : R, le web, et webR

Auteur : Colin Fay
Tags : Actualités, Autour de R
Date :

Le monde de la tech est jalonné de petites révolutions. Un jour, quelqu’un a eu l’idée d’empiler des transistors les uns à côté des autres, et de fil en aiguille, nous voilà rendus en 2024 à pouvoir commander un burger sous la douche, et demander à ChatGPT de nous faire nos plans d’entrainement de triathlon. Quelle époque.

Mais nous ne sommes pas ici pour causer révolution numérique et histoire de l’Internet (bien que ça soit typiquement le genre de sujet dont j’adore parler…) mais pour discuter de l’une des révolutions les plus récentes de ce petit monde qu’est le web : WebAssembly, et son arrivée récente dans le R world avec webR.

webAssemQuoi ?

Note : afin de vous épargner les détails trop techniques, cette partie prend quelques raccourcis pour aider à la compréhension.

Quelques digressions sur le langage machine

Un jour, en repas de famille, on m’a demandé si j’écrivais directement en binaire. Réponse : non, et me voilà en train d’expliquer que l’humain n’écrit plus en binaire depuis longtemps, et que l’on écrit dans un langage de programmation qui ressemble à du langage naturel. Et que ce langage “humain” est ensuite traduit dans un second langage “ordinateur”, qui est ensuite exécuté par l’ordinateur.

Un ordinateur n’a aucune idée de ce que veut dire a + b, il ne sait pas quoi en faire. Il lui faut une langue intermédiaire, qui va expliciter de manière très spécifique les étapes à suivre pour arriver au résultat : aller chercher les valeurs en mémoire à des emplacements spécifiques, les additionner, et stocker le résultat dans un autre emplacement mémoire, avant de le renvoyer à l’utilisateur.

C’est cette “langue du milieu” que l’on l’appelle “Assembly”. Une langue très bas niveau. Schématiquement, tous les langages de programmation transforment ce que vous leur donnez à manger en Assembly. Chaque ordinateur a son propre Assembly, et tous les langages de programmation installés sur une machine vont devoir parler ce même Assembly. Autrement dit, a + b en Python va devoir être traduit dans le même Assembly que a + b en R, en C, en Java, etc.

Si l’on devait faire une métaphore de la vie de tous les jours, prenons l’exemple de : demander à votre enfant d’aller ranger ses chaussures dans le meuble. Votre phrase “Peux-tu aller ranger tes chaussures s’il te plait ?” (le langage de haut niveau comme R) va être inconsciemment traduit dans le cerveau de votre ado par une série de commandes plus spécifiques : se déplacer jusqu’aux chaussures, se baisser, les prendre en main, se relever, aller jusqu’au meuble, etc, (le langage Assembly), que le corps transforme encore une nouvelle fois en instructions machines : déployer telle force pour soulever le pied, le plier de tant de degré, le déplacer de X cm, relâcher la pression de tant de degré pour poser le pied, etc (on est ici sur le binaire, les instructions très bas niveau qu’aucun être humain doué de raison ne rédige encore en 2024).

Et maintenant, place à webAssembly

Quand vous avez ouvert cette page web, il s’est passé plein de trucs : votre cerveau a eu un petit rush d’adrénaline “Woaw, un article de Colin“. Du côté de votre navigateur, trois langages ont été appelés à la rescousse : le HTML, qui est le plan, la structure générale de la page (purement textuel), le CSS, qui a transformé ce texte incompréhensible en magnifique toile de bleu et d’orange, et JavaScript, qui permet de rendre la page interactive (montrer/cacher des éléments, les faire bouger, ouvrir un modal de contact, prévenir la NSA que vous êtes passés pas là, et plein d’autres choses).

JavaScript est le langage de l’interactivité du web, du calcul : il permet de définir des variables, de créer et d’appeler des fonctions, de faire des boucles et des exécutions conditionnelles, etc. Une sorte de R pour le navigateur, mais qui a été codé en 10 jours (true story).

Dans les années 2000, l’adoption a été rapide (notamment parce que tout le monde pensait que c’était du Java), mais la réalité est que JavaScript n’était pas ouf niveau performance : au-delà des tâches simples, les calculs devenaient vite complexes et longs, même si le lancement de V8, un moteur JS made in Google, a bien amélioré les choses. Malgré tout, on reste bien loin des performances atteintes par des langages compilés comme C, Rust, et les autres.

C’est là qu’est venue une idée : et si on pouvait prendre un langage de programmation, le compiler dans un “langage glue”, outputant un fichier binaire qu’on pourrait lire directement dans JavaScript ? (Oui, il y a vraiment des gens qui ont des passions très spécifiques.) Si vous avez bien tout lu jusqu’ici, vous avez compris où je voulais en venir : c’est de là qu’est né WebAssembly (abrégé WASM), un “langage glue” dont les binaires peuvent être appelés depuis JS, et qui bénéficie de toutes les performances et optimisations d’un langage compilé.

Passons les détails techniques, mais en résumé : on écrit du code C, on l’envoie dans un programme, et on récupère un fichier binaire qu’on peut ouvrir depuis une console JavaScript, et appeler les objets/fonctions exportées par notre code C.

Et la version encore plus courte pour ceux qui n’ont pas suivi : WebAssembly permet de prendre un bout de code dans un langage de programmation qui n’est pas JS, et de rendre ce code disponible directement dans JS.

webR, vous avez dit webR ?

WASM n’est pas une techno toute neuve : né en 2015, il est devenu un standard du web en 2019. Et comme dans le petit monde de R on aime bien tout faire en avance, c’est en 2023 qu’a vu le jour une version WASM de R, sobrement nommée webR (je troll bien sûr, porter R en WASM n’était pas une mince affaire pour plein de raisons que je n’expliquerai pas ici, mais invitez-moi un mercredi soir si besoin).

La release de ce projet permet maintenant d’embarquer R dans des environnements JS, que ça soit dans votre navigateur ou dans NodeJS, la version backend de JavaScript. La version compilée embarque même une console R, directement dans le navigateur (vous avez même une petite démo juste en dessous). Ouvrez cette page, et tout se passera directement dans votre navigateur : https://webr.r-wasm.org/latest/

Par exemple, vous pouvez intégrer du R directement dans votre site web, comme le texte que vous voyez à la ligne ci dessous :

Cliquez ici pour voir le code
<div id = "webrdiv"></div>
<script type="module" >
  import('https://webr.r-wasm.org/latest/webr.mjs').then(
    async ({ WebR }) => {
      const webR = new WebR();
      await webR.init();
      let result = await webR.evalR('tools::toTitleCase("le texte suivant est généré par webr")');
      let output = await result.toJs();
      document.getElementById("webrdiv").innerText = output.values;
    }
  );
</script>

 

Voire, soyons fou, une console R directement dans votre navigateur :


Chargement de webR....

Cliquez ici pour voir le code
<div>
<input id="input" spellcheck="false" autocomplete="off" type="text" placeholder = "Entrez du code ici"/>
<button onclick="globalThis.sendInput()" id="run">Lancer le code</button>
<pre><div id="out">Chargement de  webR....</div></pre>

</div>
<script type="module" >
  import('https://webr.r-wasm.org/latest/webr.mjs').then(
    async ({ Console }) => {
      const webRConsole = new Console({
        stdout: line => document.getElementById('out').append(line + '\n'),
        stderr: line => document.getElementById('out').append(line + '\n'),
        prompt: p => document.getElementById('out').append(p),
      });
      document.getElementById("out").innerText = "";
      webRConsole.run();
      let input = document.getElementById('input');
      globalThis.sendInput = () => {
        webRConsole.stdin(input.value);
        document.getElementById('out').append(input.value + '\n');
        input.value = "";
      }
      input.addEventListener(
        "keydown",
        (evt) => {if(evt.keyCode === 13) globalThis.sendInput()}
      );
    }
  );
</script>

 

Code adapté depuis https://docs.r-wasm.org/webr/latest/examples.html#creating-an-interactive-webr-repl-console

webR, et prédiction pour un turfu radieux

Une application web a toujours deux composantes : un côté “client”, c’est-à-dire la partie consommée par les utilisateurs, ce que vous êtes en train de faire en ce moment (charger la page, la lire, cliquer dessus…) et une partie “serveur”, qui est la partie non visible par le visiteur, un serveur au loin qui contient des données, qui a envoyé cette page web à votre navigateur, et à laquelle vous allez potentiellement envoyer des informations pendant votre visite.

Dans une application {shiny}, ces deux composantes sont indissociables : vous interagissez, dans le navigateur, avec l’app web, l’app envoie à un serveur des informations, le serveur effectue des opérations en R, puis les renvoie au navigateur, qui les affiche au client (vous). Ce que cette petite opération sous-entend, c’est qu’il faut un serveur toujours en route, et que si on veut augmenter le nombre de visiteurs que peut gérer l’app, il faut brancher plus de serveur.

En soi, cette composante n’est pas propre à {shiny} : n’importe quelle app web “classique” aura ce modèle client / serveur, même si les spécificités de {shiny} le rend plus complexe à déployer à grande échelle, mais ça c’est (encore) une histoire pour un autre jour. Les développeurs du monde entier se sont rendu compte que c’était tout de même dommage, tous ces ordinateurs clients avec de la puissance de calcul, qui glandaient en attendant que le serveur maintenu (et payé) par les devs ait fini de calculer.

De ce constat sont nées les applications intégrant des modules WebAssembly, permettant de déporter des calculs historiquement faits par les serveurs, directement dans le navigateur. Le tout, en gardant les infos confidentielles côté serveur : authentification et autorisation, données sensibles, etc., accessible via des requêtes web standards.

Le dernier-né de cette famille des modules embarqués en webAssembly, c’est {shinylive}, vous permettant de faire tourner votre app {shiny} directement dans le navigateur sans serveur.

Comment ça marche ? Vous prenez votre application {shiny}, vous passez à la moulinette, et vous vous retrouvez avec un dossier rempli de HTML, de CSS et de JS. Un simple serveur web est alors en mesure de servir ce fichier, en l’envoyant au client, et c’est le navigateur du client qui s’occupera de faire tourner le code R, directement dans la console JavaScript.

L’un des problèmes de cette solution : la confidentialité des accès et des données. Même si le rêve du full “browser based” est alléchant, reste un problème crucial : comment gérer tout ce que le client n’est pas censé voir ? Par exemple, la connexion à la base de données, les infos utilisateurs, les tokens… Car oui, à partir du moment où vous partez sur une solution {shinylive}, cela veut dire que tout doit se passer dans le navigateur, et donc que ce dernier doit avoir accès à TOUTES les informations.

Autre point noir actuel : l’applicatif, au lancement, va (re)télécharger tous les packages dont a besoin l’app. Autant dire que le temps de chargement n’est pas optimisé. Mais on est encore aux prémisses de la techno.

webR et NodeJS

Une autre piste s’ouvre cependant, et c’est probablement dans celle-là qu’il faudra mettre ses billes : appeler R depuis NodeJS, le runtime backend de JavaScript, via webR.

JavaScript est historiquement un langage pour le navigateur… mais pas que ! NodeJS est une implémentation de JavaScript, côté serveur, c’est-à-dire appelé dans un terminal. La promesse : unifier le frontend (ce que l’utilisateur voit), et le backend (les calculs qui sont fait côté serveur). Un langage pour les gouverner tous (et dans les ténèbres les lier, vous connaissez la chanson). En clair, si aujourd’hui vous ouvrez un terminal et tapez R dedans, vous allez pouvoir lancer du code R, et si vous êtes à l’ancienne, ou que vous travaillez sur des serveurs, il vous arrive surement de lancer Rscript, une commande qui va ouvrir un fichier à l’extention .R et exécuter le code qui s’y trouve.  NodeJS, c’est la même soupe : vous pouvez ouvrir un terminal intéractif qui attendra que vous lui donniez à manger, ou lancer des fichier .js.

NodeJS, c’est aujourd’hui LA techno derrière un nombre incalculable d’applications web (qui tournent côté serveur, et qui sont appelées via votre navigateur) et d’applicatifs de bureau (Slack, Spotify, VSCode, via une techno appelée Electron). Bref, JavaScript avait le monopole des navigateur, il l’a maintenant aussi sur les backends des applications. Alors, autant en profiter.

Là où ça nous intéresse aujourd’hui, c’est que webR a aussi été porté en package NodeJS — autrement dit, en plus de l’appeler dans le navigateur, nous pouvons appeler R dans un application NodeJS. Et ça, en natif.

Ce que cela promet, je vous laisse l’imaginez un instant : avoir un algo en R, développé par une équipe spécialisées, qui peut être intégrée directement dans NodeJS, en natif. Sans bidouille un peu louche, sans sub process, vraiment, en natif. Pas d’installation de R à prévoir, et les objets R sont manipulables directement par JavaScript. Des applications {shiny} en app de bureau grace à Electron. Le tout dans une belle infra web montée avec des outils qui sont fait pour, et qui font bien leur job, plutôt que d’essayer de réinventer la poudre en R à chaque fois qu’on a besoin d’un truc qui vient du web. Franchement, c’est pas un futur radieux ça ?

Mais ça, on en parle une autre fois ?


Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *


À lire également

Nos formations Certifiantes à R sont finançables à 100% via le CPF

Nos formations disponibles via moncompteformation.gouv.fr permettent de délivrer des Certificats reconnues par l’état, enregistrées au répertoire spécifique de France Compétences. 3 niveaux de certifications existent :

Contactez-nous pour en savoir plus.

Calendrier

04/06/2024

14/05/2024

14/05/2024