Skip to content

Question: Is it possible to compute running sums using optics?

Vesa Karvonen edited this page Aug 18, 2017 · 8 revisions

Is it possible to compute running sums using optics?

For example, if you have an object {x: 2, y: -1, z: 3} you would sum the values from left-to-right and produce {x: 2, y: 1, z: 4}.

Answer: Yes, it is possible. However, it requires somewhat advanced techniques. Optic combinators do not accumulate results themselves. What they can do, however, is to create operations that can, among many others things, accumulate state while performing transformations. The way to do this is to use L.traverse with the state applicative.

So, first we need a Static Land compatible state applicative implementation:

const State = {
  of: result => state => ({state, result}),
  ap: (x2yS, xS) => state0 => {
    const {state: state1, result: x2y} = x2yS(state0)
    const {state, result: x} = xS(state1)
    return {state, result: x2y(x)}
  },
  map: (x2y, xS) => State.ap(State.of(x2y), xS),
  mapState: fn => state => {
    const result = fn(state)
    return {state: result, result}
  },
  run: (s, xS) => xS(s).result
}

The above implementation is general purpose and not specific to just this problem.

We can then define a function that uses the state applicative to keep the running sum in the state:

const accum = x => State.mapState(R.add(x))

And then we can use traverse to build up operations and run them to compute the running sums:

State.run(0, L.traverse(State, accum, L.values, {x: 2, y: -1, z: 3}))
// {x: 2, y: 1, z: 4}

Here is a playground that explores this problem.