# Optic Basics: Lenses
To use get
and set
together, Ramda use lenses. They are a bit lower-level and more specific API's. Lodash handles both Arrays
and Objects
, whereas you need to do that with separate functions in Ramda. Lenses combine get
and set
style functionality to navigate and modify large structures by composing smaller functions together.
If you hear the word "Optics", that's what a lot of the Functional people use to refer to lens functionality. We'll cover optics in the Optics chapter.
# Focus on a Property or Path, and View It in Ramda
Just like there are 2 functions in Ramda to get a property via prop
and get a deeply nested property via path
, so too are there 2 lenses to do the same. Unlike Ramda's prop
and path
(Lodash get
does what they both do), functions that return values, a lens is a Type. You call it like any other function to return a configured lens. We'll create some lenses, then use them to "focus" on certain properties and paths of our person Object.
Here's our Object:
const person = {
name: 'Jesse Warden',
address: {
street: '123 Cow Ville',
phone: ['123-456-7890']
}
}
To focus a lens on the name
property, we need to create a prop lens via lensProp
:
import { lensProp } from 'ramda'
const nameLens = lensProp('name')
Just like Ramda's prop
or Lodash' get
, we can see what that lens reveals by passing it to view
:
import { ..., lensProp, view } from 'ramda'
...
console.log(view(nameLens, person)) // Jesse Warden
For the street, we'll need a path lens via lensPath
, and pass that to view
as well:
import { ..., lensPath } from 'ramda'
...
const streetLens = lensPath(['address', 'street'])
console.log(view(streetLens, person)) // 123 Cow Ville
# Set With Lenses
Using our name lens, let's set the name via Ramda's set
:
import { ..., set } from 'ramda'
const result = set(nameLens, 'Cow', person)
console.log(result.name) // Cow
console.log(person.name) // Jesse Warden
Cool, just like Lodash' set
!
Using the street lens should yield a similar result:
const result = set(streetLens, '21 Jump Street', person)
console.log(result.address.street) // 21 Jump Street
console.log(person.address.street) // 123 Cow Ville
# Modification With Lenses
Now that we know how to view our data with our lenses, and use them to set things they're focused on, let's use them to modify data we're focused on in an immutable way using pure functions. Instead of "set it to this new value", we'll instead "take the value that's there, put it into this function, and whatever comes out, set the new value to that result". Below will use our lens to run a function on the property and replace it with the value that function returns. It'll then give us a cloned Object back with the modification:
import { ..., over, toLower } from 'ramda'
const result = over(nameLens, toLower, person)
console.log(result.name) // jesse warden
console.log(person.name) // Jesse Warden
In Lodash, the set
will modify it with the value you give it. The over
in this case will replace it with with whatever function you give it taking the current value as an input, and the output as the new value. Note that it's assumed the function you give it, in this case toLower
, is a pure function.
# Conclusions
This is the basics of lenses. Whether you use Lodash' get
and set
, or Ramda's view
and over
, you can start composing these later into bigger functions that loop over large amounts of data, safely, to view and modify it in a pure, immutable way.