const Const = require("./Const")
const Identity = require("./Identity")
const pipe = (...arr) => arr.reduce(
(acc, next) => x => next(acc(x))
, x => x
)
const compose = (...arr) => pipe(...arr.reverse());
const lens = (getter, setter) => toFunctorFn =>
target => {
const functor = toFunctorFn(getter(target))
return functor.map(focus => setter(focus, target))
};
const view = (lens, x) => lens(Const.pure)(x).run()
const over = (lens, f) => x => lens(y => Identity.pure(f(y)))(x).run()
const set = (lens, v) => over(lens, () => v)
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(
'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(compose(prop('x'), prop('y')), 42)({x: {y: 71, z: 61}, w: 81})
)
console.log(
pipe(
set(prop('x'), 87)
, over(prop('y'), y => y + 3)
, over(prop('x'), x => x / 3)
)({x: 7, y: 1})
)
console.log(
'view', view(compose(prop('x'), prop('y')),{x: {y: 71, z: 61}, w: 81})
)
class Const {
constructor(z) {
this.run = () => z
// this :: Const z x
// (x -> y) -> Const z y
this.map = f => new Const(this.run())
}
}
Const.pure = z => new Const(z)
module.exports = Const
class Identity {
constructor(x) {
this.run = () => x
// this :: Identity x
// (x -> y) -> Identity y
this.map = f => new Identity(f(this.run()))
// this :: Identity x
// (x -> Identity y) -> Identity y
this.bind = f => f(this.run())
// this :: Identity x
// (x -> (y || Identity y)) -> Identity y
this.then = f => {
const y = f(this.run())
return y instanceof Identity ? y : new Identity(y)
}
}
}
Identity.pure = x => new Identity(x)
module.exports = Identity