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.
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
// 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
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.
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
// 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;
};
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! 🤓
Comments
Post a Comment