Simple Immutable Data w/ Spectacles ?

Do you want to love immutable data but think it’s a drag?

Are you perplexed by the syntax of immutability-helper? Repulsed by immer.js’s use of assignment? Alarmed by lodash’s lack of type safety?

Looking for something a little more intuitive, powe…


This content originally appeared on DEV Community and was authored by Anthony G

Rose-Colored Spectacles

Do you want to love immutable data but think it's a drag?

Are you perplexed by the syntax of immutability-helper? Repulsed by immer.js's use of assignment? Alarmed by lodash's lack of type safety?

Looking for something a little more intuitive, powerful & flexible? Get some clarity w/ spectacles (github repo)!

Syntax (featuring auto-complete!)

import { pipe } from 'fp-ts'
import { set } from 'spectacles-ts'

const oldObj = { a: { b: 123 } }
const newObj = pipe(oldObj, set(['a', 'b'], 999))
// oldObj = { a: { b: 123 } }
// newObj = { a: { b: 999 } }

It's that simple!

(If pipe syntax is unfamiliar checkout this quick explanation)

Also featuring get and modify

Change Object types

import { upsert } from 'spectacles-ts'

const objS: { a: { b: string} } = pipe(
  obj, 
  upsert(['a', 'b'], 'abc')
)

Also featuring modifyW (which stands for 'modify widen' - as in 'widen the output type')

Functional Programming (fp)

spectacles-ts integrates seamlessly with the fp-ts ecosystem (it's built on top of the excellent monocle-ts library)

Its curried functions fit in nicely w/ a functional style:

const as: number[] = [{ a: 123 }].map(get('a'))

Array access

We can do Array access using a number for the index:

const a = pipe([{ a: 123 }], get(0, 'a'))

Since Array access at a given index might fail, we use fp-ts's Option type

import * as O from 'fp-ts/Option'

//           |
//           v
const a: O.Option<number> = pipe([{ a: 123 }], get(0, 'a'))

The Option type is powerful, featuring a full set of combinators. It can be a great, simple intro into the joys of fp-ts

This also gives us a way to know when a 'set' call has failed, using setOption:

import { set, setOption } from 'spectacles-ts'

const silentSuccess: number[] = pipe([123], set([0], 999))
const silentFailure: number[] = pipe([123], set([1], 999))
// silentSuccess = [999]
// silentFailure = [123]

const noisySuccess: O.Option<number[]> = pipe([123], setOption([0], 999))
const noisyFailure: O.Option<number[]> = pipe([123], setOption([1], 999))
// noisySuccess = O.some([999])
// noisyFailure = O.none

Also featuring modifyOption and modifyOptionW

Traversals

We can traverse an Array to collect its nested data

const a: number[] = pipe(
  [{ a: 123 }, { a: 456 }],
  get('[]>', 'a')
)

// equivalent to:
const a2: number[] = [{ a: 123 }, { a: 456 }].map(get('a'))

// a = a2 = [123, 456]

Or to make a change across all of its values

const a: { a: number }[] = pipe(
  [{ a: 123 }, { a: 456 }],
  set(['[]>', 'a'], 999)
)

// a = [{ a: 999 }, { a: 999 }]

We can also traverse a Record

declare const rec: Record<string, { a: number }>

const a: number[] = pipe(rec, get('{}>', 'a'))

Other stuff

You can access the index of a tuple:

const tup = [123, 'abc'] as [number, string]
const getIndex: number = pipe(tup, get('0'))
// getIndex = 123

You can access the key of a record:

const rec: Record<string, number> = { a: 123 }
const getKey = pipe(rec, get('?key', 'a'))
// getKey = 123

You can pick a few keys:

const pickedKeys = pipe(
  { nest: { a: 123, b: 'abc', c: false } }, 
  set(['nest', ['a', 'c'] as const], { a: 999, c: true })
)
// pickedKeys = { nest: { a: 999, b: 'abc', c: true } }

Or remove them:

import { remove } from 'spectacles-ts'

const removedKeys: { nest: { b: string } } = pipe(
  { nest: { a: 123, b: 'abc', c: false } }, 
  remove(['nest', ['a', 'c'] as const])
)
// removedKeys = { nest: { b: 'abc' } }

You can rename a key:

import { rename } from 'spectacles-ts'

const renamedKey: { nest: { a2: number } } = pipe(
  { nest: { a: 123 } }, 
  remove(['nest', 'a2'])
)
// renamedKey = { nest: { a2: 123 } }

You can refine a union type:

const refined: number = pipe(
   { a: 123 } as { a: string | number },
   get('a', (a): a is number => typeof a === 'number')
)

And there are convenience operations for working with Option and Either types

Limitation

You can only use up to four operations at a time (Alas!)

You can nest functions instead:

import { pipe } from 'fp-ts/function'
import { get, set, modify } from 'spectacles-ts'

const getDeep: number = pipe(
  { a: { b: { c: { d: { e: 123 } } } } },
  get('a', 'b', 'c', 'd'),
  get('e')
)

const setDeep = pipe(
  { a: { b: { c: { d: { e: 123 } } } } },
  modify(
    ['a', 'b', 'c', 'd'],
    set(['e'], 321)
  )
)

Nesting functions that change their output type looks a little uglier, but it works:

const upsertDeep: { a: { b: { c: { d: { e: number; e2: string } } } } } = pipe(
  { a: { b: { c: { d: { e: 123 } } } } },
  modifyW(
    ['a', 'b', 'c', 'd'],
    val => pipe(
      val,
      upsert(['e2'], 'abc')
    )
  )
)

spectacles-ts vs monocle-ts

spectacles-ts is built on top of monocle-ts, which is more powerful and flexible but a little less ergonomic.

monocle-ts has these advantages:

  • spectacles-ts only works in piped contexts (except for get)
  • No limitation on object size
  • can filter (similar to es6's filter)
  • can traverse on any arbitrary traversable object (aka Zippers or Rose Trees)
  • Can define an isomorphism between two objects
  • works with the Map type

Conclusion

I hope spectacles-ts can help you modify data both immutably & ergonomically!

CREDITS:
Logo - Stuart Leach


This content originally appeared on DEV Community and was authored by Anthony G


Print Share Comment Cite Upload Translate Updates
APA

Anthony G | Sciencx (2021-08-25T04:04:46+00:00) Simple Immutable Data w/ Spectacles ?. Retrieved from https://www.scien.cx/2021/08/25/simple-immutable-data-w-spectacles-%f0%9f%91%93/

MLA
" » Simple Immutable Data w/ Spectacles ?." Anthony G | Sciencx - Wednesday August 25, 2021, https://www.scien.cx/2021/08/25/simple-immutable-data-w-spectacles-%f0%9f%91%93/
HARVARD
Anthony G | Sciencx Wednesday August 25, 2021 » Simple Immutable Data w/ Spectacles ?., viewed ,<https://www.scien.cx/2021/08/25/simple-immutable-data-w-spectacles-%f0%9f%91%93/>
VANCOUVER
Anthony G | Sciencx - » Simple Immutable Data w/ Spectacles ?. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/08/25/simple-immutable-data-w-spectacles-%f0%9f%91%93/
CHICAGO
" » Simple Immutable Data w/ Spectacles ?." Anthony G | Sciencx - Accessed . https://www.scien.cx/2021/08/25/simple-immutable-data-w-spectacles-%f0%9f%91%93/
IEEE
" » Simple Immutable Data w/ Spectacles ?." Anthony G | Sciencx [Online]. Available: https://www.scien.cx/2021/08/25/simple-immutable-data-w-spectacles-%f0%9f%91%93/. [Accessed: ]
rf:citation
» Simple Immutable Data w/ Spectacles ? | Anthony G | Sciencx | https://www.scien.cx/2021/08/25/simple-immutable-data-w-spectacles-%f0%9f%91%93/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.