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,switchandtry.Type arguments: from
option(int)tooption<int>.Interpolated string: from
{j|hello ${name}|j}toj`hello ${name}`.Polymorphic variants: from
`redto#red.Arrays: from
[|1,2,3|]to[1,2,3]. In JS, arrays are the right default.Lists: from
[1,2,3]tolist[1,2,3]. This ties with upcoming plans to access containers in a uniform way:set[...]andmap[...]. Maybe temporary.Exception: from
try (compute()) { | Not_found => Js.log("oops")}totry compute() catch { | Not_found => Js.log("oops")}.First class module: from
(module S: Student)tomodule(S: Student).No custom infix operator for now (including
mod).Object access: from
settings##visible #= truetosettings["visible"] = true. Rejoice!Object: from
Js.t({"age": int})to just{"age": int}. TheJs.tpart is now implicit.Attribute: from
[@bs.deriving accessors]to@bs.deriving(accessors). From[%re bla]to%re(bla).Removed dereference syntax
result^. Just useresult.contents.`hello world`for multiline strings."hello world"string is now singleline.funpattern 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
RESif 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
| JavaScript | Us |
|---|---|
/* Comment */ | Same |
// Line comment | Same |
Variable
| JavaScript | Us |
|---|---|
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
| JavaScript | Us |
|---|---|
"Hello world!" | Same |
'Hello world!' | Strings must use " |
"hello " + "world" | "hello " ++ "world" |
`hello world` | Same |
Boolean
| JavaScript | Us |
|---|---|
true, false | Same |
!true | Same |
||, &&, <=, >=, <, > | Same |
a === b, a !== b | Same |
| No deep equality (recursive compare) | a == b, a != b |
a == b | No equality with implicit casting (thankfully) |
Number
| JavaScript | Us |
|---|---|
3 | Same * |
3.1415 | Same |
3 + 4 | Same |
3.0 + 4.5 | 3.0 +. 4.5 |
5 % 3 | mod(5, 3) |
* JS has no distinction between integer and float.
Object/Record
| JavaScript | Us |
|---|---|
| no types | type point = {x: int, mutable y: int} |
{x: 30, y: 20} | Same |
point.x | Same |
point.y = 30; | Same |
{...point, x: 30} | Same |
Array
| JavaScript | Us |
|---|---|
[1, 2, 3] | Same |
myArray[1] = 10 | Same |
[1, "Bob", true] | (1, "Bob", true) * |
* Heterogenous arrays in JS are disallowed for us. Use tuple instead.
Null
| JavaScript | Us |
|---|---|
null, undefined | None * |
* 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
| JavaScript | Us |
|---|---|
arg => retVal | Same |
function named(arg) {...} | let named = (arg) => {...} |
const f = function(arg) {...} | let f = (arg) => {...} |
add(4, add(5, 6)) | Same |
Blocks
| JavaScript | Us |
|---|---|
|
|
If-else
| JavaScript | Us |
|---|---|
if (a) {b} else {c} | if a {b} else {c} |
a ? b : c | Same |
switch | switch but super-powered pattern matching! |
* Our conditionals are always expressions! You can write let result = if a {"hello"} else {"bye"}
Destructuring
| JavaScript | Us |
|---|---|
const {a, b} = data | let {a, b} = data |
const [a, b] = data | let [a, b] = data * |
const {a: aa, b: bb} = data | let {a: aa, b: bb} = data |
* Gives good compiler warning that data might not be of length 2.
Loop
| JavaScript | Us |
|---|---|
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
| JavaScript | Us |
|---|---|
<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
| JavaScript | Us |
|---|---|
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).
| JavaScript | Us |
|---|---|
|
|