in , ,

TypeScript vs ReasonML – A Comparison, Hacker News


               A beautiful car           

Yes, adding the types works but it this the best way?

Both TypeScript and ReasonML claim to offer statically typed language for web developers that compiles to JavaScript. But there are important differences. TypeScript’s best and the worst feature is that is a superset of the JavaScript. And while having that familiarity with JavaScript is nice, it means that every quirkiness that we love and hate about JavaScript is still there in TypeScript. The types are added on top of the JavaScript and that works, kind of.

What ReasonML offers is a completely different yet familiar language. Different language means it will be hard to learn for JavaScript / TypeScript developers? Well, let’s take a look, shall we?

Declaring a variable

Let’s start from a variable declaration

(ReasonML) (TypeScript)

In ReasonML we declare a variable with (let) keyword. There’s no const,letis immutable by default.

Both languages ​​inferred type ofain this situation

Functions

TypeScript

letsum=(A:number,  (b) :number)=>aB

ReasonML

letsum=(a,  (b)=>ab;

Although I didn’t write any types by hand, the arguments of this function are typed. Why don’t we have to write types in ReasonML? Because of the powerful type system that allows for incredible type inference. What it means is that compiler can deduce types without your help.( )operator in ReasonML works only with integers – the only type thataandbcan be, so we don’t have to write

But you always can write types, if you want:

ReasonML

letsum=(a:int,b:int)=>ab;

Interfaces, Records

TypeScript

interfaceProduct{  name:string  id:number}

The closest thing to interface in ReasonML is a (record)

ReasonML

typeproduct={  name:string,  id:int,};

Records are like TypeScript objects but are immutable, fixed and a bit more rigidly typed.

Let’s use defined structures in some function

ReasonML

letformatName=product=>"Name:"product.name;

TypeScript

constformatName=(product:Product)=>"Name:"product.name

Again, we don’t need to annotate types! In this function, we have an argumentproductthat has a property (name) of type string. ReasonML compiler can guess the type of that variable based on usage. As the only type that hasnameproperty of type string is (product) compiler will infer it.

Updating records

letupdateName=(product,name)=>{... (product) ,name};
constupdateName=(product:Product,name:string)=>()  {. ..product,name})

ReasonML supports spread operator and punning for name and values ​​just like TypeScript

It is also interesting to look at what javascript was generated by ReasonML

functionupdateName(product,name){  return[name,product[1]]}

Records in ReasonML are represented as arrays. And generated code looks like a human wrote it if humans could remember indices of every property of every type like a compiler does.

Reducer example

This is, in my opinion, is where ReasonML really shines. Let’s compare implementations of the same reducer:

in TypeScript (following this guidehttps://redux.js.org / recipes / usage-with-typescript)

TypeScript

interfaceState{  Movies:string[]}constdefaultState:State={  Movies:[],}exportconstADD_MOVIE="ADD_MOVIE"exportconstREMOVE_MOVIE="REMOVE_MOVIE"exportconstRESET="RESET"interfaceAddMovieAction{  type:typeofADD_MOVIE  payload:string}interfaceRemoveMovieAction{  type:typeofREMOVE_MOVIE  payload:string}interfaceResetAction{  type:typeofRESET}typeActionTypes=AddMovieAction|RemoveMovieAction|ResetActionexportfunctionaddMovie(movie:string):ActionTypes{{    return{    type:ADD_MOVIE,    payload:movie,  }}exportfunctionremoveMovie(movie:string):ActionTypes{{    return{    type:REMOVE_MOVIE,    payload:movie,  }}exportfunctionreset():ActionTypes{  return{    type:RESET,  }}constreducer=(state:State,action:Action)=>{  switch(action.type){    caseADD_MOVIE:      return{movies:[movie,...state.movies]}    caseREMOVE_MOVIE:      return{movies:state.movie.filter(m=>m!==movie)}    caseRESET:      returndefaultState     default:      returnstate   }}

Nothing crazy here, we declared state interface, default state, actions, action creators and finally reducer.

and now the same thing in ReasonML

ReasonML

typestate={  movies:list(string)};typeaction=  |AddMovie(string)  |RemoveMovie(string)  |ResetletdefaultState={movies:[]}letreducer=(state)=>fun  |AddMovie(movie)=>{movies:[movie,...state.movies]}  |RemoveMovie(movie)=>{movies:  (state) .movies|>List.filter(m=>m!==movie)}  |Reset=>defaultState;letsomeAction=AddMovie("The End of Evangelion")

Yep, that is it.

Lets look at what is happening here.

First, there is a state type declaration.

After that is actionVarianttype

typeaction=  |AddMovie(string)  |RemoveMovie(string)  |Reset

What is means that any variable with the typeactioncan have one of the following values: (Reset) ,AddMoviewith some string value andRemoveMoviewith some string value

The Variant is a very powerful feature that allows us to define a type that can have values ​​A or B in a very concise way. Yes, TypeScript has union types but it doesn’t have the same level of integration with language because types were kind of patched to the JavaScript to make TypeScript but Variants are an essential part of the ReasonML language and exists side by side with other language features like pattern matching.

Speaking of pattern matching, let’s look at the reducer.

letreducer=(state)=>fun  |AddMovie(movie)=>{movies:[movie,...state.movies]}  |RemoveMovie(movie)=>{movies:  (state) .movies|>List.filter(m=>m!==movie)}  |Reset=>defaultState;

What we see here is a function that accepts the state as a first argument and then matches the second argument to possible values.

We can also write this function like this:

letreducer=(state,action)=>{  switch(action){  |AddMovie(movie)=>{movies:[movie,...state.movies]}  |RemoveMovie(movie)=>{movies:  (state) .movies|>List.filter(m=>m!==movie)}  |Reset=>defaultState;  }}

As matching an argument is a common pattern in ReasonML this kind of functions are written in a shorter format like in the previous code snippet. Parts like this –{movies: [movie, ...state.movies]}look the same as in TypeScript, but what is happening here is not the same! In ReasonML[1,2,3]is not an array but an immutable list. Imagine it as havingImmutable.jsbuilt-in into the language itself. In this part, we are taking advantage of the fact that the append operation has constant time in ReasonML lists! Coming from JavaScript or TypeScript you might write something like this without much thought and you’re getting performance improvements for free.

Now let’s look at the experience of adding a new action to this reducer. How does that look like in TypeScript? Firstly, you add a new action to type definition then write some boilerplate in form of action creators and yeah don’t forget to actually handle that case in the reducer which is who knows where.

In ReasonML first step is exactly the same but the next steps are anything but. As you hit save after adding a new action to the type definition you are led by the compiler on a gentle stroll across your codebase to handle cases accordingly

You’ll see a warning like this:

Warning 8: this pattern-matching is not exhaustive. Here is an example of a case that is not matched: Sort

Now, this is good developer experience. It will point you to the exact place where you need to handle new action and will also tell you exactly which case is missing.

Null, undefined vs Option

In TypeScript we are burdened with JavaScript legacy of having bothnulland (undefined) for representing almost the same thing – an absence of value.

(In ReasonML there’s no such thing, there’s only (Option) type.

ReasonML

typeoption(''a)=  |Some(''a)  |None;

This is an already familiar variant type. But it also has a type parameter'a. This is similar to generics in other languages ​​likeOption

Let’s compare more code

TypeScript

interfaceUser{  phone?:number}interfaceForm{  user?:User}functiongetPhone(form:Form [3/3]) (*************************************:  number|undefined{  if(form.user===undefined){    returnundefined   }  if(form.user.phone===undefined){    returnundefined   }  returnform.user.phone}

Accessing nullable properties is one of the simplest cases where you can shoot in your foot. In TypeScript, we can remedy it by enabling strict null checks and then checking manually for undefined values.

ReasonML

openBelt.Option;typeuser={  phone:option(int)};typeform={  user:option(user)};letgetPhone=form=>  form.user->flatMap(u=>u.phone);

In ReasonML we can use built-in (option) type and together with helper functions from Belt standard library we can handle possibly empty values ​​in standardized way

Labeled arguments

I don’t think that anyone will argue that the labeled arguments feature is just plain awesome. Probably everyone has had to look up the order or the meaning of the function’s arguments at some point. Unfortunately, there are no labeled arguments in TypeScript.

(TypeScript)

functionmakeShadow(x:number,y:number,spread:number,color:string){  return0}constshadow=makeShadow(10,[12/12]  10,5,"black")

In ReasonML you put a~character before argument’s name and it becomes labeled.

Reas onML

letmakeShadow=(~x:int,~Y:int,~spread:int,~~color:string)=>{  0;}letshadow=makeShadow(~spread=5,~x=10,~y=10,~color="black")

And yes, you can try and emulate it with an object as an argument in TypeScript, but then you’re allocating an object at every function call: /

TypeScript

functionmakeShadow()  ARGS:{  x:number  y:number  spread:number  color:number}){  return0}constshadow=makeShadow({x:10,y:10,spread:(5),color:"black"})

Module system

In TypeScript we explicitly export and import anything from file to file.

Hello .ts

exportconsttest="Hello"
import{test}from"./ Hello.ts"console.log(test)

In ReasonMLevery file is a modulewith the name of that file.

Hello.re

You canopenmodules which makes content available without module name prefix

open;Js.log(test);

Compiling speed

To compare compile speed let’s compile TodoMVC, as implementation and therefore project size is roughly comparable. What we are measuring is the time it takes to transpile code to JavaScript. No bundling, minifying, etc. [1]

TypeScript React.js

$ time tsc -p js TSC-P JS 6. 18 s user 0. 24 s system 115% CPU 5. 572 total

6. 18seconds

ReasonML ReasonReact

$ bsb -clean-world $ time bsb -make-world Building src / ReactDOMRe.mlast.d Building src / ReactDOMRe.cmj Building src / Fetch.mlast.d [3/3] Building src / bs_fetch.cmj [12/12] Building src / Json_encode.mlast.d Building src / Json.cmj [7/7] Building src / todomvc / App.mlast.d [3/3] Building src / todomvc / App-ReasonReactExample.cmj bsb -make-world 0. 96 s user 0. 73 s system 161% cpu 1.  (total) ***************************

(0.) (seconds)

Now this also includes compiling ReasonML dependencies, we can measure compiling time of only project files

ReasonML ReasonReact, only src /

$ bsb -clean $ time bsb -make-world ninja: no work to do. ninja: no work to do. ninja: no work to do. [7/7] Building src / todomvc / App.mlast.d [3/3] Building src / todomvc / App-ReasonReactExample.cmj bsb -make-world 0.  s user 0. 27 s system 117% CPU 0. 512 total

0. 33seconds

Now that is fast!

BuckleScript considers performance at install time, build time and run time as a serious feature

This is the quote taken from the BuckleScript documentation. BuckleScript is the tool that transpiles ReasonML to JavaScript.

Where TypeScript beats ReasonML (for now)

Not everything about ReasonML is flowers and butterflies. It is a fairly new language… Well, actually no it is based on OCaml which is fairly old but the point is that there are still not a lot of resources online. Googling TypeScript question will more likely to yield an answer than a ReasonML one.

DefinitelyTyped typings amount is just insane and it will take time for ReasonML to match them.

Closing remarks

ReasonML syntax should be really familiar for front-end developers which means that the beginning of the learning curve is not that steep (But still steeper than TypeScript). ReasonML takes the best things from the other languages ​​and tools like Immutable.js, eslint and brings it to the language level. It doesn’t try to be a completely pure programming language, you can always fallback to mutation and imperative programming when you need it. It is crazy fast which is a big part of great developer experience. ReasonML is everything TypeScript tries to be (and a bit more) without all that JavaScript weirdness. You should try it!


[1] Compile time tests performed on a Mid – 2015 MacBook Pro with Intel (R) Core (TM) i5 – 5287 U CPU @ 2. (GHz)TypeScript React.js source code
ReasonML ReasonReact source code

Brave Browser

Payeer

Read More

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

GIPHY App Key not set. Please check settings

Dow Soars But Federal Reserve Descends Into Civil War – CCN Markets, Crypto Coins News

Dow Soars But Federal Reserve Descends Into Civil War – CCN Markets, Crypto Coins News

Google accused of secretly feeding personal data to advertisers, Hacker News

Google accused of secretly feeding personal data to advertisers, Hacker News