-
Notifications
You must be signed in to change notification settings - Fork 8
Making values reducible
This document explains how custom values can be made reducible. Custom because library already does this for all the JS built-in types.
In order to make value reducible (and there for make it compatible with
all the reducer functions) one needs to define how internal reduce
method works with the value
. Currently there are two ways to do that,
define reduce
for the specific Type
/ Class
so that instances
of it will be reducible or just create a fresh reducible value.
The core of this library is defined in a separate library reducible (https://github.com/Gozala/reducible), which exports a same named function reducible allowing creation of reducible values:
var reducible = require("reducible/reducible")
var input = reducible(function reduceInput(next, initial) {
// ...
})
Whenever input
or any of it's transformation is fold
-ed
function reduceInput
passed to reducible
is invoked. It
is passed two arguments:
-
next
- function that must be passed each item of the definedinput
and accumulated state, later is just a value return bynext
in a previous call. -
initial
- value representing initial state to start accumulation from. This is used in a first call tonext
where no state is accumulated yet.
var reducible = require("reducible/reducible")
var input = reducible(function reduceInput(next, initial) {
var state = initial
state = next(1, state)
state = next(2, state)
// ...
})
If input is finite then it has an end that must be indicated
by passing special end value to a next
value. It's analogue
to EOF
. Once end
is passed, input must not call next
anymore. All subsequent calls to next
will be ignored and warnings
will be logged.
var reducible = require("reducible/reducible")
var end = require("reducible/end")
var input = reducible(function reduceInput(next, initial) {
var state = initial
state = next(1, state)
state = next(2, state)
next(end, state)
})
var print = require("reducers/debug/print")
print(input)
// => < 1 2 >
Unfortunately some input
s may error, for example representing
content of non-existing file should indicate an error. In
"reducible" protocol errors can be indicating by passing item
that is instance of Error
. Errors in the reducible protocol are
are equivalent of end
input with error. There for same rules
apply, once Error
instance is passed, input must not call
next
anymore. All subsequent calls to next
will be ignored
and warnings will be logged.
var reducible = require("reducible/reducible")
var end = require("reducible/end")
var input = reducible(function reduceInput(next, initial) {
var state = initial
state = next(1, state)
state = next(2, state)
next(Error("Ooops"), state)
})
var print = require("reducers/debug/print")
print(input)
// => < 1 2 ⚡ [Error: Ooops] >
Consumption of input
may be interrupted by a consumer, for example
input
may be consumed until specific item is discovered. To indicate
interruption of an input consumption, consumer returns specially boxed
value through return value of next
. If next
returns value such that
isReduced(state)
is true
input must stop by ending a stream,
which means either sending end
of input: next(end, state.value)
or an error: next(Error("Failed to close"), state.value)
and next
must not be called anymore. All subsequent calls to next
will
be ignored. Attempt to send value instead of ending an input will cause
Error("Failed to interrupt input")
to be send to a consumer, indicating
error in input:
var reducible = require("reducible/reducible")
var isReduced = require("reducible/is-reduced")
var end = require("reducible/end")
var input = reducible(function reduceInput(next, initial) {
var state = initial
state = next(1, state)
if (isReduced(state)) return next(end, state.value)
state = next(2, state)
if (isReduced(state)) return next(end, state.value)
next(end, state)
})
var print = require("reducers/debug/print")
print(input)
// => < 1 2 >
var take = require("reducers/take")
print(take(input, 1))
// => < 1 >
Types can be made reducible in a very similar way as reducible values can
be created. In order to define make values of Foo
type reducible internal
reduce function must be defined as follows:
var reduce = require("reducible/reduce")
reduce.define(Foo, function(foo, next, initial) {
// ...
})
As you can see API is almost identical to previously described, only
difference is additional first foo
argument that will be an actual
instance being reduced / folded.
Sometimes one may need to make specific instance reducible instead of
making creating new one or making whole type / class reducible. This
can also be done in a very similar way as with types only thing that
changes is that reduce.implement
must be used instead of
reduce.define
:
var reduce = require("reducible/reduce")
reduce.implement(instance, function(instance, next, initial) {
// ...
})
Now go ahead and make things reducible!! In the process you may find some already existing libraries useful to look at:
- fs-reduce - Reducible filesystem library.
- stream-reduce - Node streams made reducible
- callback-reduce - Callbacks made reducible
- dom-reduce - Reducible DOM APIs
- http-reduce - Reducible HTTP library (XHR only so far)