Mutation

ReScript has great traditional imperative & mutative programming capabilities. You should use these features sparingly, but sometimes they allow your code to be more performant and written in a more familiar pattern.

Mutate Let-binding

Let-bindings are immutable, but you can wrap it with a ref, exposed as a record with a single mutable field in the standard library:

ReScriptJS Output
let myValue = ref(5)

Usage

You can get the actual value of a ref box through accessing its contents field:

ReScriptJS Output
let five = myValue.contents // 5

Assign a new value to myValue like so:

ReScriptJS Output
myValue.contents = 6

We provide a syntax sugar for this:

ReScriptJS Output
myValue := 6

Note that the previous binding five stays 5, since it got the underlying item on the ref box, not the ref itself.

Note: you might see in the JS output tabs above that ref allocates an object. Worry not; local, non-exported refs allocations are optimized away.

Tip & Tricks

Before reaching for ref, know that you can achieve lightweight, local "mutations" through overriding let bindings.

Self-Referencing Callbacks

Sometimes a callback needs a value returned by the function that registers it. For example, suppose subscribe registers a message callback and returns a function that unsubscribes it. To unsubscribe after the first message, create a ref with a no-op function, then assign the returned function to it:

RES
@val external subscribe: (string => unit) => unit => unit = "subscribe" let unsubscribe = ref(() => ()) unsubscribe := subscribe(message => { Console.log(message) unsubscribe.contents() })

The callback reads unsubscribe.contents when a message arrives, so it sees the function assigned after subscribe returns.

This assumes subscribe delivers messages after it returns. A callback invoked during subscription would still call the initial no-op function.