1. First steps
2. We combine functions
3. Partial application (currying)
4. Declarative programming
5. Ruleless Notation
6. Immutability and objects
7. Immutability and arrays
8. Lenses
9. Conclusion
This post is the eighth part of a series of articles on functional programming called "Thinking in the Ramda Style".
In the sixth and seventh parts, we studied how to read, update and transform the properties of objects and elements of arrays in a declarative and immutable style.
Ramda also provides a more general tool for performing these operations, called lenses.
What kind of lenses?
The lens combines the getter function and the setter function into one mechanism. Ramda provides a set of functions for working with lenses.
We can think of lenses as something that focuses on a specific part of a large data structure.
How can I create a lens?
The main way to create a lens in Ramda is the lens function. lens
takes a getter function and a setter function and returns a new lens.
const person = { name: 'Randy', socialMedia: { github: 'randycoulman', twitter: '@randycoulman' } } const nameLens = lens(prop('name'), assoc('name')) const twitterLens = lens( path(['socialMedia', 'twitter']), assocPath(['socialMedia', 'twitter']) )
Here we use the prop
and path
methods as our assocPath
functions, and also assoc
and assocPath
as our setters functions.
Please note that we have duplicated the arguments with the name of the property and the path to the desired property for these functions. Fortunately, Ramda provides cool cuts for the most common lens usage situations: lensProp , lensPath and lensIndex .
lensProp
creates a lens that focuses on the property of an object.lensPath
creates a lens that focuses on the nested property of an objectlensIndex
creates a lens that focuses on the array element
We can rewrite our above-created lenses using lensProp
and lensPath
:
const nameLens = lensProp('name') const twitterLens = lensPath(['socialMedia', 'twitter'])
It is much easier and eliminates duplicates. In practice, I found that I almost never needed the original lens
function.
What can I do with all this?
Okay, great, we created a pair of lenses. What can we do with them now?
Ramda provides three functions for working with lenses.
- view reads lens value
- set updates lens value
- over applies the transform function to the lens
view(nameLens, person) // => 'Randy' set(twitterLens, '@randy', person) // => { // name: 'Randy', // socialMedia: { // github: 'randycoulman', // twitter: '@randy' // } // } over(nameLens, toUpper, person) // => { // name: 'RANDY', // socialMedia: { // github: 'randycoulman', // twitter: '@randycoulman' // } // }
Notice that set
and over
return the entire object with the modified value that your lens was focused on.
Conclusion
Lenses can be useful if we have a certain complex data structure from which we wish to abstract when calling a code. Instead of providing structure or providing getters, setters, and transformers for each available property, we can instead provide lenses.
Client code can then work with our data structures through the use of view
, set
and over
without linking to the exact form of the data structure.
Further
Now we know about Ramda a lot of everything that it provides; in general, it is enough to do the majority of all the operations that we perform in our programs. The final article in this series reviews the subject and mentions some other topics that we may wish to explore on our own.