C'est qui Reason(ML) et qu'est ce qu'il fait là?

C'est qui Reason(ML) et qu'est ce qu'il fait là?

2f6c2dcd4ba8fc44f1e7f0b2ed3d587d?s=128

Maxime Thirouin

November 20, 2018
Tweet

Transcript

  1. @MoOx C'est qui Reason(ML) et qu'est ce qu'il fait là?

    1
  2. @MoOx Do you need redux? 2 Previously in React Toulouse…

  3. @MoOx Redux
 is a predictable state container for JavaScript apps.

    3 Previously in React Toulouse…
  4. @MoOx What redux does, ReasonReact and Elm do 4 Actions

    are types, reducers are functions,
 state is immutable, but at the language level Previously in React Toulouse…
  5. @MoOx “done in collaboration with the ReactJS team” 5 https://reasonml.github.io/reason-react/blog/2017/09/01/reducers.html

    Previously in React Toulouse…
  6. @MoOx 6 JS

  7. @MoOx • Available everywhere • Easy to start • Multiparadigm

    7
  8. @MoOx • Super hard to master • Error prone •

    Hacky 8
  9. @MoOx 9 let add = (a, b) => a +

    b;
  10. @MoOx 10 add(1, 1); // 2 add(1, "1"); // "11"

    add(1, null); // 1 add(1, undefined); // NaN
  11. @MoOx Dynamic typing + Automatic coercion 11

  12. @MoOx 12

  13. @MoOx 13

  14. @MoOx • ESLint? • Flow? • TypeScript? 14

  15. @MoOx 15

  16. @MoOx Developers are humans 16 And we make mistakes

  17. @MoOx What about TESTS? 17

  18. @MoOx We covers what we think about. 18 And we

    are developers. And we make mistakes
  19. @MoOx 19

  20. @MoOx 20

  21. @MoOx 21 1. A language 2. A syntax 3. A

    compiler 4. An ecosystem
  22. @MoOx OCaml 22 %

  23. @MoOx • Has a great type system • Infers most

    types • Multiparadigm 23
  24. @MoOx Syntax can be hard Tooling can be hard 24

  25. @MoOx let rec map f = function [] -> []

    | a::l -> let r = f a in r :: map f l 25
  26. @MoOx 1. A language 2. A syntax 3. A compiler

    4. An ecosystem 26
  27. @MoOx ~ES2015+ 27 JS

  28. @MoOx 28 let add a b = a + b

    let add = (a, b) => a + b; ML RE
  29. @MoOx 29 1. A language 2. A syntax 3. A

    compiler 4. An ecosystem
  30. @MoOx BuckleScript 30

  31. @MoOx RE 31 JS

  32. @MoOx • Outputs clean and performant code • Makes interop

    easy 32 BUCKLESCRIPT
  33. @MoOx 33 1. A language 2. A syntax 3. A

    compiler 4. An ecosystem
  34. @MoOx 34 npm

  35. @MoOx A bit of practice? 35

  36. @MoOx 1. Basics 2. Modules 3. Functions 4. Variants 5.

    Interop 36
  37. @MoOx 37 BASICS 1; /* int */ 1.0; /* float

    */ "Hello"; /* string */ 'H'; /* char */ true; /* bool */
  38. @MoOx 38 BASICS [1, 2, 3]; /* list(int) */ [|1,

    2, 3|]; /* array(int) */ Some(x); /* option('a) */ () /* unit */
  39. @MoOx 39 RECORDS let me = { name: "Matthias", age:

    24, }; let meLater = { ...me, age: 25, }; type user = { name: string, age: int, }; You declare the type of the record You can create records of this type You can create spread values to create a new record from a previous one
  40. @MoOx 1. Basics 2. Modules 3. Functions 4. Variants 5.

    Interop 40
  41. @MoOx 41 App.re MODULES Every file is a module and

    filenames are unique Welcome.re let sayHi = (name) => Js.log("Hi " ++ name ++ "!"); Welcome.sayHi("people"); Every top level binding is exported by default To open a module, just use its capitalised name
  42. @MoOx 42 MODULES module Talk { include Presentation; let slides

    = ["a", "b", "c"]; let slidesWithIndex = slides ->Belt.List.mapWithIndex((index, item) => (index, item)); }; open OtherModule; Declare a module within a module Include modules like a static extends Make all module available in scope
  43. @MoOx 1. Basics 2. Modules 3. Functions 4. Variants 5.

    Interop 43
  44. @MoOx 44 FUNCTIONS let add = (a, b) => a

    + b; let addDoubles = (a, b) => { let doubleA = a * 2; let doubleB = b * 2; doubleA + doubleB; }; Every function has its signature let add: (int, int) => int = <fun>; Last expression is the returned value
  45. @MoOx 45 FUNCTIONS Pipe operator! Functions are auto curried That

    means that they return a function taking the rest of the arguments as long as you didn't pass all the required ones [1, 2, 3] ->Belt.List.keep(item => item mod 2 === 0) ->Belt.List.map(item => item * 2);
  46. @MoOx 46 FUNCTIONS let make = ( ~rootPath="./", ~fileExtensions=["re", "js"],

    ~watch=false, ~onChange=?, () ) => { /* ... */ }; Functions can have labelled arguments Their order at function application doesn't mater Optionals Default values Final non labelled argument When you have optional arguments, this tells the compiler «I have all the arguments there»
  47. @MoOx 1. Basics 2. Modules 3. Functions 4. Variants 5.

    Interop 47
  48. @MoOx 48 WHAT'S WRONG WITH THIS? type state = {

    isLoading: false, hasErrored: false, data: null, };
  49. @MoOx 49 IMPOSSIBLE STATES ARE ACTUALLY POSSIBLE isLoading hasErrored data

    FALSE FALSE NULL TRUE FALSE NULL FALSE TRUE NULL TRUE TRUE NULL FALSE FALSE DATA TRUE FALSE DATA FALSE TRUE DATA TRUE TRUE DATA NotAsked Loading Errored Loaded
  50. @MoOx 50 THE SOLUTION: VARIANTS type t('a) = | NotAsked

    | Loading | Loaded('a) | Errored; A variant type is a «OR» type A type can be parametrised aka Generics Every constructor can hold data like a container type
  51. @MoOx 51 THE SOLUTION: VARIANTS switch (resource) { | NotAsked

    => "" | Loading => "Loading ..." | Loaded(value) => "Loaded: " ++ value | Errored => "An error occurred" }; You use pattern matching to extract values
  52. @MoOx 52 THE SOLUTION: VARIANTS switch (resource) { | NotAsked

    => "" | Loading => "Loading ..." | Loaded(value) => "Loaded: " ++ value }; Warning 8: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Errored
  53. @MoOx 53 THERE'S NO NULL type option('a) = | Some('a)

    | None; switch (value) { | Some(value) => value | None => "Nothing to see" }; Move null checks at compile time If it's maybe a value, you need to handle it explicitly
  54. @MoOx 54 1 ARRAY VS LIST 1 2 3 1

    1 1 2 1 3 1
  55. @MoOx 55 LIST IS A VARIANT TYPE type list('a) =

    | Empty | Head('a, list('a)); let list = Head(1, Head(2, Head(3, Empty)));
  56. @MoOx 56 LIST IS A VARIANT TYPE If there's nothing

    left Return empty Return a head with transformed item then transform the rest with a recursive call Recursive let rec map = (f, list) => switch (list) { | Empty => Empty | Head(x, rest) => Head(f(x), map(f, rest)) };
  57. @MoOx 57 LIST IS A VARIANT TYPE Reassigned Reassigned let

    foldLeft = (array, f, acc) => { let index = -1; while(++index < array.length) { acc = f(acc, array[index]) }; return acc };
  58. @MoOx 58 ACTUALLY IN JS // http://es5.github.io/#x15.4.4.21 function foldLeft(arr, fn)

    { var array = new Object(arr), index = -1, length = array.length >>> 0, acc, hasInitialValue; if (Object.prototype.toString.call(fn) != "[object Function]") { throw new TypeError(); } if (arguments.length > 1) { acc = arguments[1]; } else { hasInitialValue = false; while (++index < length) { if (!(hasInitialValue = index in array)) { continue; } acc = array[index]; break; } if (!hasInitialValue) { throw new TypeError(); } } while (++index < length) { if (!(index in array)) continue; acc = fn.call(void 0, acc, array[index], index, array); } return acc; };
  59. @MoOx 59 ACTUALLY IN JS: DYNAMIC TYPING // http://es5.github.io/#x15.4.4.21 function

    foldLeft(arr, fn) { var array = new Object(arr), index = -1, length = array.length >>> 0, acc, hasInitialValue; if (Object.prototype.toString.call(fn) != "[object Function]") { throw new TypeError(); } if (arguments.length > 1) { acc = arguments[1]; } else { hasInitialValue = false; while (++index < length) { if (!(hasInitialValue = index in array)) { continue; } acc = array[index]; break; } if (!hasInitialValue) { throw new TypeError(); } } while (++index < length) { if (!(index in array)) continue; acc = fn.call(void 0, acc, array[index], index, array); } return acc; };
  60. @MoOx 60 LIST IS A VARIANT TYPE let rec fold_left

    = (f, acc, list) => switch (list) { | [] => acc | [x, ...rest] => fold_left(f, f(acc, x), rest) };
  61. @MoOx 1. Basics 2. Modules 3. Functions 4. Variants 5.

    Interop 61
  62. @MoOx How do we incrementally adopt Reason on an existing

    codebase? 62 INTEROP
  63. @MoOx 63 INTEROP: EXTERNAL MODULES [@bs.module "../myExistingJsModule"] external myExistingJsModule :

    string = ""; Declare that it's a module located at "../myExistingModule" The module is of string type Js.log(myExistingJsModule); The module is only imported when used
  64. @MoOx 64 INTEROP: EXTERNAL MODULES // Generated by BUCKLESCRIPT VERSION

    2.2.4, PLEASE EDIT WITH CARE 'use strict'; var MyExistingJsModule = require("../myExistingJsModule"); console.log(MyExistingJsModule.myExistingJsModule); /* Not a pure module */
  65. @MoOx 65 INTEROP: CREATE AND READ JAVASCRIPT OBJECTS let myJsObject

    = { "name": "Matthias", "age": Js.Null.return(24) }; Js.log(myJsObject##name);
  66. @MoOx 66 INTEROP: ACCESS THE JS STDLIB array |> Js.Array.map(item

    => item * 2);
  67. @MoOx ReasonReact 67

  68. @MoOx 68

  69. @MoOx 69 WHERE DOES REACT COME FROM? • Immutability •

    Functional approach • PropTypes
  70. @MoOx 70 WHERE DOES REACT COME FROM? Creator of React

    Jordan Walke wrote the first React prototype in SML
  71. @MoOx 71 REASONREACT HAS A LOT BAKED IN • Components

    • Router • Reducers • Subscriptions
  72. @MoOx 72 JSX <MyComponent prop optionalProps=?optionalValue boolProp=false />

  73. @MoOx 73 REASONREACT: BASIC COMPONENT let component = ReasonReact.statelessComponent("HelloWorld"); let

    make = (~name, _children) => { ...component, render: (_self) => <div> (ReasonReact.string("Hello " ++ name)) </div> };
  74. @MoOx 74 REASONREACT: REDUCER COMPONENT Define the type of the

    state Define all possible actions type state = int; type action = | Increment | Decrement; let component = ReasonReact.reducerComponent("Counter");
  75. @MoOx 75 REASONREACT: REDUCER COMPONENT Return an update for each

    action can be Update, UpdateWithSideEffect, SideEffect … reducer: (action, state) => switch (action) { | Increment => ReasonReact.Update(state + 1) | Decrement => ReasonReact.Update(state - 1) }
  76. @MoOx 76 REASONREACT: REDUCER COMPONENT Send actions in your render

    function render: ({state, send}) => <div> (ReasonReact.stringToElement(string_of_int(state))) <Button onClick=((_) => send(Increment)) title="+" /> <Button onClick=((_) => send(Decrement)) title="-" /> </div>
  77. @MoOx 77 REASONREACT: INTEROP ReasonReact.wrapReasonForJs(/* ... */) ReasonReact.wrapJsForReason(/* ... */)

  78. @MoOx Sum up 78

  79. @MoOx • Safer • Less bugs • Easy refactoring 79

    SUM UP
  80. @MoOx We all come up with the wrong assumptions on

    our first pass at something 80 SUM UP
  81. @MoOx Fix your assumptions at the type level and let

    the compiler show you the way 81 SUM UP
  82. @MoOx Spaghetti Reason code is better than spaghetti JS 82

    SUM UP
  83. @MoOx C’est une bonne raison pour utiliser Reason 83 SUM

    UP: ONE QUESTION
  84. @MoOx 84 Questions ? https://moox.io/ @MoOx Maxime Thirouin Matthias Le

    Brun @bloodyowl Slides ripped from