Les voies de la

Pas simple de comprendre ce qu'est Reason

  • Grosse hype à sa sortie...
  • Elle est un peu passée depuis
  • Reason ? ReasonML ?
  • site officiel

Reason est une nouvelle syntaxe au langage OCaml inspirée par Javascript

Première définition :

🤔

Mais pourquoi ?

Un peu de story telling ...

Il était une fois Jordan Walke

Jordan Walke a codé FaxJS en StandardML (SML) !

functor BFS (structure Q: QUEUE) =
  struct 
     datatype 'a tree
      = E
      | T of 'a * 'a tree * 'a tree
    fun bfsQ (q  : 'a tree Q.queue)  : 'a list = 
      if Q.isEmpty q then []
      else let
         val (t, q') = Q.remove q
        in case t
          of E => bfsQ q'
           | T (x, l, r) => let
                val q'' = Q.insert (r, Q.insert (l, q'))
               in
                 x  :: bfsQ q''
                end
         end
     fun bfs t = bfsQ (Q.singleton t)
  end
functor BFS (structure Q: QUEUE) = (* after Okasaki, ICFP, 2000 *)
  struct 
     datatype 'a tree
      = E
      | T of 'a * 'a tree * 'a tree
    fun bfsQ (q  : 'a tree Q.queue)  : 'a list = 
      if Q.isEmpty q then []
      else let
         val (t, q') = Q.remove q
        in case t
          of E => bfsQ q'
           | T (x, l, r) => let
                val q'' = Q.insert (r, Q.insert (l, q'))
               in
                 x  :: bfsQ q''
                end
         end
     fun bfs t = bfsQ (Q.singleton t)
  end
var End=Caml_exceptions.create("Utils-Test_algo.End");
function range(e,r,n,t){for(;;){var a=t,o=e,i=void 0
!==n?n:1;if(o===r)return List.rev(a);t=[o,a],n=i,e=o+i
|0}}function rangeGenerator(e,r,n){if(e===r)throw End;
return{value:e,next:function(t){return rangeGenerator(
e+n|0,r,n)}}}function generator(e,r){return{value:r,ne
xt:e}}export{End,range,rangeGenerator,generator};

Text

js_of_ocaml

Jordan Walke est un peu triste ...

Le language StandardML à été rejeté en bloc

+

=

Esy

Des goodies

reason-react

Une définition plus complète :

  • Ocaml
  • Syntaxe Sexy
  • Un ensemble d'outils de développement
  • Différentes destinations
  • 100% interopérable
  • features hype builtins:
    • JSX
    • Reason-React

Messenger

Réécriture incrémentale en Reason

Se concentrer sur les parties critiques

50% javascript

10 bugs remontés par semaine

50% Reason

10 bugs remontés

100% des devs chez Facebook ayant utilisé Reason ont reçu une augmentation

  • Reason à tous les avantages d'OCaml
  • programmation fonctionnelle
  • pas d'effet de bord
  • immuabilité
  • functions as first-class citizens
  • typage fort

==

Let binding

let greeting = "hello!";
let score = 10;
let newScore = 10 + score;
let message = {
  let part1 = "hello";
  let part2 = "world";
  part1 ++ " " ++ part2
};

Les types

let myInt1 = 5;
let myInt2: int = 5;
let myInt3 = (5: int) + (4: int);
type scoreType = int;
let x: scoreType = 10;
type coordinates('a) = ('a, 'a, 'a);

type intCoordinatesAlias = coordinates(int);
let buddy: intCoordinatesAlias = (10, 20, 20);
type student = {taughtBy: teacher}
and teacher = {students: list(student)};
type human = {
    name: string,
    children: list(human)
};

Typage implicite / explicite

Alias

Paramètres de type

Type récursif

Mutuellement récursif

String / character

let firstName = "Romain";
let lastName = "Commande";

Js.Console.log(firstName ++ " " ++ lastName);
let firstLetterOfAlphabet = 'a';

Booléen

let doYouLikeThisPresentation = true;
let areYouBored = false;

&&

||

!

<=, >=, <, >

==

===

!=

!==

int / float

let age = 34;
let happyBirthday = age + 1;
let age = 34.0;
let happyBirthday = age +. 1.0;

tuple

let ageAndName = (16, "Lil' Reason");
let my3dCoordinates = (20.0, 30.5, 100.0);
switch (isWindowOpen, isDoorOpen) { /* this is a 2-tuple */
| (true, true) => ...
| (true, false) => ...
| (false, true) => ...
| (false, false) => ...
}
let my3dCoordinates = (20.0, 30.5, 100.0);
let (_, y, _) = my3dCoordinates; /* now you've retrieved y */

record

type person = {
  name: string,
  age: int,
};

let romain = {
  name: "Romain",
  age: 34,
};

print_endline("Hello " ++ romain.name);
let happyBirthday = (person) => {
  {...person, age: person.age + 1};
};
type mutablePerson = {
  name: string,
  mutable age: int,
};

let happyBirthday = (person) => {
  person.age = person.age + 1;
};

punning

let name = "Romain";
let age= 34;

let romain = {name, age};
let romain = {name: name, age: age};

Variant

type response = Yes | No | Other(string);

let answer = (review) => {
  switch(review) {
	|Yes => "I know, I'm the best !"
  	|No => "I'm so sad..."
  	|Other("I love you") => "Me too ! I love me !"
  	|Other(string) => "Thank you for your advice"
  }
};

let review = Yes;
let result = answer(review);

La notion de "rien" n'existe pas

 «Je l’appelle mon erreur à un milliard de dollars. En 1965, je concevais le premier système de typage complet pour un langage orienté objet et je n'ai pas pu résister à ajouter la référence nulle, simplement parce que c'était si facile à implémenter. Ceci a conduit à un nombre incalculable d'erreurs, … qui ont probablement causé des dommages d'un milliard de dollars dans les quarante dernières années. »

Charles Antony Richard Hoare

On utilise un variant à la place

type option('a) = None | Some('a);
type quelquechoseOuPas(a') = RienDuTout | QuelqueChose(a');

Liste

let listeIngredients = ["Levure boulangere", "sel", "huile d'olive"];

let ajouteFarine = ingredients => ["farine", ...ingredients];

let pateAPizza = ajouteFarine(listeIngredients);

Js.Console.log(listeIngredients);
/*["Levure boulangere",["sel",["huile d'olive",0]]]*/
Js.Console.log(pateAPizza);
/*["farine",["Levure boulangere",["sel",["huile d'olive",0]]]]*/

Tableau

let theTruth = [|"Jordan", "is", "the", "best"|];

let firstItem = theTruth[0]; /* "Jordan" */

theTruth[0] = "Romain";
/* now [|"Romain", "is", "the", "best"|] */

functions

let greet = (name) => {
    let message = "Hello ";
    message ++ name;
};

let addCoordinates = (~x, ~y) => {
  /* use x and y here */
};

addCoordinates(~x=5, ~y=6);


let drawCircle = (~radius as r, ~color as c) => {
  setColor(c);
  startAt(r, r);
  /* ... */
};
drawCircle(~radius=10, ~color="red");

fonctions récursives

let rec count = (list) => {
    switch(list) {
        | [] => 0,
        | [a, ...rest] => 1 + count(rest) 
    }
};

unit

let doNothing = () => {
  ()
};

doNothing();
// Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
'use strict';


function doNothing(param) {
  
}

exports.doNothing = doNothing;
/* No side effect */

()

auto-currification

let additionne = (valeur1, valeur2) => {
    valeur1 + valeur2;
}

let ajouteACinq = additionne(5);
let quinze = ajouteACinq(10);

additionne(5, 10);
additionne(5)(10);

paramètre optionel

let drawCircle = (~color, ~radius=?, ()) => {
  setColor(color);
  switch (radius) {
  | None => startAt(1, 1)
  | Some(r_) => startAt(r_, r_)
  }
};

drawCircle(~color="red", ());
drawCircle(~color="red")();

pipe first ->

validateAge(getAge(parseData(person)))

person
  ->parseData
  ->getAge
  ->validateAge

If / else

let message = if (isMorning) {
  "Good morning!"
} else {
  "Hello!"
};

Pattern matching

type response = Yes | No | Other(string);

let answer = (review) => {
  switch(review) {
	|Yes => "I know, I'm the best !"
  	|No => "I'm so sad..."
  	|Other("I love you") => "Me too ! I love me !"
  	|Other(string) => "Thank you for your advice"
  }
};

let review = Yes;
let result = answer(review);

Promesse

let myPromise = Js.Promise.make((~resolve, ~reject) => resolve(. 2));

myPromise
|> Js.Promise.then_(value => {
     Js.log(value);
     Js.Promise.resolve(value + 2);
   })
|> Js.Promise.then_(value => {
     Js.log(value);
     Js.Promise.resolve(value + 3);
   })
|> Js.Promise.catch(err => {
     Js.log2("Failure!!", err);
     Js.Promise.resolve(-2);
   });

Module

module School = {
  type profession = Teacher | Director;

  let person1 = Teacher;
  let getProfession = (person) =>
    switch (person) {
    | Teacher => "A teacher"
    | Director => "A director"
    };
};

Tout fichier .re est un module

Pas d'import. On utilise le module

Tout module doit avoir un nom unique

Open

let p = {
  open School;
  getProfession(person1);
};
/* le contenu de School n'est plus visible ici */

let p = School.(getProfession(person1));

include

module BaseComponent = {
  let defaultGreeting = "Hello";
  let getAudience = (~excited) => excited ? "world!" : "world";
};

module ActualComponent = {
  /* the content is copied over */
  include BaseComponent;
  /* overrides BaseComponent.defaultGreeting */
  let defaultGreeting = "Hey";
  let render = () => defaultGreeting ++ " " ++ getAudience(~excited=true);
};

Imperative loop

for (myBinding in startValue to endValue) {
  /* utilisez myBinding ici */
};


while (testCondition) {
  /* déclarations */
};

Object

type tesla = {
  .
  color: string,
};

let obj: tesla = {
  val red = "Red";
  pub color = red;
};

Js.log(obj#color) /* "Red" */

JSX

En natif !!!

module Counter = {
  [@react.component]
  let make = (~name) => {
    let (count, setCount) = React.useState(() => 0);

    <div>
      <p> {React.string(name ++ " clicked " ++ string_of_int(count) ++ " times")} </p>
      <button onClick={_ => setCount(_ => count + 1)}>
        {React.string("Click me")}
      </button>
    </div>
  };
};

JSX

Mais c'est pas le même que celui de React !

interopérabilité

[@bs.val] external alert: string => () = "window.alert";

Convaincu ?

Code safe by design

Syntaxe sexy et light

L'intégration en douceur dans des projets existants

Bref, je vous encourage vraiment à essayer

Des points négatifs ?

Pas d'UTF-8 !!!!

let name = {js| "Romain Commandé" |js}

Bucklescript ne fourni pas de sourcemaps

Des erreurs de types pas toujours clair

Des questions ?

Les voies de la Reason

By Romain Commandé

Les voies de la Reason

Petite présentation sans prétention sur Reason par un mec qui sait faire des "Hello world"

  • 761