const createLens = (getter, setter) => toFunctor => targetData =>
toFunctor(getter(targetData)).map(focus => setter(focus, targetData));
// redifine `toGetAccessors` and `toSetAccessors`
// toGetAccessors
const Const = data => ({
get value() {
return data;
},
map() {
return this;
}
});
// toSetAccessors
const Identity = data => ({
get value() {
return data;
},
map(mapf) {
return Identity(mapf(data));
}
});
// redefine view, set, over
const helpers = {
view: (lens, targetData) => lens(Const)(targetData).value,
over: (
lens,
overFn /* like the `mapf` callback in `Array.prototype.map(mapf)`.
i.e.: You get a value and return a new value. */,
targetData
) => lens(data => Identity(overFn(data)))(targetData).value,
set: (lens, value, targetData) =>
helpers.over(
lens,
() =>
value /* we use `over` with a `overFn` function,
that just returns the value argument */,
targetData
)
};
// lens creator helpers:
const lensCreators = {
lensProp: propName =>
createLens(
(obj = {}) => obj[propName],
(value, obj) => ({
...obj,
[propName]: value
})
),
lensIndex: (index = 0) =>
createLens(
(arr = []) => arr[index],
(value, arr = []) => arr.slice(0, index).concat(value, arr.slice(index))
),
lensPath: (path = []) =>
compose(
...path.reduce(
(lenses, nextPathElem) => [
...lenses,
typeof nextPathElem === "number"
? lensCreators.lensIndex(nextPathElem)
: lensCreators.lensProp(nextPathElem)
],
[]
)
)
};
{
const { lensPath, lensProp } = lensCreators;
const { view, over, set } = helpers;
const studentsList = lensPath(["employees", "students", "list"]);
const berlinStudents = compose(
lensProp("berlin"),
studentsList
);
const parisStudents = compose(
lensProp("paris"),
studentsList
);
const locationsWithStudentListForParis = set(parisStudents, [], locations);
const locationsWithOneStudentInParis = over(
parisStudents,
(list = []) => [...list, { name: "You", setVia: "Lens" }],
locations
);
const locationsWithTwoStudentsInBerlin = over(
berlinStudents,
(list = []) => [...list, { name: "Me", setVia: "Lens" }],
locationsWithOneStudentInParis
);
console.log(
"Paris:",
"before:",
view(parisStudents, locations) // undefined
);
console.log(
"after:",
view(parisStudents, locationsWithOneStudentInParis) // [{name: "You", setVia: "Lens"}]
);
console.log(
"Berlin:",
"before:",
view(berlinStudents, locationsWithOneStudentInParis) // [{id: "c816-2234", name: "Kirsten Denesik", …}]
);
console.log(
"after:",
view(
berlinStudents,
locationsWithTwoStudentsInBerlin
) /*
[
{id: "c816-2234", name: "Kirsten Denesik", …},
{name: "Me", setVia: "Lens"}
] */,
locationsWithTwoStudentsInBerlin
);
}