Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A good Reason for typing

A good Reason for typing

New version of the talk I gave at Rennes.js on 05/04/2018

Matthias Le Brun

April 05, 2018
Tweet

More Decks by Matthias Le Brun

Other Decks in Programming

Transcript

  1. <script> window.beOpinionAsyncInit = function() { BeOpinionSDK.init({ account: 'YOUR_ACCOUNT_ID', }); BeOpinionSDK.watch();

    }; !</script> <script async src="https:#//widget.beopinion.com/sdk.js%">!</script> <div class="BeOpinionWidget%">!</div>
  2. WHAT'S LESS COOL ABOUT JS let add = (a, b)

    '=> a + b; What's its signature?
  3. WHAT'S LESS COOL ABOUT JS add(1, 1); #// 2 add(1,

    "1"); #// "11" add(1, null); #// 1 add(1, undefined); #// NaN
  4. WHAT'S COOL ABOUT OCAML • Has a great type system

    • Infers most types • Multiparadigm
  5. WHAT'S LESS COOL ABOUT OCAML let rec map f =

    function [] !-> [] | a*::l !-> let r = f a in r *:: map f l
  6. A FAMILIAR SYNTAX let add a b = a +

    b let add = (a, b) '=> a + b; ML RE
  7. npm

  8. BASICS 1; ,/* int .*/ 1.0; ,/* float .*/ "Hello";

    ,/* string .*/ 'H'; ,/* char .*/ true; ,/* bool .*/ [1, 2, 3]; ,/* list(int) .*/ [|1, 2, 3|]; ,/* array(int) .*/ Some(x); ,/* option('a) .*/ () ,/* unit .*/
  9. 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
  10. 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
  11. MODULES module Talk { include Presentation; let slides = ["a",

    "b", "c"]; let slidesWithIndex = slides 2|> List.mapi((index, item) '=> (index, item)); }; open OtherModule; Declare a module within a module Include modules like a static extends Make all module available in scope
  12. 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
  13. FUNCTIONS [1, 2, 3] 2|> List.filter(item '=> item mod 2

    **=== 0) 2|> List.map(item '=> item * 2); 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
  14. 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»
  15. 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
  16. 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
  17. THE SOLUTION: VARIANTS switch (resource) { | NotAsked '=> ""

    | Loading '=> "Loading **..." | Loaded(value) '=> "Loaded: " .++ value | Errored '=> "An error occurred" }; You use pattern matching to extract values
  18. 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
  19. 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
  20. LIST IS A VARIANT TYPE type list('a) = | Empty

    | Head('a, list('a)); let list = Head(1, Head(2, Head(3, Empty)));
  21. LIST IS A VARIANT TYPE let rec map = (f,

    list) '=> switch (list) { | Empty '=> Empty | Head(x, rest) '=> Head(f(x), map(f, rest)) }; If there's nothing left Return empty Return a head with transformed item then transform the rest with a recursive call Recursive
  22. LIST IS A VARIANT TYPE let foldLeft = (array, f,

    acc) '=> { let index = -1; while(.++index < array.length) { acc = f(acc, array[index]) }; return acc }; Reassigned Reassigned
  23. 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 55>>> 0, acc, hasInitialValue; if (Object.prototype.toString.call(fn) 2!= "[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; };
  24. 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 55>>> 0, acc, hasInitialValue; if (Object.prototype.toString.call(fn) 2!= "[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; };
  25. LIST IS A VARIANT TYPE let rec fold_left = (f,

    acc, list) '=> switch (list) { | [] '=> acc | [x, **...rest] '=> fold_left(f, f(acc, x), rest) };
  26. 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
  27. 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 .*/
  28. INTEROP: CREATE AND READ JAVASCRIPT OBJECTS let myJsObject = {

    "name": "Matthias", "age": Js.Null.return(24) }; Js.log(myJsObject*##name);
  29. REASONREACT: BASIC COMPONENT let component = ReasonReact.statelessComponent("HelloWorld"); let make =

    (~name, _children) '=> { **...component, render: (_self) '=> <div> (ReasonReact.stringToElement("Hello " .++ name)) !</div> };
  30. REASONREACT: REDUCER COMPONENT type state = int; type action =

    | Increment | Decrement; let component = ReasonReact.reducerComponent("Counter"); Define the type of the state Define all possible actions
  31. REASONREACT: REDUCER COMPONENT reducer: (action, state) '=> switch (action) {

    | Increment '=> ReasonReact.Update(state + 1) | Decrement '=> ReasonReact.Update(state - 1) } Return an update for each action can be Update, UpdateWithSideEffect, SideEffect …
  32. REASONREACT: REDUCER COMPONENT render: ({state, send}) '=> <div> (ReasonReact.stringToElement(string_of_int(state))) <Button

    onClick=((_) '=> send(Increment)) title="+" :/> <Button onClick=((_) '=> send(Decrement)) title="-" :/> !</div> Send actions in your render function
  33. We all come up with the wrong assumptions on our

    first pass at something SUM UP
  34. Fix your assumptions at the type level and let the

    compiler show you the way SUM UP