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
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
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
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 = ; Last expression is the returned value
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
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»
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
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
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
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
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
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
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
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 .*/
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
(ReasonReact.stringToElement(string_of_int(state))) send(Increment)) title="+" :/> send(Decrement)) title="-" :/> ! Send actions in your render function