Ramda already has lenses

http://ramdajs.com/docs/#lens
http://ramdajs.com/docs/#lensOn

But I like the API in DrBoolean’s lenses. The problem, as @finnpoitier pointed out, is the 200+ kB footprint that comes with that.

So I want to build something like that so I can use with Ramda.

By the way, as of version 0.13.0 of Ramda, lensOn seems to be broken.

So this is what I came up with:

var lens = (x) => { return R.lens(
function get(o) { return o[x] },
function set(v, o) { var p = Object.create(o); p[x] = v; return p }
)}

var user = { firstName: 'kev', lastName: 'le' }

var _mapLense = function(result, current) {
result[current] = lens(current);
return result;
}

var makeLenses = R.reduce(_mapLense, {})

var L = makeLenses(['firstName', 'lastName'])

L.firstName(user) //=> kev

var kevin = L.firstName.set('kevin', user)
L.firstName(kevin) //=> kevin

var LE = L.lastName.map(R.toUpper, user)
L.lastName(LE) //=> LE

Check it out here

The limitation is that the object where you want to put a lense on (user in this example) has to be flat and cannot be nested.

Update (04-21-2015)
The above Lenses are still depending on Ramda which might or might not OK, depending on the situation. Just as an exercise, I tried to remove the Ramda dependency. Here’s the result that I came up with:

//This curry function is from JavaScript Allonge
function curry(fn) {
var arity = fn.length

return given([])

function given(argsSoFar) {
return function helper() {
var updatedArgsSoFar = argsSoFar.concat(Array.prototype.slice.call(arguments, 0))

if (updatedArgsSoFar.length >= arity) {
return fn.apply(this, updatedArgsSoFar)
}
else return given(updatedArgsSoFar)
}
}
}

function _lens(get, set) {
var f = function(obj) { return get(obj) }
f.get = function(obj) { return get(obj) }
f.set = curry(set)
f.map = curry(function(fx, obj) { return set(fx(get(obj)), obj) })
return f
}

var lens = function(prop) { return _lens(
function get(obj) { return obj[prop] },
function set(val, obj) { var clone = Object.create(obj); clone[prop] = val; return clone }
)}

var _mapLense = function(result, current) {
result[current] = lens(current)
return result
}

var makeLenses = function(ar) { return ar.reduce(_mapLense, {}) }

No more Ramda dependency. The usage is the same as the previous version of Lenses that depended on Ramda.

var L = My.MakeLenses(['id'])
var n = L.id.set(10, {id:100})
L.id(n) //=> 10

var fx = function(x) { return x + 5; }
var m = L.id.map(fx, {id:100})
L.id(m) //=> 105

//and these set and map functions are curried too
var setTo10 = L.id.set(10)
var o = setTo10({id:100})
L.id(o) //=> 10