Created
June 21, 2017 23:52
-
-
Save jethrolarson/2a28945743668cf828c9eb5a4a924d2f to your computer and use it in GitHub Desktop.
partial.lenses intro
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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