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!"
};
Better Software Without If-Else :
https://medium.com/swlh/5-ways-to-replace-if-else-statements-857c0ff19357
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"
- 745