Immutably updating JavaScript objects in ReasonML (BuckleScript)

Yawar Amin - Nov 8 '19 - - Dev Community

BUCKLESCRIPT provides a type-safe OCaml programming environment that allows us to output and deploy JavaScript. ReasonML provides some syntax sugar to make working with JavaScript objects a bit nicer. However, it doesn't sugar over everything we can do with objects in JavaScript, like object spread syntax.

In JavaScript, you can use object spread syntax to immutably update objects:

const bob = {id: 1, name: "Bob", age: 34};
const bob2 = {...bob, age: 35};
Enter fullscreen mode Exit fullscreen mode

In Reason, there is no object spread syntax, but you can do it manually:

let bob = {"id": 1, "name": "Bob", "age": 34};
let bob2 = {"id": bob##id, "name": bob##name, "age": 35};
Enter fullscreen mode Exit fullscreen mode

But this is quite manual. There must be a more elegant way!

Well–in JavaScript before the advent of object spread, people used the Object.assign method. And BuckleScript ships with a binding Js.Obj.assign, to exactly that method. This binding is effectively just a normal function from the Reason/OCaml point of view. So we can build a convenient, functional-style 'object spread' on top of it!

Here's the update function:

// JsObj.re

/** [update(~props, obj)] returns a shallow copy of [obj] updated with
    [props]. */
let update = (~props, obj) =>
  Js.Obj.(()->empty->assign(obj)->assign(props));
Enter fullscreen mode Exit fullscreen mode

This effectively does an immutable update of obj with the given props. The input obj is unchanged. Perfect for React state updates, for example! Here's an example usage:

let bob = {"id": 1, "name": "Bob", "age": 34};
let bob2 = update(~props={"age": 35}, bob);

Js.log2("bob:", bob);
Js.log2("bob2:", bob2);
Enter fullscreen mode Exit fullscreen mode

If you're familiar with JavaScript's Object.assign, you probably already understand how this works. If not, short explanation: it allocates a new, empty object, then shallow-copies all the props from obj into the new object, then sets the new props on the new object–leaving the old obj alone. The new props are also provided in the form of a normal object.

So, does this cover all the use cases of JavaScript's object spread? Probably not! But it's a demonstration of how Reason can have quite elegant, functional-style equivalents to JavaScript idioms.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .