Docs / BuckleScript / NewBucklescriptSyntax

New BuckleScript Syntax

BuckleScript 8.1 comes with a brand new syntax parser and printer that allows for much better syntax error messages and other benefits we will talk about soon.

More details about the new syntax can be found in this early announcement blog post.

This document gives an overview over the most relevant features until more details / docs are revealed.

Note: In case you are looking for the current Reason language documentation (v3.6), you can find it here.

What is Different to Reason v3.6?

  • Complete removal of semicolon (you can still write them).

  • No need for parentheses around if, switch and try.

  • Type arguments: from option(int) to option<int>.

  • Interpolated string: from {j|hello ${name}|j} to j`hello ${name}` (8.1.1 update: now it also works without j, as the type-safe version). The string now has proper unicode support!

  • Polymorphic variants: from `red to #red.

  • Arrays: from [|1,2,3|] to [1,2,3]. In JS, arrays are the right default.

  • Lists: from [1,2,3] to list[1,2,3] (8.1.1 update: now it is list{1, 2, 3}). This ties with upcoming plans to access containers in a uniform way: set[...] and map[...]. Maybe temporary.

  • Exception: from try (compute()) { | Not_found => Js.log("oops")} to try compute() catch { | Not_found => Js.log("oops")}.

  • First class module: from (module S: Student) to module(S: Student).

  • No custom infix operator for now (including mod).

  • Object access: from settings##visible #= true to settings["visible"] = true. Rejoice!

  • Object: from Js.t({"age": int}) to just {"age": int}. The Js.t part is now implicit.

  • Attribute: from [@bs.deriving accessors] to @bs.deriving(accessors). From [%re bla] to %re(bla).

  • Removed dereference syntax result^. Just use result.contents.

  • `hello world` for multiline strings. "hello world" string is now singleline.

  • fun pattern matching syntax removed.

  • Type declaration is non-recursive by default, consistent with let bindings. To have recursive types, use type rec myList<'a> = Nil | Cons('a, myList<'a>).

  • Use any words, including reserved keywords, as your identifier name: let \"try" = true.

How to Use the New Syntax

Install bs-platform@8.1 and create a file with the .res or .resi file extension to enable the new syntax parser (no further configuration required).

Quick Example

RES
if hasEaten { Js.log("more dessert please") } else { Js.log("dessert please") } let message = j`Hello ${userName->Js.String.toUpperCase}!` type student<'extraInfo> = { name: string, age: int, otherInfo: 'extraInfo, } @bs.val external window: {..} = "window" window["addEventListener"]("focus", onFocus)

Comparison to JS

Let's have a look on how different JS constructs look in our new syntax:

Comments

JavaScriptUs
/* Comment */Same
// Line commentSame

Variable

JavaScriptUs
const x = 5;let x = 5
var x = y;No equivalent (thankfully)
let x = 5; x = x + 1;let x = ref(5); x := x.contents + 1

String & Character

JavaScriptUs
"Hello world!"Same
'Hello world!'Strings must use "
"hello " + "world""hello " ++ "world"
`hello world`Same

Boolean

JavaScriptUs
true, falseSame
!trueSame
||, &&, <=, >=, <, >Same
a === b, a !== bSame
No deep equality (recursive compare)a == b, a != b
a == bNo equality with implicit casting (thankfully)

Number

JavaScriptUs
3Same *
3.1415Same
3 + 4Same
3.0 + 4.53.0 +. 4.5
5 % 3mod(5, 3)

* JS has no distinction between integer and float.

Object/Record

JavaScriptUs
no typestype point = {x: int, mutable y: int}
{x: 30, y: 20}Same
point.xSame
point.y = 30;Same
{...point, x: 30}Same

Array

JavaScriptUs
[1, 2, 3]Same
myArray[1] = 10Same
[1, "Bob", true](1, "Bob", true) *

* Heterogenous arrays in JS are disallowed for us. Use tuple instead.

Null

JavaScriptUs
null, undefinedNone *

* Again, only a spiritual equivalent; we don't have nulls, nor null bugs! But we do have an option type for when you actually need nullability.

Function

JavaScriptUs
arg => retValSame
function named(arg) {...}let named = (arg) => {...}
const f = function(arg) {...}let f = (arg) => {...}
add(4, add(5, 6))Same

Blocks

JavaScriptUs
const myFun = (x, y) => { const doubleX = x + x; const doubleY = y + y; return doubleX + doubleY };
let myFun = (x, y) => { let doubleX = x + x let doubleY = y + y doubleX + doubleY }

If-else

JavaScriptUs
if (a) {b} else {c}if a {b} else {c}
a ? b : cSame
switchswitch but super-powered pattern matching!

* Our conditionals are always expressions! You can write let result = if a {"hello"} else {"bye"}

Destructuring

JavaScriptUs
const {a, b} = datalet {a, b} = data
const [a, b] = datalet [a, b] = data *
const {a: aa, b: bb} = datalet {a: aa, b: bb} = data

* Gives good compiler warning that data might not be of length 2.

Loop

JavaScriptUs
for (let i = 0; i <= 10; i++) {...}for i in 0 to 10 {...}
for (let i = 10; i >= 0; i--) {...}for i in 10 downto 0 {...}
while (true) {...}while true {...}

JSX

JavaScriptUs
<Foo bar=1 baz="hi" onClick={bla} />Same
<Foo bar=bar /><Foo bar /> *
<input checked /><input checked=true />
No children spread<Foo>...children</Foo>

* Argument punning!

Exception

JavaScriptUs
throw new SomeError(...)raise(SomeError(...))
try {a} catch (Err) {...} finally {...}try a catch { | Err => ...} *

* No finally.

Blocks

The last expression of a block delimited by {} implicitly returns (including function body). In JavaScript, this can only be simulated via an immediately-invoked function expression (since function bodies have their own local scope).

JavaScriptUs
let result = (function() { const x = 23; const y = 34; return x + y; })();
let result = { let x = 23 let y = 34 x + y }