Ocsigen 2, une bouffée d'air frais pour le web
Ocsigen est un framework web conçu pour le langage OCaml, un langage de programmation fonctionnelle statiquement typé, et développé par le laboratoire français PPS. Relativement différent des technologies auxquelles les développeurs web sont habitués, il propose d'écrire des sites complets, fiables et performants dans le langage OCaml, et a récemment connu d'importantes mises à jour.
Des fondations mûrement réfléchies
Comme beaucoup de technologies alternatives (moins connues que, par exemple, PHP ou Java), Ocsigen tente de présenter des solutions originales à des problèmes récurrents. Avec la version 2, il est possible qu'un certain nombre de ces idées soient suffisamment présentables pour essaimer dans d'autres langages et frameworks, et il est donc intéressant de comprendre pourquoi et comment Ocsigen a vu le jour.
Modéliser les interactions web
L'une des premières sources d'inspiration citées par les auteurs d'Ocsigen est un article de Christian Queinnec (enseignant-chercheur de l'Université de Paris 6) publié en 1999, qui présente notamment un problème récurrent dans les applications web un peu complexes : les formulaires multiples. Lorsqu'un utilisateur passe une commande sur un site de ventes en ligne, il est fréquent qu'il doive valider plusieurs étapes successives (un pour commander des articles, un pour indiquer son adresse de livraison, etc.).
Il est a priori facile de programmer ces interactions avec l'utilisateur : une première idée est d'utiliser des formulaires différents, et des informations sauvegardées par cookies. Mais cette approche a plusieurs défauts : premièrement, que se passe-t-il si l'utilisateur passe plusieurs commandes en même temps ? Les cookies sont partagés par toutes les fenêtres. On pourra alors préférer stocker des informations intermédiaires dans un champ caché.
Mais si notre application souhaite que l'utilisateur adopte un comportement linéaire (un formulaire après l'autre), en réalité les options proposées par le navigateur sont beaucoup plus riches : par exemple, il est possible que l'utilisateur, trouvant que la page se charge lentement, la réactualise (et donc, qu'il renvoie les informations — sans pour autant vouloir passer une deuxième commande). Il est également possible qu'il estime s'être trompé, et qu'il revienne en arrière.
Naturellement, une bonne application doit gérer efficacement tous ces cas de figure. Pour ce faire, Queinnec cherche un moyen de ramener le problème (modéliser les interactions client/serveur) à un autre problème, plus connu. Il propose le mécanisme des continuations, qui ont déjà servi à modéliser des comportements compliqués (comme ceux induits par l'instruction goto de certains langages).
Les continuations
On voudrait alors utiliser les continuations dans le code d'un site web, pour gérer proprement les actions des utilisateurs. Bien entendu, il est passablement pénible de systématiquement devoir programmer par continuations. C'est là que le concept de framework prend toute son importance : Ocsigen propose un ensemble de concepts et d'outils qui permettent de gérer tout ceci quasi-automatiquement, et distingue les concepts de « services » et de « coservices » (au sens de « services auxiliaires »).
Les premiers correspondant aux services principaux du site, qui sont en quelque sorte des points d'entrée (pouvant être mémorisés dans un signet, par exemple). Les seconds, au contraire, n'existent qu'à côté d'un service principal, et peuvent d'ailleurs être créés dynamiquement (et expirer au-delà d'un délai particulier), ce qui est une façon d'implémenter les continuations.
Prenons un exemple : supposons que nous voulions demander un nombre à l'utilisateur via un formulaire, puis un deuxième dans un second formulaire (lorsque l'utilisateur soumet le premier), et enfin que nous voulions afficher la somme de ces deux nombres lorsqu'il soumet le second formulaire. Bien sûr, l'utilisateur ne doit pas arriver sur la deuxième ou la troisième page sans être passé par la première.
Voici un pseudo-code ressemblant à ce qu'il faudrait écrire avec Ocsigen (mais débarrassé des détails techniques, comme la génération du code HTML — les arguments nommés sont précédés de ~, comme en OCaml) :
let service1 = service ~url:"/calc/" ~paramètres:[] let service2 = service ~url:"/calc/" ~paramètres:[entier "i"] let handler1 = formulaire ~suivant:service2 ~code:( int_input # Entier que l'on passera à service2 ) let handler2 = let service3 = coservice # Coservice créé dynamiquement ~session:session_courante # Coservice limité à l'utilisateur courant ~paramètres:[entier "j"] in let handler3 = afficher(i + j) in attacher(service3, handler3); formulaire ~suivant:service3, ~code:( int_input # Entier que l'on passera à service3 ) attacher(service1, handler1); attacher(service2, handler2);
Bien que la syntaxe d'OCaml (et donc le code nécessaire pour utiliser Ocsigen) soit un peu plus compliqué, l'idée y est : le concept un peu difficile à comprendre de continuation est abstrait par la notion de coservice créé dynamiquement.
Ici, les deux services ont la même URL. Selon que l'utilisateur envoie un paramètre ou non, il est donc possible de savoir à quel stade de l'interaction on se trouve. Et le coservice, lui, n'est rattaché à aucune URL : il est donc impossible de tomber dessus par hasard.
La programmation fonctionnelle, adaptée pour le web
Parmi les autres travaux précurseurs d'Ocsigen, on trouve également un article de Jean-Vincent Loddo (enseignant-chercheur de l'Université de Paris 13). Celui-ci est notamment l'auteur d'un article sur la programmation fonctionnelle appliquée au développement web.
Selon Loddo, il est facile de se représenter les sites web comme des fonctions, qui dépendent des arguments passés (par requêtes GET ou POST), interrogent éventuellement une base de données, et produisent ensuite un résultat. La nature non-connectée du HTTP renforce cette intuition : dans sa conception même, le protocole fait expliciter aux programmeurs les relations entre les différentes pages, et notamment les informations qui doivent être transmises.
Bien que cet argument soit à tempérer, notamment par la présence de sauvegarde d'un état (via les cookies côté client, ou tout autre mécanisme de stockage sur le serveur), il encourage à chercher une nouvelle façon de représenter les services web : pourquoi pas les fonctions, justement ?
Ocsigen, de nouveaux concepts pour le web
Ocsigen diffère des technologies habituellement utilisées pour le web. Ainsi, plutôt que de demander au programmeur d'écrire différents codes sources qui correspondent aux différentes pages du site web (comme on le ferait par exemple en PHP), il propose d'écrire un programme OCaml correspondant au site (éventuellement réparti dans plusieurs fichiers, pour mieux organiser le code, mais ça n'est pas une nécessité), et de compiler tout ce site dans un module, qui sera ensuite chargé par le serveur web, et qui fournira des services aux clients.
La plupart des nouveautés introduites par la version 2 sont des améliorations des idées qui existaient déjà dans Ocsigen. Passons donc en revue les spécificités de la technologie.
Des services typés
À la lumière des travaux précédents, le projet Ocsigen (dirigé par Vincent Balat, enseignant-chercheur de l'Université de Paris 7) propose la programmation (dans le langage OCaml) d'applications riches pour le web, notamment basées sur le concept de services.
Ceux-ci ne dépendent pas du fichier dans lequel ils sont placés (comme ça serait par exemple le cas pour une page web sur un site PHP), mais sont « attachés » par le programmeur à une URL particulière, avec une liste d'arguments que doit pouvoir recevoir le service (et qui peuvent être tout ce que l'on est habitué à envoyer avec un formulaire). Ces arguments doivent être typés, c'est à dire que le programmeur doit indiquer leur type (entier, flottant, chaîne… ou de nouveaux types dont le programmeur précise la représentation).
C'est ensuite le serveur particulier d'Ocsigen qui s'occupe de choisir le service correspondant à une URL et à des paramètres donnés. Le fait que les paramètres des services soient typés permet d'éliminer certaines erreurs potentielles, sans qu'il soit nécessaire de tester ces parties minutieusement (et en empêchant les visiteurs de tomber dessus par erreur !).
En outre, dans un code Ocsigen, les différents services du site sont des valeurs OCaml comme les autres, et peuvent être utilisées comme cibles pour les liens et les formulaires (on écrira ainsi let nouveau_formulaire = get_form ~service:service_cible … pour déclarer un formulaire vers un service nommé service_cible). Comme le code OCaml est ensuite compilé, plusieurs vérifications sont faites : notamment, le compilateur s'assure que vous utilisez des services qui existent (ce qui diminue les chances d'introduire des liens morts dans le site). Et surtout, il s'assure que les formulaires envoient bien toutes les données au service ciblé, et qu'elles ont toutes le bon type !
Si ça n'est pas le cas, le service ciblé par l'utilisateur ne sera pas appelé. À la place, celui-ci sera redirigé sur un service traitant d'erreurs (s'il a été défini — ce qui est notamment possible grâce au paramètre optionnel ~fallback, lors de la création d'un service).
Un code vérifié
OCaml est un langage statiquement et fortement typé, ce qui veut dire qu'il détermine tous les types des variables utilisées au moment de la compilation (et pas pendant l'exécution, comme par exemple PHP ou JavaScript), et qu'il s'assure que toutes les opérations utilisées ont un sens qui ne nécessite pas de conversions implicites (contrairement, ici encore, à ces deux langages). Cela permet d'écrire du code plus fiable (car le compilateur ne laisse pas passer d'erreurs de type qui ne seront détectées que pendant l'exécution), et aussi plus rapide (car il optimise en conséquence) lorsque celui-ci est compilé nativement. Comme le compilateur OCaml fait ceci systématiquement (en ne demandant que très peu d'indications de la part du programmeur), pourquoi ne pas l'utiliser pour vérifier également les codes XHTML, JavaScript et SQL ?
Bien qu'une application Ocsigen puisse fonctionner avec un code XHTML écrit indépendamment, l'une des autres idées de Jean-Vincent Loddo était d'utiliser un langage fonctionnel typé moderne pour vérifier le code XHTML écrit par le développeur. Dans Ocsigen, il est ainsi possible de s'assurer que son code XHTML suit relativement fidèlement les recommandations du W3C, car celui-ci est vérifié à la compilation. Pour cela, un des composants du framework, nommé Eliom, propose beaucoup de fonctions OCaml toutes prêtes, servant à générer le code XHTML. Celles-ci s'appuient sur une fonctionnalité avancée du système de types du langage OCaml, les variants polymorphes, qui servent à taguer les valeurs du programme, et qui permettent d'écrire des fonctions acceptant « au moins » certains arguments, ou « au plus » certains autres. Par exemple, il est nécessaire de passer à la fonction head un titre, tagué avec la fonction title — et cette fonction title ne pourra être utilisée nulle part ailleurs sur la page.
Le programmeur n'est pas obligé de directement se soucier de ces variants polymorphes, et peut utiliser directement les fonctions d'Eliom, qui permettent donc de manipuler le code XHTML directement comme des fonctions OCaml. De même, Eliom fournit un certain nombre de fonctions toutes prêtes pour générer des formulaires typés, ou des liens vers des services qui passent des arguments correctement typés. On élimine donc un ensemble relativement important d'erreurs dans le programme, grâce à l'aide du compilateur. Le code a alors la forme de
let titre = … (* valeur du titre *) in (html (head (title titre) []) (body [h1 [titre]; … (* suite de la page *)]))
D'autres modules sont disponibles, soit pour émettre d'autre code que du XHTML (comme du SVG ou du HTML 5), soit pour utiliser des syntaxes un peu moins lourdes. Mais les vérifications d'Ocsigen ne se limitent de toute façon pas au code serveur et au XHTML : en réalité, le framework va plus loin, et permet d'écrire, en OCaml, du code JavaScript et SQL, et donc de vérifier ceux-là également.
Du JavaScript en OCaml : le module js_of_ocaml
De plus en plus de technologies permettent aujourd'hui d'écrire des scripts web clients dans un autre langage que JavaScript. Citons par exemple CoffeeScript, Cappuccino, Dart (le nouveau langage de Google)… elles proposent alors des compilateurs qui transforment les programmes écrits dans ces langages en JavaScript, avec une perte d'efficacité le plus souvent insignifiante. Le but est systématiquement d'ajouter des fonctionnalités à JavaScript, comme un typage statique (dans le cas de Dart), ou des messages (pour Objective-J, le langage de Cappuccino).
Ocsigen permet, de la même façon, d'écrire en OCaml du code qui sera ensuite compilé en JavaScript. Cela permet d'écrire dans un même programme OCaml du code qui sera réparti (à la compilation) chez le client et chez le serveur, et donc de partager du code et des types, de rendre le code source plus compréhensible — et surtout de pouvoir écrire des applications clientes en OCaml, ce qui permet de bénéficier des forces de ce langage, dont le typage statique.
Ainsi, les objets JavaScript sont manipulables directement dans du code OCaml, et un certains nombre de types sont traduits d'un langage vers l'autre. Quand ce n'est pas le cas (comme par exemple pour les chaînes de caractères, trop différentes d'un langage à l'autre), des fonctions de conversion sont accessibles via le module Js. De manière générale, le partage de valeurs entre du code OCaml serveur et du code OCaml traduit ensuite en JavaScript demande quelques précautions. Pour aider le programmeur, Ocsigen dispose d'un certain nombre d'outils. Par exemple, il met en place une extension syntaxique, basée sur la bibliothèque deriving.
Inspirée du mot clef deriving du langage Haskell, celle-ci permet de déclarer de nouveaux types de données qui seront automatiquement sérialisés au format JSON, en laissant OCaml calculer le format exact. On écrirait alors type nouveau_type = … deriving (Json). Par la suite, n'importe quelle valeur de ce type pourrait être envoyée du client au serveur, ou vice-versa, via le module Eliom_bus, pendant une interaction avec l'utilisateur.
Du SQL en OCaml : le module Macaque
Disposer d'un langage fiable et statiquement typé pour écrire l'application serveur ne suffit pas à garantir l'absence d'erreurs de types, dont certaines ne seront découvertes qu'à l'exécution : même si un programme OCaml est typé correctement, certaines fonctions (dites partielles) peuvent échouer à l'exécution, et provoquer des erreurs (qui doivent être traitées par le programmeur, à l'aide de code supplémentaire et rébarbatif).
Ainsi, on voudrait pouvoir écrire des requêtes sur une base de données dans un langage vérifié, éventuellement embarqué dans le langage de programmation serveur. Cela permet, ici encore, de faire correspondre plus fortement les typées utilisés par la base de données, et par le reste de l'application. Au sein d'Ocsigen, le module Macaque (encore relativement incomplet et peu documenté du fait de sa jeunesse) est chargé de jouer les intermédiaires, et permet d'écrire des requêtes par compréhension, directement dans du code OCaml.
Par exemple, une requête SELECT a la forme <:select< row | row in $sql_table$; row.id < 50 >>. Macaque s'occupe, à la compilation, de traduire la deuxième forme en une requête SQL, et permet de vérifier (sans qu'il soit nécessaire de faire de test) que la table utilisée possède les bons champs, que ceux-ci sont bien du type souhaité, etc. Le module permet également d'encoder dans le type (et donc de vérifier) certaines propriétés, comme la non-nullabilité des valeurs (qui garantit, sous certaines conditions, l'impossibilité de travailler par erreur sur une valeur SQL NULL).
Il y aurait encore beaucoup de choses à dire sur Macaque et tous les problèmes que ce module essaye de résoudre. Son auteur (gasche) a écrit à son sujet un rapport de stage qui décrit la problématique et les principaux choix de conception de la bibliothèque, ainsi que quelques articles de blog.
Un petit mot sur Lwt
Conçue à l'origine pour supporter Ocsigen, Lwt est une bibliothèque de multithreading léger pour OCaml. Elle implémente un système de multithreading coopératif, une des façons les plus simples de représenter l'exécution simultanée de plusieurs sous-programmes. L'idée est de donner la main à un programme qui s'exécute jusqu'à ce qu'il décide lui-même de céder sa place (interrompant alors son calcul). Un autre programme est alors choisi, et est exécuté à son tour, et ainsi de suite. Ce mécanisme permet de gérer un grand nombre de threads, et d'économiser de la mémoire en évitant de dupliquer une pile et tout un contexte d'exécution.
Mais là où réside sa force principale, c'est dans la facilité déconcertante qu'offre Lwt pour faire de la programmation asynchrone (accès aux fichiers, envoi ou réception de données sur le réseau, etc.) : lors d'une opération bloquante, les autres threads sont automatiquement exécutés pendant l'attente et une fois que le résultat est apparu, le thread qui a fait l'appel est réveillé.
Enfin, Lwt vient avec une large bibliothèque couvrant beaucoup de besoins : manipulations de fichiers, communication sur le réseau, exécution de processus, etc. Les différents composants d'Ocsigen s'appuient dessus, ce qui garantit une capacité de passage à l'échelle raisonnablement bonne. Le programmeur est également invité à les utiliser pour structurer l'exécution de son programme, dans le style monadique. Un port de la bibliothèque a également été réalisé pour JavaScript, sous le nom de Lwt_js : les threads légers s'adaptent parfaitement au cadre d'exécution mono-threadé de JavaScript.
Quel sera le web de demain ?
Parmi les frameworks web utilisés aujourd'hui, il est clair qu'Ocsigen est absolument insignifiant. Très peu de sites sérieux l'utilisent, et à peine plus de projets communautaires. Cependant, bien qu'elle soit encore inachevée et trop peu documentée, la version 2 du framework confirme la qualité des idées initiales : les développeurs ont réussi à obtenir un ensemble de bibliothèques puissantes, qui peuvent être combinées pour produire des sites web efficaces.
Au final, il est peu probable qu'Ocsigen s'impose un jour comme un ténor des frameworks web. En revanche, il confirme l'intérêt de continuer à explorer de nouveaux concepts pour la programmation (et le web en particulier), et suggère de nouvelles pistes à suivre dans des technologies plus traditionnelles.
Liens complémentaires
- Le site web d'Ocsigen, qui contient toute la documentation du framework. Le tutoriel officiel est basé sur des exemples concrets, et notamment sur la création d'une application de dessin collaboratif. Attention, il est nécessaire de déjà avoir appris OCaml.
- Une introduction en français à Ocsigen 2, qui propose plus d'explications pour les débutants, et fournit un exemple très simple. À suivre.
- La récente thèse de Benjamin Canou sur la programmation web typée, qui détaille notamment O'Browser, une machine virtuelle pour OCaml en JavaScript (une approche différente de celle décrite plus haut). Claire, complète et écrite en français, cette thèse aborde également un tour d'horizon des technologies web comparables à Ocsigen (et notamment Opa, développé par la société française MLState).
Merci à asmanur pour son paragraphe sur Lwt et ses différents conseils. Merci également à anmee et spider-mario.
(Cette dépêche est sous licence
.)
5 commentaire(s)
| Auteur | Commentaire |
|---|---|
| Poulet | le 14/10/2011 à 19:42:44 |
| Dinosaure | le 14/10/2011 à 20:19:18 |
| Nuki | le 15/10/2011 à 15:15:23 |
| Poulet | le 15/10/2011 à 15:18:22 |
| Dinosaure | le 15/10/2011 à 19:02:16 |
Vous devez être connecté pour commenter cette news.
Se connecter