ReScript Logo

@shritesh / NashFP

Lineage

  • BuckleScript: Compile OCaml to readable JS
  • ReasonML: JS-like syntax for (native) OCaml
  • ReScript: BuckleScript rebranded with JS-focused syntax

Hello World

Js.log("Hello World")

FizzBuzz

open Js

for n in 1 to 20 {
  switch (mod(n, 3), mod(n, 5)) {
  | (0, 0) => "FizzBuzz"
  | (0, _) => "Fizz"
  | (_, 0) => "Buzz"
  | (_, _) => Int.toString(n)
  }->log
}

ReScript vs. Elm

  • Not a framework
  • Impure
  • Overhead-free interop with JS
  • Human readable JS output
  • More powerful type system
  • Arrays by default

ReScript vs. TypeScript

  • Not a superset of JavaScript
  • Simpler language
  • Sound type system, not opt-in
  • (Ridiculously) fast compiler
  • Smaller community and library bindings

Language Tour

Let and blocks

Immutable bindings

let answer = 21 * 2

Block scope; returns last expression

let message = {
  let part1 = "Hello"
  let part2 = "world"
  part1 ++ " " ++ part2
}

Types

  • Strong, static and sound
  • Global type inference

Type annotation

let count: int = 42_000
let count = (42_000: int)

Type alias

type occurence = (char, int)
let lInHello: occurence = ('l', 2)

Types (contd.)

Type parameters (generics)

type vec2<'a> = ('a, 'a)
let point: vec2<int> = (10, 10)
let point: vec2<float> = (10.0, 10.0)

let points: array<vec2<int>> = [(1,1), (2,2), (3,3)]

Operators

Different operators for different types

3 + 5
3.0 +. 5.0
"Nash" ++ "FP"

Custom operators will be added back soon.

String interpolation

let name = "Shritesh"
let age = 25
let greeting = `hello ${name}` // Strings only
let message = j`hello $age years old, $name`

Functions

let greet = () => "Hello"

let rec factorial = n =>
  if n <= 1 { 1 } else { n * factorial(n - 1) }

let make = (~bread, ~cheese, ~meat) => 
  j`$meat sandwich with $cheese cheese on $bread`

let sandwich = make(~cheese="feta", ~bread="flatbread", ~meat="lamb")

More features: Optional labels, explicit optionals, default values, uncurried functions.

Records

Nominal product types requiring type declaration

type person = {
  name: string,
  age: int
}
let shritesh = {name: "Shritesh", age: 25} // type is inferred
let olderShritesh = {...shritesh, age: shritesh.age + 1}

Mutation and ref

type player = {
  name: string,
  mutable score: int
}
let mario = {name: "Mario", score: 10}
mario.score = mario.score + 1
// built into the language
type ref<'a> = {
  mutable contents: 'a
}
let i = ref(0)
while i.contents < 10 {
  Js.log(i.contents)
  i := i.contents + 1
}

Objects

Structural, inferred, polymorphic product types; Compiles to JS Objects

let shritesh = {
  "name": "Shritesh",
  "nationality": "Nepali"
}

let zuko =  {
  "name": "Zuko",
  "breed": "Persian"
}

let print = entity => Js.log(entity["name"])

print(shritesh);
print(zuko);

Variants

Nominal sum types

type shape =
  | Point
  | Circle(float)
  | Triangle({base: float, height: float}) // inline record
  | Rectangle(float, float)

let area = shape => switch shape {
  | Point => 0.0
  | Circle(r) => Js.Math._PI *. r *. r
  | Triangle(t) => t.base *. t.height /. 2.0
  | Rectangle(l, b) => l *. b
}

Rectangle(3.0, 3.0)->area->Js.log

Option and Result

type option<'a> = None | Some('a)
type result<'a, 'b> = Ok('a) | Error('b)

Recursive types

type rec node<'a> =
  | None
  | Node('a, node<'a>)

let linkedList = Node(10, Node(20, None))

Polymorphic variants

Anonymous variants that are structually typed

let drawVegetable = color => switch color {
  | #Green => "lettuce"
  | #Red => "radish"
  | #White => "mushroom"
  }

let drawFruit = color => switch color {
  | #Yellow => "banana"
  | #Red => "cranberry"
  | #Green => "avocado"
  }

let color = #Red
let vegetable = drawVegetable(color)
let fruit = drawFruit(color)

Arrays

ReScript uses Arrays by default. Indexing is syntatic sugar for Array.get

open Belt
let arr = [1, 2, 3]
let first: option<int> = arr[0]

In OCaml and JS, Array.get doesn't return an option

// open Js
let arr = [1, 2, 3]
let first: int = arr[0]

List

let l = list{1, 2, 3}

let isEmpty = switch l {
  | list{} => true
  | list{_head, ..._rest} => false
}

More data structures will follow this pattern in the future

Control flow

if something { thenThis } else { thenThat }
if something { thenThis } // else { () }  // implicit

for i in 0 to 10 { Js.log(i) }
for i in 10 downto 0 { Js.log(i) }

while condition { inner_loop() } // See ref example above

Destructuring and Pattern matching

Irrefutable patterns can be destructured

let coordinates = (1, 2, 0)
let (x, y, _) = coordinates

let sum = ((x, y, z)) = x + y + z // even in functions

Pattern matching using switch

switch Some(10) {
| Some(n) when n > 10 => "More than ten" // when guard
| Some(2) | Some(4) => "Two or four" // multiple clauses
| Some(_) => "Not two, four or ten"
| None => "No number given"
}

Pipe (first)

Syntactic sugar for function application

let sum = (a, b, c) => a + b + c
let four = 1->sum(2, 1)
let addTwo = 1->sum(_, 1) // placeholder
let five = addTwo(3)

let someFive = five->Some // Can pipe into variants!!!

|> is deprecated, heavier, pipes last and doesn't work with variants

Exceptions

Exceptional variants

exception Out_of(string)

let chewBubblegum = () =>
  if Js.Math.random() < 0.5 {
    raise(Out_of("bubblegum"))
  } else { "Kick Ass" }

try {
  chewBubblegum()
} catch {
| Out_of(thing) => "I'm all out of " + thing
}

switch chewBubblegum() {
| message => message
| exception Out_of(thing) => "I'm all out of " + thing
}

Misc.

  • JSX
  • First Class Modules / Functors
  • Lazy

Interop Demo

APIs used

Thank You

@shritesh