In functional programming, the Lenses pattern offers a solution for handling data manipulation in an immutable way. A Lens essentially serves as a first-class reference to a subpart of some data type. Despite its regular use in languages with built-in support for lenses (e.g. Haskell), a JavaScript developer can still incorporate the lens pattern via libraries or custom implementations.
This blog post will explore the lenses pattern and demonstrate how you can implement it in JavaScript to work with deeply nested paths.
What is a Lens?
A Lens is a functional pattern used to manage immutable data operations. They let us “zoom in”, or focus, on a particular part of a data structure (like an object or an array). Every lens consists of two functions: a getter and a setter.
Getter Function: Retrieves a sub-part of the data.
Setter Function: Updates a sub-part of the data in an immutable way.
An important feature of lenses is that they compose, meaning lenses focusing on nested data can be effectively chained to manipulate a required piece of data.
Implementing Lenses in JavaScript
Even though JavaScript doesn’t provide built-in support for lenses, we can create a custom lens function to achieve similar functionality. A basic lens function involves creating a getter and setter to retrieve and update the data respectively.
Let’s start simple and create a lens that is not dynamic, meaning it works with a single predetermined path:
function lens(getter, setter) { return { get: getter, set: setter }; }
But the real power of lenses comes when we make them handle deeply nested paths. To achieve that, we make our lens function accept a path (an array of keys), and modify our getter and setter to navigate the object using this path:
function lens(path) { return { get: (object) => path.reduce((obj, key) => obj && obj[key], object), set: (value, object) => { const setObjectAtKeyPath = (obj, path, value) => { if (path.length === 1) { return { ...obj, [path[0]]: value }; } const key = path[0]; return { ...obj, [key]: setObjectAtKeyPath(obj[key] || {}, path.slice(1), value) }; }; return setObjectAtKeyPath(object, path, value); }, }; }
To use this lens, we create a path array indicating the sequence of keys to the desired property, and pass this path to the lens function. The returned lens object provides get and set methods for reading and updating the property:
const person = { name: "John Doe", address: { street: "123 Main St", city: "Any town", country: "USA" }, };
// Create a lens for the address.street path: const streetLens = lens(["address", "street"]);
// Get street using the lens: console.log(streetLens.get(person)); // Outputs: 123 Main St
// Set street using the lens, resulting in a new (immutable) object: const newPerson = streetLens.set("456 Broadway St", person); console.log(newPerson); // Outputs the new person object with the updated street
Conclusion
The lenses pattern rewards developers with the ability to maintain immutability while easily accessing and updating deeply nested data structures. The effectiveness and elegance of lenses are in their composability and the simplicity of resulting code. While JavaScript does not support lenses natively, we’ve seen how to produce a lens like behaviour using array methods and recursion. However, for simplification, this custom implementation does not handle edge cases that established libraries do. Thus, for production-level code, consider using libraries such as Ramda or partial.lenses that offer more comprehensive lens functionalities.
Link to my original article:
https://dev.to/ashutosh_mathur/understanding-and-implementing-the-lenses-pattern-in-javascript-333a
#Javascript #functional-architecture-pattern #frontend #ramda
7 Comments
Can Just say this sort of relief to look for someone who actually knows what theyre going over situated on the internet. You certainly an idea how to convey a problem to gentle and create it important. Many people need to have comprehend this facet of the story. I cant believe youre not known as a result of you actually hold the gift.
Your point of view caught my eye and was very interesting. Thanks. I have a question for you. https://accounts.binance.com/en-NG/register?ref=JHQQKNKN
Your point of view caught my eye and was very interesting. Thanks. I have a question for you.
You absolutely know how to keep your readers interest with your witty thoughts on that topic. I was looking for additional resources, and I am glad I came across your site. Feel free to check my website FQ5 about Airport Transfer.
Your article helped me a lot, is there any more related content? Thanks!
Your article helped me a lot, is there any more related content? Thanks! https://accounts.binance.info/register?ref=P9L9FQKY
Unless they are at high risk for flu complications, kids don t necessarily need a flu test or Tamiflu, so don t necessarily need to visit their pediatrician when you think they have the flu priligy 30mg