To mutate, or immutate, that is the question
Tags: featuredposts
Published: 2019-07-08 20:18:30

---
title: To mutate, or immutate, that is the question
published: true
tags: Featured Posts
canonical_url: https://blog.logrocket.com/to-mutate-or-immutate/
---

![](https://thepracticaldev.s3.amazonaws.com/i/urfo9rqt2uix7qmf3ytc.png)

This is the second post in the **not all compilers are created equal** series, in which we compare _TypeScript_ (TS) and _PureScript (PS)_, two statically typed languages that compile to _JavaScript (JS)_. In the [first post](https://dev.to/bnevilleoneill/typescript-vs-purescript-not-all-compilers-are-created-equal-12i6-temp-slug-4466128), we introduced both languages and explained their reason to be. We also talked about how having restrictions provide us certain guarantees that increase our confidence in the code. The first restriction we talked about was _purity,_ in this post we are going to address another one: _Immutability_.

The world is always changing, and if a software application is trying to represent some aspect of the real world, it will have to have mutable state. But the fact that somewhere in the application we need to model values that change, doesn’t mean that all values in the program should be allowed to change. And I’d argue that adding a restriction to work with immutable data gives you the guarantee that no value will change unexpectedly.

[![](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2019/06/minimal-rocket-2xA-1.png?ssl=1)](https://logrocket.com/signup/)

## Mutable by default

Both _JavaScript_ and _TypeScript_ are mutable by default. We can use some of the language features to avoid accidental mutation, but we need to pay attention to some details.

![](https://paper-attachments.dropbox.com/s_3FA8B4DB81C1702262F976E2E8FE755602BB0FE3E5BC08FB0DE427D74C5BB95C_1557523100642_Screen+Shot+2019-05-10+at+18.17.48.png)

It’s worth mentioning that the only _TypeScript_ specific syntax is the type definition in line 26 and the `as XXX` in lines 28 and 32, the rest is plain old _JavaScript_ that is validated by the compiler.

Being able to mark a value as read-only is really helpful when working with libraries such as [Redux](https://redux.js.org/) that relies on the reducers being immutable to properly work. Overlooking a situation where we mutate some data inadvertently is so common in plain _JavaScript_ that there is a [documentation page](https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#common-mistake-2-only-making-a-shallow-copy-of-one-level) explaining the common mistakes and some patterns to correctly work with immutable data structures. By using _TypeScript’s_ `Readonly`_,_ `ReadonlyArray` _and the_ [new const assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html) (released in version 3.4), we can free up our mind from that burden and put the weight in the compiler, trusting that it will lead us to more accurate code.

But Redux isn’t the only place where we can benefit from using immutable values. It is said that if you are not ashamed of the code you wrote X years ago, you haven’t learned anything in those X years( and I’ve learned a lot from an unfinished project that I wrote 6 years ago. ![😅](https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f605.png)

It was a tool called [mddoc](https://github.com/hrajchert/mddoc) that was meant to sync up documentation and code, read files that had references to other files, extract information and create a web page customized by plugins. One of the biggest problems was the communication between different steps of the program. I chose to use a shared mutable object that I called `Metadata`.

Without tools like _TypeScript_, it was very complicated to understand the structure of this object, which had several nested properties, not all relevant, some with cryptic names and some that were assigned at later times from different parts of the code.

Having the simplicity to just share an object and mutate it freely was really helpful at the beginning but as the project grew it became very hard to manage. Eventually, I abandoned it, except for a small sprint a couple of months ago, whereby migrating the project to _TypeScript_ I resolved the pain of not knowing the structure of the `Metadata` object and [shed some light](https://github.com/hrajchert/mddoc/blob/50d148519e3465c822ce6fdfa67f98386c249265/src/metadata-manager.ts#L113) into [some of the problems](https://github.com/hrajchert/mddoc/blob/50d148519e3465c822ce6fdfa67f98386c249265/src/metadata-manager.ts#L35) that made the project [hard to maintain](https://github.com/hrajchert/mddoc/blob/50d148519e3465c822ce6fdfa67f98386c249265/src/markdown-parser/markdown-file-reader.ts#L38).

## Immutable by default

_PureScript_ is immutable by default, which makes it a great fit to work with pure functions. Similar to the `const` keyword, once we set a value to an identifier, we can’t change it.

```jsx
nine :: Int
nine = 9

-- Compiler error: The value nine has been defined multiple times
nine = 8 
```

A nice side effect is that the syntax is clearer because we don’t need to distinguish between `let` and `const`. Even more, there is no syntax to change the value of a property once it’s defined. What we can do is create a new object from the old one, by simply defining the properties we want to change. This pattern is so common that there is a syntax feature called [record updates](https://github.com/purescript/documentation/blob/master/language/Syntax.md#record-updates) to help us express this in a clean way.

```jsx
type Request = { url :: String, verb :: String }

-- We define a full object
defaultReq :: Request
defaultReq = { url: "", verb: "GET"}

-- And then use it to create a new one, with a changed property
googleReq :: Request
googleReq = defaultReq { url = "http://www.google.com" }
--          { url: "http://www.google.com", verb: "GET" }
```

If we do want to work with mutable data, one option is to use the [ST](https://pursuit.purescript.org/packages/purescript-st/4.0.0/docs/Control.Monad.ST) and [STRef](https://pursuit.purescript.org/packages/purescript-st/4.0.0/docs/Control.Monad.ST.Ref) types. As the documentation states, these types allow us to create computations with local mutation, i.e. mutation which does not “escape” into the surrounding computation. We can compose and extend the computation and eventually run it in order to get a value. Once we run it, we lose the reference to the mutable data, hence the “safe mutation”.

The following is a contrived example to show how we can use those types to represent mutable data and their _TypeScript_ counterpart. The `mutable1` example creates a reference to a new mutable object with an initial value of 0, then reads that value into the `val` identifier and writes the result of the expression `(val + 1)` into the mutable object. The `mutable2` example uses [modify](https://pursuit.purescript.org/packages/purescript-arrays/5.3.0/docs/Data.Array.ST#v:modify) with an anonymous function to do the same in a more concise way. And `mutable3` shows how we can use a loop function that plays nicely with mutations like `for` or `while`.

![](https://paper-attachments.dropbox.com/s_3FA8B4DB81C1702262F976E2E8FE755602BB0FE3E5BC08FB0DE427D74C5BB95C_1556999560419_Screen+Shot+2019-05-04+at+16.52.18.png)

In the [previous post](https://dev.to/bnevilleoneill/typescript-vs-purescript-not-all-compilers-are-created-equal-12i6-temp-slug-4466128), we mentioned the importance of a [language that can grow](https://www.youtube.com/watch?v=_ahvzDzKdB0) by showing that _JavaScript’s_ [pipeline operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator) (which is still being debated in committee) is implemented in _PureScript_ user-land by the [applyFlipped](https://pursuit.purescript.org/packages/purescript-prelude/4.1.1/docs/Data.Function#v:applyFlipped) function. The same happens with object mutation. In _TypeScript,_ we need special language modifier such as `Readonly`, `ReadonlyArray` and `as const`, while in _PureScript_ we use a normal user-land type (`ST`) that has a [clever](https://github.com/purescript/purescript-st/blob/master/src/Control/Monad/ST/Internal.js#L1) foreign function interface ([FFI](https://github.com/purescript/documentation/blob/master/guides/FFI.md)).

## Representing abstraction

Before we explain how ST and STRef work, let’s introduce an example we’ll use in the final section and use it to explain some language features. The following is a naive implementation of the [QSort algorithm](https://www.youtube.com/watch?v=Hoixgm4-P4M) using a recursive function and immutable arrays.

![Recursive immutable qsort in TS (left) and PS (right)](https://paper-attachments.dropbox.com/s_3FA8B4DB81C1702262F976E2E8FE755602BB0FE3E5BC08FB0DE427D74C5BB95C_1556831093351_Screen+Shot+2019-05-02+at+18.04.19.png)<figcaption>Recursive immutable qsort in TS (left) and PS (right)</figcaption>

The first thing we need to do is to distinguish the base case from the recursion and separate the `head` and `tail` of the array. In _TypeScript,_ we use an _if statement_ for the first part and [destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) for the second. In _PureScript_ we use the [uncons](https://pursuit.purescript.org/packages/purescript-arrays/5.3.0/docs/Data.Array#v:uncons) function, which returns a `Maybe` value to an object with the properties `head` and `tail`. Then with [pattern matching](https://github.com/purescript/documentation/blob/master/language/Pattern-Matching.md#pattern-matching), we can distinguish the base case from the recursion, and assign identifiers to the object properties.

For the second part, we need to calculate `small`, `mid` and `large` using the arrays native filter. In _TypeScript_ we just add those definitions inside the function block and call the **filter method** on `tail`, passing an arrow function. In _PureScript_ we need to use an expression, so instead of a block code, we have to use the [let or where bindings](https://github.com/purescript/documentation/blob/master/language/Syntax.md#let-and-where-bindings). Then we call the **filter function** with an anonymous [lambda](https://github.com/purescript/documentation/blob/master/language/Syntax.md#functions) and the `tail`. From an imperative or OOP perspective it would seem that the arguments are in reverse order, but as this [Haskell article](https://wiki.haskell.org/Parameter_order) shows, it’s to allow better composition. Finally, in order to create the result array, we use the _JavaScript’s_ [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) in TS and the [append method](https://pursuit.purescript.org/packages/purescript-prelude/4.1.1/docs/Data.Semigroup#v:(%3C%3E)) in PS.

We can only get so far in a statically typed language without introducing the notion of abstraction. The previous function only sorts numbers ascendingly, but we would like to sort anything in any order. To do so, we extract the compare function and leave it as a parameter that should be provided by the user. And to enforce type correctness we use [generics](https://www.typescriptlang.org/docs/handbook/generics.html) in _TypeScript_, and parametric types in _PureScript_.

![](https://paper-attachments.dropbox.com/s_3FA8B4DB81C1702262F976E2E8FE755602BB0FE3E5BC08FB0DE427D74C5BB95C_1556831628471_Screen+Shot+2019-05-02+at+18.13.13.png)

_TypeScript_ defines generic types inside angle brackets just before the function parameters. Influenced by Java and .NET it is customed to use capital letters like `T` or at least words that start with an uppercase (even if [it’s not required to](https://www.typescriptlang.org/play/#src=function%20id%3Ct%3E(a%3A%20t)%20%7B%0D%0A%20%20%20%20return%20a%3B%0D%0A%7D%0D%0A%0D%0Aconst%20num%20%3D%20id(9)%3B))). _PureScript_, inspired by _Haskell_, uses the [universal quantifier](https://en.wikipedia.org/wiki/Universal_quantification) `forall` to declare the type parameters. The parameters are separated by space and have a dot to distinguish them from the rest of the definition. It is customed to use lowercase letters like `a`. You can use words, but unlike TS it has to start with a lowercase letter. _PureScript_ supports unicode, so you can replace `forall` for its mathematical symbol `∀`.

Notice that we don’t know anything about `T` or `a`, so we cannot do much with them, just pass them around. In this example, we are basically using them to make sure that the compare function receives two values of the same type as the array. In both languages, we have a way to add a restriction on the type, which gives us more power in what we can do with it, but that’s a concept for a different post.

## Understanding the types

Let’s analyze the `mutable1` example to see how the types fit together. If we look at the [documentation](https://pursuit.purescript.org/packages/purescript-st/4.0.0/docs/Control.Monad.ST.Ref#v:new) for `new` we can see the following signature, that we can split into four parts.

```jsx
new :: forall a r. a -> ST r (STRef r a)
-- 1) forall a r.
-- 2) a ->
-- 3) ST r (...)
-- 4) (STRef r a)
```

First, the function defines two type parameters: `a` is the type of the mutable value, and `r` is a “phantom type” whose only purpose is to restrict the scope of the mutation. Then the function receives only one parameter, the initial value of type `a`. It returns a mutable computation `ST r (...)` that it’s bound to the phantom type. The computation is not to a value of type `a`, rather to a reference to the value (`STRef r a`), which is also bound by the phantom type.

The [read function](https://pursuit.purescript.org/packages/purescript-st/4.0.0/docs/Control.Monad.ST.Ref#v:read) receives a reference to a value and returns a mutable computation to it.

```jsx
read :: forall a r. STRef r a -> ST r a
-- 1) forall a r.
-- 2) STRef r a ->
-- 3) ST r a
```

The [write function](https://pursuit.purescript.org/packages/purescript-st/4.0.0/docs/Control.Monad.ST.Ref#v:write) receives two arguments: the value to write and where to write it. The function then returns a mutable computation of the written value so we don’t have to read it or compute it again.

```jsx
write :: forall a r. a -> STRef r a -> ST r a
-- 1) forall a r.
-- 2) a ->
-- 3) STRef r a ->
-- 4) ST r a
```

We can glue these functions together in an “imperative way” using the `do` notation. This language feature allows us to work with types that have the shape `M a`, like `Effect Unit`, `Array String`, etc as long as the type `M` satisfies a certain restriction that we shall not name in this post. Just rest assured that these types and many others, meet this restriction. The `do` notation does something different depending on the underlying type. When used with `Effect`, we can call synchronous effectful computations one after the other. When used with `Maybe`, we can call different computations that _may_ result in a value or _may_ be empty and if one of them returns empty then the whole computation returns empty. When used with `Array` we can achieve [array comprehensions](https://leanpub.com/purescript/read#leanpub-auto-array-comprehensions) and when used with `ST` then we can run different computations that mutate data.

Each expression inside `do` has to return the same `M` but can have different `a` types. Those different `a` can be bound to an identifier using `identifier ← expression`, except for the last expression which defines the type of the entire `do` expression.

```jsx
foo :: M Int
bar :: M String
zoo :: Int -> String -> M Boolean

what :: M Boolean
what = do
  int <- foo
  str <- bar
  zoo int str
```

If we are working with `Maybe String` we could replace `M` with `Maybe` and `a` with `String`. Similarly, if we have `Array Number` we could say `M = Array` and `a = Number`, but what happens when we work with types that has multiple type parameters? If we have `Either String Number` then `M = Either String` and `a = Number` which means that all the expressions can succeed with different values (`a`) but if they fail, they fail with a `String`. If we have `ST r Number` then `M = ST r` and `a = Number` .

If we look back to the types of `new`, `read` and `write`, we can see that they all return an `ST r something`, so if we put it all together we can see the type of our do expression.

```jsx
new :: forall a r. a -> ST r (STRef r a)
read :: forall a r. STRef r a -> ST r a
write :: forall a r. a -> STRef r a -> ST r a

myDoExpr :: forall r. ST r Int
myDoExpr = do
    -- ref :: STRef r Int
    ref <- Ref.new 0
    -- val :: Int
    val <- Ref.read ref
    -- ST r Int
    Ref.write (val + 1) ref
```

Finally, once we build up our computation, we can run it in order to get the value out.

```jsx
run :: forall a. (forall r. ST r a) -> a
myDoExpr :: forall r. ST r Int

mutable1 :: Int
mutable1 = run myDoExpr
```

Notice that the `run` function has a `forall` inside the first parameter, that feature, called [Rank N types](https://github.com/purescript/documentation/blob/master/language/Types.md#rank-n-types), is the one responsible to avoid leaking the mutation.

We could then ask the millenary question: If a value is mutated inside a function and no one from outside can mutate it, does it make a sound? I mean, is it still pure? I would say yes, and point out that even to resolve the simplest addition (1 + 1) the ALU needs to mutate internal registry, and no-one would question its purity.

## Mutable QSort

In the section “representing abstraction” we examined a naive immutable implementation of the QSort algorithm. Now let’s see how a mutable version looks in both languages (implemented from this [pseudo-code](https://www.geeksforgeeks.org/quick-sort/)).

![Mutable QSort Algorithm in TypeScript (Left) and PureScript (Right)](https://paper-attachments.dropbox.com/s_3FA8B4DB81C1702262F976E2E8FE755602BB0FE3E5BC08FB0DE427D74C5BB95C_1557613145961_Screen+Shot+2019-05-11+at+19.18.31.png)<figcaption>Mutable QSort Algorithm in TypeScript (Left) and PureScript (Right)</figcaption>

In the _TypeScript_ version, we can notice that `mutableQSortBy` receives and returns a `ReadonlyArray`, but inside the body, the recursive function `sort` uses a normal mutable array. In line 17 we do the only copy, which we’ll `sort` in place and once it returns it’ll be marked as a `ReadonlyArray` to avoid further mutation. In _PureScript_ we do a similar thing, in line 6 the [withA](https://pursuit.purescript.org/packages/purescript-arrays/5.3.0/docs/Data.Array.ST#v:withArray)[r](https://pursuit.purescript.org/packages/purescript-arrays/5.3.0/docs/Data.Array.ST#v:withArray)[ray](https://pursuit.purescript.org/packages/purescript-arrays/5.3.0/docs/Data.Array.ST#v:withArray) function executes a mutable computation on a copy of the immutable array, and that computation uses an inner recursive `sort` function that has in scope a reference to the mutable `arr`.

In both cases, the inner `sort` uses an auxiliary function called `partition`, which will pick a pivot and `swap` the lower elements to the left and the higher elements to the right. We can use the comments to see how different parts of the algorithm relates to each other.

![](https://paper-attachments.dropbox.com/s_3FA8B4DB81C1702262F976E2E8FE755602BB0FE3E5BC08FB0DE427D74C5BB95C_1557611826105_Screen+Shot+2019-05-11+at+18.56.32.png)

In the `swap` function, the one that actually does the array mutation, _PureScript_ knows that reading or writing a value with a random index can cause an out of bounds error. So we can use a [safe peek](https://pursuit.purescript.org/packages/purescript-arrays/5.3.0/docs/Data.Array.ST#v:peek) that returns a `Maybe` to the value, or an [unsafe peek](https://pursuit.purescript.org/packages/purescript-arrays/5.3.0/docs/Data.Array.ST.Partial#v:peek) that can cause a runtime exception. We use the later which is faster as we don’t need to wrap and unwrap the value, but it requires us to use `unsafePartial` to indicate that we know the risks.

![](https://paper-attachments.dropbox.com/s_3FA8B4DB81C1702262F976E2E8FE755602BB0FE3E5BC08FB0DE427D74C5BB95C_1556847755763_Screen+Shot+2019-05-02+at+22.37.33.png)

## Conclusion

In this post, we’ve seen how we can represent the same mutable and immutable algorithms in _TypeScript_ and _PureScript_, and how the default of the language changes the ergonomics. Even if it is subjective, I’d say that mutable algorithms look more natural in TypeScript and immutable ones in _PureScript_.

Choosing whether to use mutation or not can depend on different factors, but personally, I try to avoid mutation and only use it when the convenience is greater than the risk. An interesting example to debate can be found in [purescript-halogen-realworld](https://github.com/thomashoneyman/purescript-halogen-realworld/blob/d213795014a69af99dcb90cdbc8628d3f760c171/src/AppM.purs#L68), whereby using the [Ref effect](https://pursuit.purescript.org/packages/purescript-refs/4.1.0/docs/Effect.Ref) (similar to ST) in conjunction with the `ReaderT` type, the application handles the logged user in a sort of “controlled global variable”. Please comment and share if you find it useful.
* * *
### Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps

### [![](https://i0.wp.com/cdn-images-1.medium.com/max/2400/1*s_rMyo6NbrAsP-XtvBaXFg.png?ssl=1)](https://logrocket.com/signup/)

[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

[Try it for free](https://logrocket.com/signup/).
* * *
The post [To mutate, or immutate, that is the question](https://blog.logrocket.com/to-mutate-or-immutate/) appeared first on [LogRocket Blog](https://blog.logrocket.com).