ReasonML : la série
#ReasonMLJe me lance dans l'exercice de la série d'article. Pour commencer, je souhaite partager avec vous un langage et un ecosytème qui me passionne depuis 3 ans ReasonML
Si vous ne connaissez pas encore ce langage, il s'agit d'une syntaxe de OCaml développée au sein de Facebook par Jordan Walke, le papa de ReactJS. Facebook n'en est pas à ses début avec l'écosystème OCaml puisque ce dernier a notament servi à réaliser le prototype de react avant de passer sur Javascript pour des raisons marketing, à réaliser la machine virtuelle PHP/Hack HHVM ou encore à réaliser le typechecker javascript Flow
A Reason why
L'intention derrière le projet ReasonML tiens dans cette promesse
Reason lets you write simple, fast and quality type safe code while leveraging both the JavaScript & OCaml ecosystems.
Dis autrement, partant du postulat que la syntaxe est un problème à l'accès au merveilleux écosystème OCaml, ReasonML propose d'introduire une syntaxe le plus proche possible de celle de Javascript au dessus du modèle de type d'OCaml. Dès le départ, il y a clairement une intention de faciliter le Javascript as a bytecode, nous aurons l'occasion d'en reparler, mais pas que. A l'inverse, des langages tels que le populaire Typescript ou le plus confidentiel Purescript, en passant par Clojurescript ou Elm, la volonté n'est pas de créer un nouveau langage dédié à la compilation vers js, mais un langage complet dont la compilation vers js n'est qu'une des possibilités. Dès lors, s'appuyer sur un ecosystème riche de plus de 20 ans d'expérience est un gage de confiance.
Tour d'O-Reason
Avant de poursuivre, je souhaite donner quelques éléments de syntaxe :
Déclaration de valeurs (let bindings)
Si vous avez déjà utilisé Flow, vous ne devriez pas être surpris. Sinon le mot clé let
sert à lié une valeur à un nom, ici greeting
, suivi de la déclaration de type, : string
puis de la valeur = "hello"
.
/* Ceci est un commentaire */
let greeting : string = "hello";
Les valeurs sont immutables par défaut en ReasonML. C'est plutôt une excellente nouvelle, néanmoins il est possible de simuler des variables en utilisant des références ; dans la plupart des situations, vous ne devriez pas en avoir besoin.
let score : int = 10;
let scoreMutable : ref(int) = ref(0);
scoreMutable := 5;
Il est possible de définir des alias de type afin de rendre le code plus lisible, ce qui est toujours appréciable dans une démarche DDD.
type scoreType = int; /* type alias */
let myScore : scoreType = 7;
Les fonctions sont des valeurs comme les autres et peuvent être exprimées dans le système de types
let concatStr : (string, string) => string = (a, b) => a ++ b ;
/* ++ est l'opérateur de concaténation de 2 string */
Inférence de type
ReasonML est capable d'inférer le type de vos expressions, ce qui permet d'écrire du code très proche de ce que vous feriez en Javascript.
let twelve = 12; /* val twelve : int */
let addInt = (a, b) => a + b; /* val addInt : (int, int) => int */
let addFloat = (a, b) => a +. b; /* val addInt : (float, float) => float */
let aListOfInt = [1, 2, 5]; /* val aListOfInt : list(int) */
Les types peuvent recevoir des paramètres, à l'image de génériques dans d'autres langages. Les paramètres de types commencent toujours par '
.
type coordinate('a) = ('a, 'a, 'a);
type intCoordinate = coordinate(int);
let int3DPoint: intCoordinate = (1, 2, 10);
type floatCoordinate = coordinate(float);
let float3DPoint: floatCoordinate = (1., 2., 10.);
type transfert('a, 'b) = coordinate('a) => coordinate('b);
type intToFloatTransfert = transfert(int, float);
let intToFloatCoodinate: intToFloatTransfert = ((x, y, z)) => (float_of_int(x), float_of_int(y), float_of_int(z));
Opérateur de chaînage
ReasonML fournit un opérateur de chaînage (pipe operator) |>
qui fonctionne à l'image du pipe |
unix, c'est à dire que la valeur à gauche est fournit comme paramètre à droite de l'expression à droite de l'opérteur :
let compose = (f, g, x) => g(f(x));
/* === */
let compose2 = (f, g, x) => f(x) |> g;
let foo = (f,g) => compose(f,g, 5);
/* === */
let bar = (f,g) => 5 |> compose(f,g);
Application partielle
Les fonctions ReasonML sont curryfié c'est à dire que la fonction de type ('a,'b) => 'c
est juste du sucre syntaxique pour la fontion de type 'a => 'b => 'c
let add = (x, y) => x + y; /* === let add = x => y => x + y */
let add5 = add(5); /* === let add5 = y => add(5,y) */
let sub = (x, y) => x - y; /* === let sub = x => y => x - y */
let sub2 = 2 |> sub; /* === let sub2 = x => sub(x, 2) */
React to the future
Les motivations qui ont animé la création de ReasonML (en anglais) :
Un programme Reason-able
Dans cette série de découverte, nous aborderons ensemble prochainement :
- Types de données algébriques
- Les modules ReasonML
- Outillage d'un projet ReasonML
- Découverte d'Irmin
- Gestion des erreurs
Pour illustrer le propos, nous allons développer un générateur de personnage pour le jeu de rôle Les secrets de la 7eme mer seconde édition.
[EDIT - 27/12/2020] : Arrêt de la série ReasonML
Les évènements de l'été 2020, aillant amené à la création de Rescript ont très fortement réduit mon intérêt pour ces langages.
La direction prise ne correspond plus à ce que j'attends, mes prochains billets seront probablement tournés autour de OCaml. Il est probable que je "traduise" certains billets en OCaml, si vous êtes intéressés, envoyer moi un MP sur Twitter https://twitter.com/Oteku