Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jethrolarson/2a28945743668cf828c9eb5a4a924d2f to your computer and use it in GitHub Desktop.
Save jethrolarson/2a28945743668cf828c9eb5a4a924d2f to your computer and use it in GitHub Desktop.
partial.lenses intro
Lenses
======
What is a lens?
---------------
pair of getter and setter that perform immutable updates on parameterized data structure
e.g.
```js
// getChecked :: Object -> Boolean
const getChecked = obj => obj.checked
// more generally its
// s a -> a
// setChecked :: Boolean -> Object -> Object
const setChecked = checked => obj => ({checked, ...obj})
// a -> s -> s a
const checkedLens = Lens(getChecked, setChecked)
```
What's special about that?
--------------------------
composition
```js
const episodeCheckedLens = compose(episodeLens, checkedLens)
```
mapping
```js
const modify = (lens, updater, data) => set(lens, updater(get(lens, data)), data)
```
partial.lenses has some helpers that make defining lenses concise:
```js
// checkedLens implementation can be replaced with
const checkedLens = prop('checked');
// we can go one step further and just use the key string as a shorthand the prop lens:
'checked' === prop('checked')
set('checked', true, {checked: false}); // => {checked: true}
// To target an index of an array-like you use
index(i)
// but you can just use the number as a shorthand
1 === index(1)
// array of lenses is converted to composition of lenses
['episodes', 1, 'checked']
// is equivalent to:
compose(prop('episode'), index(0), prop(checked))
// so to toggle the checked property of an episode at a specific index:
modify(['episodes', 1, 'checked'], x => !x, {episodes: [{}, {checked: false}]}) // => {episodes: [{}, {checked: true}]}
```
Traversals
==========
Like a lens but interacts with a collection of data within a data structure
```js
//check all episodes in a group
set(['episodes', elems, 'checked'], true, seasonGroup)
// get the sum of all durations of all episodes in all groups in all seasons
sum(['seasons', elems, 'groups', elems, 'episodes', elems, 'duration'], show)
// Sum is an example of a fold which is just another word for reduce
select(['seasons', elems, 'groups', elems, when(group => group.name === 'cool group')], show)
```
Everything in partial.lenses is curried ramda-style so you can partially apply arguments to create targeted functions.
```js
const mapFirstEpisode = modify(['episodes', 0])
```
I've also found that you can create namespaces of lenses to create class-like structures. This gives cohesion and an object-oriented flavor back to functional programming.
```js
const Episode = {
checked: 'checked',
rating: 'rating',
name: ['name', define('EpisodeName')], // `define` sets the backup value if name is undefined or if set to undefined. This means that the name will never be undefined and cannot even be set to an undefined value.
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment