const pipe = (fn,...fns) => (...args) => fns.reduce( (acc, f) => f(acc), fn(...args));
const compose = (...fns) => pipe(...fns.reverse());
const mapObject = f => obj =>
Object.assign({}, ...Object.keys(obj).map(k => ({[k]: f(obj[k]) })));
const Const = x => ({value: x, map: () => Const(x)})
const Identity = x => ({value: x, map:f => Identity(f(x))})
const lens = (getter, setter) => toFunctorFn =>
target => {
console.log(toFunctorFn(getter(target)))
const functor = toFunctorFn(getter(target))
return functor.map(focus => setter(focus, target))
};
const view = (lens, x) => lens(Const)(x).value
// Using `Const` effectively ignores the setter function of the `lens`,
// leaving the value returned by the getter function unmodified.
const over = (lens, f, x) => lens(y => Identity(f(y)))(x).value
// The value returned by the getter function is first transformed with `f`,
// then set as the value of an `Identity`. This is then mapped over with the
// setter function of the lens.
const set = (lens, v, x) => over(lens, () => v, x)
const prop = name => lens(x => x[name], (a, x) => ({...x, [name]: a}))
const xLens = lens(x => x.x, (a, s) => {
return {...s, x: a}
});
const yLens = prop('y')
console.log(
// mapObject(x => x.length)({a: [], b: [1,2,3,]})
'view', view(xLens, {x: 12, y: 32})
)
console.log(
'over', over(xLens, x => x * 2, {x: 12, y: 32})
)
console.log(
'set', set(xLens, 42, {x: 12, y: 32})
)
console.log(
'set', set(yLens, 42, {x: 12, y: 32})
)
console.log(
'set', set(pipe(prop('y'), prop('x')), 42, {x: {y: 71, z: 61}, w: 81})
)