reducible
Version:
High-order abstraction for reducible data structures
88 lines (78 loc) • 3.21 kB
JavaScript
;
var reduce = require("./reduce")
var end = require("./end")
var isError = require("./is-error")
var isReduced = require("./is-reduced")
var reduced = require("./reduced")
function Reducible(reduce) {
/**
Reducible is a type of the data-structure that represents something
that can be reduced. Most of the time it's used to represent transformation
over other reducible by capturing it in a lexical scope.
Reducible has an attribute `reduce` pointing to a function that does
reduction.
**/
// JS engines optimize access to properties that are set in the constructor's
// so we set it here.
this.reduce = reduce
}
// Implementation of `accumulate` for reducible, which just delegates to it's
// `reduce` attribute.
reduce.define(Reducible, function reduceReducible(reducible, next, initial) {
var result
// State is intentionally accumulated in the outer variable, that way no
// matter if consumer is broken and passes in wrong accumulated state back
// this reducible will still accumulate result as intended.
var state = initial
try {
reducible.reduce(function forward(value) {
try {
// If reduction has already being completed return is set to
// an accumulated state boxed via `reduced`. It's set to state
// that is return to signal input that reduction is complete.
if (result) state = result
// if dispatched `value` is is special `end` of input one or an error
// just forward to reducer and store last state boxed as `reduced` into
// state. Later it will be assigned to result and returned to input
// to indicate end of reduction.
else if (value === end || isError(value)) {
next(value, state)
state = reduced(state)
}
// if non of above just accumulate new state by passing value and
// previously accumulate state to reducer.
else state = next(value, state)
// If state is boxed with `reduced` then accumulation is complete.
// Indicated explicitly by a reducer or by end / error of the input.
// Either way store it to the result in case broken input attempts to
// call forward again.
if (isReduced(state)) result = state
// return accumulated state back either way.
return state
}
// If error is thrown then forward it to the reducer such that consumer
// can apply recovery logic. Also store current `state` boxed with
// `reduced` to signal input that reduction is complete.
catch (error) {
next(error, state)
result = reduced(state)
return result
}
})
}
// It could be that attempt to reduce underlaying reducible throws, if that
// is the case still forward an `error` to a reducer and store reduced state
// into result, in case process of reduction started before exception and
// forward will still be called. Return result either way to signal
// completion.
catch(error) {
next(error, state)
result = reduced(state)
return result
}
})
function reducible(reduce) {
return new Reducible(reduce)
}
reducible.type = Reducible
module.exports = reducible