If Rust is "C++ in ML clothing" then ReScript is "JavaScript in ML clothing".
What is ReScript?
ReScript is "Fast, Simple, Fully Typed JavaScript from the Future". What that means is that ReScript has a lightning fast compiler, an easy to learn JS like syntax, strong static types, with amazing features like pattern matching and variant types. Until 2020 it was called "BuckleScript" and is closely related to ReasonML.
ReScript is growing and adding features to make it more appealing as an alternative to JavaScript. ReScript v11 was recently released and adds some very nice quality of life improvements.
Let's take a quick look at a few features that Rust and ReScript share with some code examples.
Types
Rust and ReScript are both strong, statically-typed languages with roots in OCaml. Everything has a type and that type is guaranteed to be correct. Unlike TypeScript you won't find the any
type in ReScript.
ReScript relies heavily on type inference which helps remove some clutter from type annotations.
// Typescript
let add = (a, b) => a + b // infers (any, any) => any
// ReScript
let add = (a, b) => a + b // infers (int, int) => int
You can add type annotations if you would like.
// ReScript
let add = (a: int, b: int): int => a + b
Expression based
Rust and ReScript are expression based languages, which means most lines of code or blocks of code return a value. You also don't have to write an explicit return for an expression, the last value in a block is the value returned.
// rust
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {y}"); // the value of y is: 4
}
// rescript
let main = () => {
let y = {
let x = 3
x + 1
}
Console.log(`The value of y is: ${y->Int.toString}`) // the value of y is: 4
}
Pattern Matching
Pattern matching is a powerful way to do something conditionally based on types or data. You can think of it as a super powered switch
statement that can match on the type or the value of the data. Rust and ReScript will give you a compiler error if you fail to handle all possible cases, which makes refactoring much easier to handle. If you ever have to add a new case you'll know exactly where to go in the code to make sure you handle it correctly.
// Rust
fn main() {
let n = 42;
match n {
10 => println!("The number ten."),
42 => println!("Answer to the Ultimate Question of Life, the Universe, and Everything."),
_ => println!("Some other number."),
}
}
// rescript
let main = () => {
let x = 42
switch x {
| 10 => Console.log("The number ten.")
| 42 =>
Console.log(
"Answer to the Ultimate Question of Life, the Universe, and Everything.",
)
| _ => Console.log("Some other number.")
}
}
Tagged Unions and Variant types
Rust's tagged unions and ReScript variant types give you a way to not only create a type, but to also give that type data. This can be combined with pattern matching to make sure you handle all possible cases.
This article I wrote shows how you can use ReScript's variant types to connect business logic to the compiler.
// Rust
enum Widget {
Red(u8),
Blue(u8),
None,
}
fn main() {
let x = Widget::Red(42);
match x {
Widget::Red(_) => println!("You have a red widget!"),
Widget::Blue(_) => println!("You have a blue widget!"),
Widget::None => println!("You have no widget!"),
}
}
// ReScript
type widget =
| Red(int)
| Blue(int)
| None
let main = () => {
let x = Red(42)
switch x {
| Red(_) => Console.log("You have a red widget!")
| Blue(_) => Console.log("You have a blue widget!")
| None => Console.log("You have no widget!")
}
}
Option
instead of undefined
or null
Not having to deal with null or undefined errors saves so much time and prevents errors and headaches in production. Option
forces you to explicitly deal with anything that might not be defined and having it typed this way makes it clear when you expect something to be undefined.
// Rust
fn main() {
let n = Some("Josh");
match n {
Some(name) => println!("Hello there {:?}!", name),
None => println!("Who are you?")
}
}
// rescript
let main = () => {
let n = Some("Josh")
switch n {
| Some(name) => Console.log(`Hello there ${name}!`)
| None => Console.log("Who are you?")
}
}
Result
instead of throwing errors
Both languages have the Result
type which can represent a response from an expression that might have failed. It's useful to handle expected errors without blowing up the program. Expected errors could be things like an api timing out, or a 400
response for a bad request. It allows the program to track information about an error and handle it gracefully without throwing an exception.
// rust
fn main() {
let n: Result<&str, &str> = Ok("Josh");
match n {
Ok(name) => println!("Hello there {:?}!", name),
Err(err) => println!("Error: {:?}", err),
}
}
// rescript
let main = () => {
let n: result<string, string> = Ok("Josh")
switch n {
| Ok(name) => Console.log(`Hello there ${name}`)
| Error(err) => Console.log(`Error: ${err}`)
}
}
Learn more about ReScript
If you are interested in learning more about ReScript check out the official site or come over to the forum!
Top comments (10)
Great write up!
It's nice to see BuckleScript (ReScript) get some love.
I've always been a fan of OCaml but even more than Haskell community the OCaml'ers seem to have adopted the maxim "avoid 'success at all costs'!" which has led to an awesome if sadly very niche language. ReScript taking the excellent ideas of OCaml to JS seems like a real step forward.
It's an amazing language that solves so many issues with JS while not being as difficult to set up and use as others like Elm, F# Fable, or PureScript. It uses npm and all the normal JS tooling, and it has the easiest interop with JS. It's a great mix of strong, sound types while not getting on the way of getting shit done.
Love the title and the execution. Been thinking in a similar vein when I heard popular dev influencers talking about Rust and it's various language features.
Keep it up dude
I assume that "ML" does not refer to Machine Learning in this context. But I can't seem to track down what it stands for. The ReasonML website also seems to not define what is meant by "ML". What does ML mean here?
"ML" stands for "Meta Language".
en.wikipedia.org/wiki/ML_(programm...
You're not the first person to be confused by that :)
Thanks!
I like the syntax of both Rust and ReScript, thanks. I want to learn Next.js, you think ReScript is a good choice to match with?
I don't see why not! The official Rescript site uses Next and is written in ReScript.
github.com/rescript-association/re...
Thanks... I has been reading about Rust too, is so interestng.