1.5.0 • Published 9 years ago

safety-lens v1.5.0

Weekly downloads
3
License
Apache-2.0
Repository
github
Last release
9 years ago

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, and pairs well with Immutable.

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/es2015'

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:

  • Lenses perform immutable updates: you get an updated copy of a data structure, while the original is untouched. This means that lenses pair nicely with immutable data libraries like Immutable or Mori.

  • Lenses can be composed to focus on deeply nested pieces of a data structure. Updating a deeply-nested, immutable data structure by hand is painful - but lenses make it easy.

  • Lenses provide type safety. Immutable has methods getIn, setIn; and Mori has equivalent functions. These are invaluable for working with deeply nested data structures. But there is no type checker available today that can check the type of a value returned from one of these functions, or that can check that appropriate types are used in updates. Lenses do the same job, but with dependable type-checking using Flow.

  • Lenses provide encapsulation. When writing a library or module, you can export lenses, but keep the details of your data structures hidden.

Documentation

Please refer to these topics for more information:

Install

npm install --save safety-lens

Building from source

Run:

$ npm install

That invokes the prepublish npm script, which runs the type checker, transpiles code, and runs tests. Requires GNU Make.

Examples of usage

Getting and setting nested values

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

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/es2015'

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:

import { List } from 'immutable'

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

const events = List.of(
  { 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 } from 'safety-lens/immutable'
import { prop } from 'safety-lens/es2015'

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/immutable'

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
)
1.5.0

9 years ago

1.4.0

10 years ago

1.3.0

10 years ago

1.2.0

10 years ago

1.1.1

10 years ago

1.1.0

10 years ago

1.0.2

10 years ago

1.0.1

10 years ago

1.0.0

10 years ago