Another reason why programmers really like monads is that they make writing libraries very easy. To explore this, let's extend our User
object with more functions for getting and setting values but, instead of using getters and setters, we'll use lenses.
Lenses are first-class getters and setters. They allow us to not just get and set variables, but also to run functions over it. But instead of mutating the data, they clone and return the new data modified by the function. They force data to be immutable, which is great for security and consistency as well for libraries. They're great for elegant code no matter what the application, so long as the performance-hit of introducing additional array copies is not a critical issue.
Before we write the lens()
function, let's look at how it works.
var first = lens( function (a) { return arr(a)[0]; }, // get function (a, b) { return [b].concat(arr(a).slice(1)); } // set ); first([1, 2, 3]); // outputs 1 first.set([1, 2, 3], 5); // outputs [5, 2, 3] function tenTimes(x) { return x * 10 } first.modify(tenTimes, [1,2,3]); // outputs [10,2,3]
And here's how the lens()
function works. It returns a function with get, set and mod defined. The lens()
function itself is a functor.
var lens = fuction(get, set) { var f = function (a) {return get(a)}; f.get = function (a) {return get(a)}; f.set = set; f.mod = function (f, a) {return set(a, f(get(a)))}; return f; };
Let's try an example. We'll extend our User
object from the previous example.
// userName :: User -> str var userName = lens( function (u) {return u.getUsernameMaybe()}, // get function (u, v) { // set u.setUsername(v); return u.getUsernameMaybe(); } ); var bob = new User(); bob.setUsername('Bob'); userName.get(bob); // returns 'Bob' userName.set(bob, 'Bobby'); //return 'Bobby' userName.get(bob); // returns 'Bobby' userName.mod(strToUpper, bob); // returns 'BOBBY' strToUpper.compose(userName.set)(bob, 'robert'); // returns 'ROBERT' userName.get(bob); // returns 'robert'