Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 25, 2021 04:04 am GMT

Simple Immutable Data w/ Spectacles

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)

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

That's one reason you might want to use a function like get:

import { get } from 'spectacles-ts'const as: number[] = [{ a: 123 }].map(get('a'))// as = [123]

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'//           |//           vconst a: O.Option<number> = pipe([{ a: 123 }], get(0, 'a'))// a = O.some(123)

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

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

const rec =   { a: 123, b: 456 } as Record<string, { a: number }>const a: number[] = pipe(rec, get('{}>', 'a'))// a = [123, 456]

Modification

You can modify a value in relation to its old value:

import { modify } from 'spectacles-ts'const mod: { a: number }[] =  pipe([{ a: 123 }], modify([0, 'a'], a => a + 4))// mod = [{ a: 127 }]

You can even change a value's type this way:

import { modifyW } from 'spectacles-ts'//         ^//         |// The 'W' stands for 'widen'// as in 'widen the type'const modW: { a: string | number }[] =  pipe([{ a: 123 }], modifyW([0, 'a'], a => `${a + 4}`))// mod = { a: '127' }

Also featuring modifyOption and modifyOptionW

Change Object types

You can change an existing key:

import { upsert } from 'spectacles-ts'const obj: { a: { b: string} } = pipe(  { a: { b: 123 } },   upsert(['a', 'b'], 'abc'))// obj = { a: { b: 'abc' } }

Or add a new one:

const obj: { a: { b: number; c: string } } = pipe(  { a: { b: 123 } },   upsert(['a', 'c'], 'abc'))// obj = { a: { b: 123, c: 'abc' } }

Or remove a few of 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' } }

Or rename a key:

import { rename } from 'spectacles-ts'const renamedKey: { nest: { a2: number } } = pipe(  { nest: { a: 123 } },   rename(['nest', 'a'], 'a2'))// renamedKey = { nest: { a2: 123 } }

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 = { a: 123 } as Record<string, number>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 } }

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


Original Link: https://dev.to/anthonyjoeseph/simple-immutable-data-w-spectacles-4nb5

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To