Home

Awesome

safety-lens

Join the chat at https://gitter.im/hallettj/safety-lens

Type-safe, functional lens library in JavaScript. This is basically a port of some of the concepts from the Haskell lens library. Safety-lens goes best with Flow.

A lens is used to focus in on a specific piece of a data structure. Using a lens, that piece can be read or updated. For example:

import { get, set } from 'safety-lens'
import { prop } from 'safety-lens-native'

let obj = { foo: 1, bar: 2 }

// Get a value
assert( get(prop('bar'), obj) === 2 )

obj = set(prop('bar'), 3, obj)

// Set a value
assert( get(prop('bar'), obj) === 3 )

The function call prop('bar') creates a lens that focuses on the bar property of any object.

get takes a lens and an object, and returns a reference to the focused piece of the object. So get(prop('bar'), obj) returns 2.

set takes a lens, a new value, and an object, and replaces the focused piece of the object with the new value. Or to be more precise, set creates a copy of the object in which the focused piece has been replaced with the new value. In the example above, set(prop('bar'), 3, obj) creates a new object that sets the bar property to 3, and keeps the foo property set to 1.

This is obviously more complicated than writing the equivalent expressions obj.bar or obj.bar = 3. But lenses do come with several advantages:

Documentation

Please refer to these topics for more information:

Install

npm install --save safety-lens

Building from source

Run:

$ yarn install && make

Requires yarn and GNU Make.

Examples of usage

Getting and setting nested values

import { get, set } from 'safety-lens'
import { prop } from 'safety-lens-native'

let obj = { foo: 1, bar: 2 }

// Get a value
assert( get(prop('bar'), obj) === 2 )

obj = set(prop('bar'), 3, obj)

// Set a value
assert( get(prop('bar'), obj) === 3 )

Transforming nested values with a function

import { over } from 'safety-lens'
import { prop } from 'safety-lens-native'

let obj = { foo: 1, bar: 2 }

obj = over(prop('bar'), x => x * 2, obj)

assert( obj.bar === 4 )

Composing lenses

Imagine a program with values of this type:

type Calendar = Array<{ date: Date, title: string }>

const events = [
  { date: new Date(), title: 'get coffee' },
  { date: new Date(), title: 'plan day' }
]

To construct a lens that can focus on the title of the first event in a calendar list:

import { compose, lookup } from 'safety-lens'
import { index, prop } from 'safety-lens-native'

const firstEventLens = index(0)
const titleLens = prop('title')

const firstEventTitleLens = compose(firstEventLens, titleLens)

assert( lookup(firstEventTitleLens, events) === 'get coffee' )

A lens might focus on multiple pieces within a data structure simultaneously. To operate on all events in a calendar:

import { over } from 'safety-lens'
import { traverse } from 'safety-lens-native'

const dateLens = prop('date')

const allDatesLens = compose(traverse, dateLens)

// Moves all events forward by one day.
const rescheduled = over(
  allDatesLens,
  date => new Date(Number(date) + 24 * 60 * 60 * 1000),
  events
)