Tales of a Sardinian software engineer - Functional Friday Episode 1 - Discovering Rescript - Learn how to define types and let bindings

Functional Friday - Episode 1

Discovering Rescript - Types and Let Binding


Welcome to this first episode of Functional Friday! This series is meant to be a journey of ReScript discovery and what it looks like for a JavaScript/TypeScript developer.
If you're relatively new to functional programming, don't worry: I've been there. Functional programming in its purest form has its roots set in mathematics and some concepts may be hard to grasp. That's why I'll use a more pragmatic approach to it and provide comparison with imperative, unfunctional code whenever it's needed.

But first, let's see how this journey started.


1.1 Me and TypeScript 💔

In the last couple of years I tried to fall in love with TypeScript, I promise I really did. But unfortunately the more I use the language, the more I realize that it didn't really solved that much for me.
Having type checking is a great feature indeed and allows you to catch pitfalls in your code before hitting production and also decreases the test cases. Things preventing me from being fully sold on TypeScript are:

  • unsound type system, which means that it will allow code that violates the static types at runtime;
  • inconsistent inference behavior, leading to continuous head-scratcher and negotiations. In the worst case scenario your code will not even transpile because of wrong inference;
  • sometimes you need a lot of annotation, which may also become really hard to understand really quickly;
  • narrowing not working as intended/expected, especially when paired with switch statement;
  • not all libraries are compatible with TS, and a good amount of third-party definition files are incomplete or even flat out wrong. This is even worse when a library gets updated and its types don't.

Despite all of these things, I'm still using TypeScript. I started programming using statically typed languages as C and Java. My first experience with dynamically typed languages was with PHP and, subsequently, with JavaScript. Just as a reference, we're talking about more than 12 years ago (MooTools anyone?).

The thing I like the most about TypeScript is, well... types. Good typing prevents a lot of headache. Also, I think that TypeScript offers one of the best (if not the best) IDE developer experience, especially when using a well supported editor such Visual Studio Code.
In my opinion this level of integration is what makes TypeScript so pleasant to work with and a lot of plugin developers should have a look at it and take notes.


1.2 Breaking the habit and look outside 🗺

After more than 3 years spent working with TypeScript on a fairly big project at Treatwell, I get used to it. May not be perfect but that's what we have; it helps us preventing bugs and it's also fairly popular. Or at least that was what I thought.

If we have a look to the Google Trends report for the last 3 years, it should be immediately noticeable how TypeScript is still a niche compared to JavaScript, even more in the last year.

This, paired with the poor support to functional programming (no real immutable data set, no native optional type, no pipelining) and a minimal advantage on detectable bugs [1], made me wonder about alternatives. And this is how I discovered about ReScript.

JavaScript vs TypeScript vs ReScript - Google Trends from January, 2020 to January, 2023

Before going on, I want to clarify this: I'm not arguing against using TypeScript, by any mean. If it is working for you and you're really aware of what the pros/cons are and you're fine with it, then great. What I'm personally against is blind adoption or following a trend just because someone else said so.

At the end of the day, as developers, I personally believe we should always try to make decisions around our codebase considering our needs, while also evaluating the costs and the benefits of technologies we choose to adopt.


1.3 Discovering ReScript 👀

If you ended up here, chances are you already know ReScript. But for those of you who never heard about it

"ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript. It comes with a lightning fast compiler toolchain that scales to any codebase size." [2]

Now, aside from the catchy line (we all need a little bit of marketing in our lives), there are some key elements that made ReScript really appealing to me:

  • it compiles to JavaScript (which is still the most used programming language 🥲) [3];
  • it has a very robust type system;
  • it requires little to no type annotation;
  • it offers bindings for React out of the box (via @rescript/react module) which seems really handy for Frontend development;
  • it looks relatively easy to integrate into an existing codebase since it does not only compile to JavaScript, but also offers TypeScript interoperability via genType, allowing us to use ReScript types and values in TypeScript and vice versa.

At this point in time, I've just started my journey and I honestly can't tell how this will end. The goal of these articles is learning together a new language mostly by practice, see how it compares against the de facto industry standard and eventually if it may be considered production ready.
All of that being said, let's take our first step in ReScript.

1.3.1 Let bindings

In programming, binding refers to the action of assigning a value to an accessor, generally known as a named variable. I know, sounds pretty mouthful so let's start with some familiar JavaScript code

JS
// using let
let meaningOfLife = 42;

//... or using const
const meaningOfLife = 42;

//... or with the old friend var (please don't)
var meaningOfLife = 42;

where all of the above examples are bindings. JavaScript (and TypeScript too) makes things harder than needed in this regard, since we have three ways to define a variable. In Rescript, you just do

RES
let meaningOfLife = 42

but there're few important differences compared to JavaScript/TypeScript to keep in mind:

  • bindings are immutable, unless you explicitly request otherwise. Once assigned, they cannot change;
  • bindings can be redeclared in the same scope. This technique is known as shadowing.
RES
let meaningOfLife = 42

// This will cause an error an compile time. Immutable, remember? 😉
meaningOfLife = 422

// This will re-bind our variable. From now on, it is a string.
let meaningOfLife = "42" 

1.3.2 Types definition

I'm firmly convinced that in both dynamically and statically typed languages, understanding how types work in terms of declaration, inference and coercion (if any) is crucial and it shouldn't be overlooked. A good number of issues in a JavaScript codebase comes from developers not having a proper understanding of how these things work resulting in their code generating... bananas 🍌 [4].
In TypeScript, you've object types and you can declare a new one as it follows

TS
// a Person object type using an interface
interface Person {
  name: string;
  surname: string;
};

// a Person object type using a type
type Person = {
  name: string;
  surname: string;
};
and in ReScript, declaring a new type is really similar
RES
type person = {
  name: string,
  surname: string
}

with the main difference being the usage of commas instead of semicolon and one dedicated keyword instead of two. Also, every type name we declare should start with a lower case letter or you will get a fancy error at compile time. Neat.


1.4 Let's wrap it up! 🫡

If you reached this point, I sincerely want to thank you for reading through the whole article. If you're interested in knowing more and dive a little bit deeper into the concepts we've seen so far, I highly suggest you to read both types and let bindings documentation. I intentionally kept it short, since these two arguments will recur a lot in the future, we're just scratching the surface for now.

I really hope you enjoyed the content and you'll stick around for the next episode. Up to then, happy coding and... see you next time!
Cheers! 🤓

References

[1] A study done during 2022 over 600 GitHub repositories shown how typings can help preventing ~15% of bugs. While significant, this means that most bugs cannot be addressed nor solved just with static type checking.

[2] Source: ReScript Website

[3] According to Octoverse 2022

[4] Refers to ('b' + 'a' + + 'a' + 'a').toLowerCase() returning "banana" in JavaScript. Even if it may sound weird at first, the result is totally expected since the second occurrence of + 'a' it's a unary operator which will results in a number coercion and it will be evaluated as NaN (Not a Number) then converted to a string and subsequently concatenated to the remaining letters.

Comments

Popular posts from this blog