Skip to content

Instantly share code, notes, and snippets.

@jkachmar
Last active April 25, 2017 22:14
Show Gist options
  • Select an option

  • Save jkachmar/6e55f39af11a68e20e3c2db7138abe7c to your computer and use it in GitHub Desktop.

Select an option

Save jkachmar/6e55f39af11a68e20e3c2db7138abe7c to your computer and use it in GitHub Desktop.
partial.lenses tutorial
const R = require('ramda');
const L = require('partial.lenses');
/* -------------------------------------------------------------------------- */
const arr = [1, 2, 3];
// 'get', pulls value from some data structure
L.get(L.index(0), arr);
// > 1
// 'set', returns a copy of some data structure with updated fields
L.set(L.index(0), 0, arr);
// > [0, 2, 3]
/* -------------------------------------------------------------------------- */
const record = {
field1: 0,
field2: 0
};
// NOTE: `'field1'` is shorthand for `L.prop('field1')`
L.get('field1', record);
// > 0
/* -------------------------------------------------------------------------- */
const nested = {
foo: {
bar: {
baz: 0
}
}
};
// NOTE: [x, y, z] is shorthand for L.compose(x, y, z)
// Focusing 'optic' for `baz` within the `nested` object
const fooBarBaz = ['foo', 'bar', 'baz'];
L.get(fooBarBaz, nested); // > 0
// NOTE: partial.lenses automatically calls Object.freeze when operating in
// a non-production environment
L.modify(fooBarBaz, x => x + 1, nested);
// > { foo: { bar: { baz: 1 } } }
/* -------------------------------------------------------------------------- */
// Following along with the partial.lenses tutorial
const sampleTitles = {
titles: [ { language: 'en', text: 'Title' },
{ language: 'sv', text: 'Rubrik' }]
};
/* -------------------------------------------------------------------------- */
// Small lenses can be combined through basic function composition
// This is a lens that focuses on the text value for a given language key
const textIn = language =>
['titles',
L.define([]),
L.normalize(R.sortBy(L.get('language'))),
L.find(R.whereEq({language})),
L.valueOr({language, text: ''}),
L.removable('text'),
'text'];
// A getter for the text value for the 'sv' language code
L.get(textIn('sv'), sampleTitles);
// > 'Rubrik'
// A getter for the text value for the 'en' language code
L.get(textIn('en'), sampleTitles);
// > 'Title'
// A getter for the text value for a non-existent language code, which
// falls back to the default given by `L.valueOr({language, text: ''})`
L.get(textIn('fi'), sampleTitles);
// > ''
// A setter for a non-existent field, which creates the surrounding structure
// in the
L.set(textIn('fi'), 'Otsikko', sampleTitles);
// > { titles:
// > [ { language: 'en', text: 'Title' },
// > { language: 'fi', text: 'Otsikko' },
// > { language: 'sv', text: 'Rubrik' } ] }
// Sets a field as undefined, removing it from the focused structure
L.remove(textIn('sv'), sampleTitles);
// > { titles: [ { language: 'en', text: 'Title' } ] }
// Removes all the fields, which _doesn't_ result in an empty/undefined object
// as `L.define([])` designates `[]` as the 'empty' structure for this lens
R.compose(L.remove(textIn('sv')),
L.remove(textIn('en'))
)(sampleTitles);
// > { titles: [] }
/* -------------------------------------------------------------------------- */
// Lenses can be structured similarly to the object we're accessing
// A 'title' is a removable field with the key 'text'.
const Title = {
text: [L.removable('text'), 'text']
};
// A member of the array of 'titles' can be accessed given the value of its
// language key, if no 'title' exists that matches the given key, return an
// empty string.
const Titles = {
titleIn: language =>
[L.find(R.whereEq({language})),
L.valueOr({language, text: ''})]
};
const Model = {
titles: ['titles',
L.define([]),
L.normalize(R.sortBy(L.get('language')))],
textIn: language => [Model.titles,
Titles.titleIn(language),
Title.text]
};
L.get(Model.textIn('sv'), sampleTitles);
// > Rubrik
/* -------------------------------------------------------------------------- */
// Traversals are optics that focus on multiple targets in a structure
// The result of composing a lens with a traversal is another traversal
const texts = [Model.titles,
L.elems,
Title.text];
// This traversal focuses on the all the titles in our 'sampleTitles' object
L.collect(texts, sampleTitles);
// > [ 'Title', 'Rubrik' ]
// Traversals can be mapped and reduced over
const Max = {empty: () => 0, concat: Math.max};
// This traversal finds the maximum length of a 'title' element in 'sampleTitles'
L.concatAs(R.length, Max, texts, sampleTitles);
// > 6
// Traversals can also modify the contents they focus on...
L.modify(texts, R.toUpper, sampleTitles);
// > { titles:
// > [ { language: 'en', text: 'TITLE' },
// > { language: 'sv', text: 'RUBRIK' } ] }
// ...as well as selectively modify those contents
L.remove([texts,
L.when(t => t.length > 5)],
sampleTitles);
// > { titles: [ { language: 'en', text: 'Title' } ] }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment