• Stars
    star
    181
  • Rank 212,110 (Top 5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 12 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Library for higher-order manipulation of collections

reducers

Build Status

Browser support

Library for higher-order manipulation of collections, based upon reduce.

Rationale

Most functional languages (including beloved JS) typically come with some collection transformation functions like filter and map that take a logical collections and return transformed version of it. Unfortunately they tend to complect, by implying mechanism, order, laziness and representation. This library is an attempt to provide simple solution for some of the hard problems by decomplecting and building upon simple premise - minimum definition of collection is something that is reducible.

More specifically library defines super-generalized and minimal abstraction for collections - a collection is some set of things that, when given a function to apply to its contents, can do so and give you the result, i.e. a collection is (at minimum) reducible. In other words, you can call reduce on it.

A very minimal abstraction for collection is more powerful than it may seem at first!

Basics

Demonstration of features of this library requires some basic understanding of the abstraction above. So let's take a more practical look at the idea. Let's say we have a reduce function with (very familiar) API:

reduce(source, f, initial) // => accumulated result

It takes reducing function, a reducible source and initial value to accumulate reductions upon. In return it outputs an accumulated result. Reducing functions performing accumulation have a following shape:

f(result, value) // => new result

A reducing function is simply a binary function, akin to the one you might pass to reduce. While the two arguments might be treated symmetrically by the function, there is an implied semantic that distinguishes the arguments: the first argument is a result or accumulator that is being built up by the reduction, while the second is some new input value from the source being reduced.

Transformations

All of the collection operations can be expressed in terms of transformations. By the definition all transformations will produce reducible collections that can be reduced via reduce function defined above:

map(source, JSON.parse) // => reducible collection
filter(numbers, isEven) // => reducible collection

In order to explain transformations we'll need a primitive API for producing reducible collections. Let's define one in form of reducible function that takes accumulator function and returns something that can be reduced via reduce function:

reducible(accumulator) // => reducible

Argument it takes, accumulator is a function that performs has following shape:

accumulate(next, initial) // => accumulated result

And when invoked it performs reductions via next reducing function starting from initial result.

Now consider following implementation of map & filter transformation functions:

function map(f, source) {
  return reducible(function accumulator(next, initial) {
    return reduce(source, function reducer(result, input) {
      return next(result, f(input))
    }, initial)
  })
}

function filter(predicate, source) {
  return reducible(function accumulator(next, initial) {
    return reduce(source, function reducer(result, input) {
      return predicate(input) ? next(result, input) : result
    }, initial)
  })
}

There are a few things to note here:

  • Type of the source is irrelevant as long as it is reducible and there for can be reduced via reduce function.
  • Transformations do not traverse collections, instead they compose results that can be reduced by a receiver of the result later.
  • Transformations do not imply timing in which reducer in invoked with an each input of the source, there for source can be asynchronous.
  • Filtering can skip inputs by simply returning the incoming result.

Features

Laziness

Library consists of transformation functions which, as seen above, when called do nothing except the creation of a recipe for a new collection, a recipe that is itself reducible. No work is done yet to the contained elements and no concrete collection is produced. All the transformations defer actual work to a point where result of transformations pipeline is being reduced.

The beautiful thing is that this mechanism also works for all other traditional transformations take, drop, merge etc. Note the fact that filter is (potentially) contractive, and flatten is (potentially) expansive per step - the mechanism is general and not limited to 1:1 transformations.

Uniformity

Transformation functions are absolutely agnostic of the actual type of the source, as they just describe transformations and leave it up to source to do a reduction when result is consumed.

Library takes a advantage of this feature and takes it even step further by treating every possible value as a reducible collection. Non collection values like numbers, booleans, objects etc. are treated as collection of single item, item being a value. Also null and undefined are considered as empty collections.

This means that library can be used on any data type and more importantly transformations between different data types & compose naturally, which is great, let's you define logic in terms of abstractions instead of specific types.

Composability

All the transformations are fully composable as a matter of fact transformation pipelines produce compositions equivalent of a function compositions created by a compose. Also type agnostic nature of the transformation functions enables compositions between different types of data.

Performance

Since transformations doesn't do the work, but merely create a recipe, there is no per-step allocation overhead, so it's faster. Also note that transformations are composed by curring transformation functions and all the actual work happens in a pipe line at the end when result is consumed, which means that no intermediate collections are produced, unlike it's a case with arrays etc..

Think monad & category theory if you fancy that.

It can even outperform arrays when used wisely, although it's not the point & arrays are not the primary use case.

Asynchronicity

As it was already pointed out transformation functions do not imply any timing of individual value delivery, which means they can be used on asynchronous data structures like node streams or FRP events & signals.

This feature is extremely powerful as it allows structuring complex asynchronous programs in simple intuitive code without a callback hell and manual error propagation. See lstree for examples.

Even better actually exact same code can be used with both synchronous and asynchronous data structures. For example exact same code in fs-reduce can be forced to do blocking IO by via options.sync option.

Extensibility

Since transformations are source type agnostic it's highly extensible. In fact implementation is based of polymorphic method dispatch library and enables one to add support for new data types without any changes to this library or data types / classes them self. This feature is used by stream-reduce library to add support for node streams. There are more examples of this feature in callback-reduce, dom-reduce, http-reduce...

Very likely all data types like signal provided by this library will be move out into own libraries too.

Automatic disposal

Reducible data structures feature auto cleanup of the resources at the end of consumption. For example dom-reduce and fs-reduce use this feature to remove event listeners / close file descriptors once input is consumed and to set you free from clean up constraints. This means you spend more time on actual problems rather and less on plumbing.

Infinity

Infinite data structures can be trivially represented via reducibles since nothing implies the end. In fact dom-reduce uses this feature to represent user events in form of reducibles that pretty much can be infinite.

That being said reducibles are not the best abstraction for the some types of infinite data structures specially ones that rather better be polled instead.

F.A.Q.

1. Q: Can this handle "back pressure" ?

A: Short answer is Yes.

See IO Coordination for more detailed answer

Install

npm install reducers

Prior art

More Repositories

1

querystring

Node's querystring module for all engines.
JavaScript
287
star
2

selfish

Class-free, pure prototypal inheritance
JavaScript
165
star
3

crypto

JavaScript implementations of standard and secure cryptographic algorithms.
JavaScript
157
star
4

sky-edit

Editor embedded in browser.
JavaScript
119
star
5

ambiance

Ambiance code editor
JavaScript
75
star
6

protocol

Protocol based polymorphism for javascript.
JavaScript
75
star
7

streamer

Asynchronously recursive pure function <3 via lazy streams
JavaScript
66
star
8

interactivate

Interactive code editing in browser
JavaScript
47
star
9

micro-promise

Just an essentials for working with promises
JavaScript
41
star
10

undefined.js

Just require undefined & everything will be defined!
JavaScript
40
star
11

teleport

Tool that enables you to write interoperable modules across server and browser without any dependency hassles.
JavaScript
40
star
12

js-tail-call

JS tail recursion shim through trampolining
36
star
13

lunet

Exploration: P2P Network access through the service worker
JavaScript
35
star
14

light-traits

Light traits in javascript with some syntax sugar
JavaScript
32
star
15

channel

CSP style channel implementation, for the Channel specification
JavaScript
31
star
16

firebox

SDK on top of Firefox for building desktop apps using HTML/CSS/JS
JavaScript
30
star
17

actor

Library for managing concurrent effects using generators.
JavaScript
30
star
18

method

Functional polymorphic method dispatch
JavaScript
27
star
19

ipdf

Content content-addressable data feed
JavaScript
25
star
20

test-commonjs

CommonJS test runner for Unit Testing/1.1
JavaScript
24
star
21

json-template

Minimal but powerful templating language implemented in multiple languages
JavaScript
23
star
22

dispatcher

Pattern matching for JavaScript.
JavaScript
23
star
23

jsdocs

CommonJS based javascript documentation generator
JavaScript
21
star
24

elmjs

Elm in JS
JavaScript
20
star
25

alias-quokka-plugin

Quokka plugin to provide module import aliases
JavaScript
20
star
26

dominion

Library for describing & applying DOM changes (off UI thread)
JavaScript
19
star
27

markdown-wiki

A Bidirectional WikiText to Markdown converter.
JavaScript
18
star
28

replicator

Interactive notebook experiment
JavaScript
18
star
29

slime-js

Emacs slime extension for swank-js
Emacs Lisp
15
star
30

guards

JavaScript library for data type & data structure validations.
JavaScript
15
star
31

styleless

Yet another alternative to CSS, with variables, functions, mixins. But now it's all js.
JavaScript
15
star
32

graphquire

Module dependency graph builder / linker / installer
JavaScript
15
star
33

scratch-kit

Scratchpad for add-on SDK
JavaScript
15
star
34

unreachable

Utility function for exhaustiveness checking with typed JS (TS or Flow)
JavaScript
14
star
35

reducible

High-order abstraction for reducible data structures
JavaScript
13
star
36

jetpack-protocol

Library allowing to add new protocols to XULRunner.
JavaScript
13
star
37

jetpack-io

NodeJS style IO for jetpack
JavaScript
12
star
38

transducers

Clojure inspired transducers implementation in JS
JavaScript
12
star
39

antimutable-array

Immutable alternatives to built-in array operators
JavaScript
12
star
40

jetpack-net

jetpack net library (Implements nodejs APIs)
JavaScript
12
star
41

interset

Binary operations for logical sets
JavaScript
11
star
42

episcope

ECMAScript scope analyzer
JavaScript
11
star
43

extendables

Simple and elegant inheritance in JS.
JavaScript
11
star
44

bespin-vim

vim keyboard mappings for bespin
JavaScript
10
star
45

callback-reduce

Callbacks made reducible
JavaScript
10
star
46

functional

functional javascript <3
JavaScript
10
star
47

web-encoding

TextEncoder and TextDecoder APIs from Encoding Standard APIs in a universal package
JavaScript
10
star
48

node.core

core.async flavored interface to a node standard library
Clojure
9
star
49

jep4repl

javascript shell for jetpacks
JavaScript
9
star
50

doc

Runtime documentation tool for REPL
JavaScript
9
star
51

hackalyst

Hack catalyst: Knowledge exchange community
9
star
52

experiments

just prototypes
JavaScript
9
star
53

vice

Vim mode for ace
JavaScript
9
star
54

volcan

Client for firefox remote debugging protocol
JavaScript
8
star
55

isoscope

ECMAScript function isolation analyzer
JavaScript
8
star
56

eventual-cljs

Abstraction of eventual values for clojurescript
Clojure
8
star
57

narwhal-xulrunner

xulrunner engine support for Narwhal
JavaScript
7
star
58

namespace

Library for defining private / shared namespaced properties.
JavaScript
7
star
59

functional-lua

Functional <3 for Lua
Lua
7
star
60

prose

Experimental multilingual literate programming
JavaScript
7
star
61

streduce

Node streams made reducible
JavaScript
7
star
62

eventual

Abstraction for eventual values
JavaScript
7
star
63

membrane-traits

Traits & object-capability security model based wrappers for secure interaction with unprivileged code
JavaScript
7
star
64

github

CommonJS library for accessing GitHub API
JavaScript
7
star
65

lt.plugins.vulcan

Firefox debugger client for Light Table
JavaScript
6
star
66

reactor-commonjs

CommonJS Reactor/A for nodejs
JavaScript
6
star
67

perma-map

Immutable Hash Maps implemented as Hash-Array Mapped Trie (HAMT)
JavaScript
6
star
68

filesystem-composer

CommonJS filesystem composition library
JavaScript
6
star
69

node-interactivate

Interactive code editing for node
JavaScript
6
star
70

try-wisp

Interactive wisp code editor with compile preview.
JavaScript
6
star
71

task.flow

Library for describing async operations as data & managing them as explicit effects
JavaScript
6
star
72

wiky

bidirectional wikitext markup converter
JavaScript
5
star
73

gozala.github.com

Personal cloud
CSS
5
star
74

conspire

collaborative rich text editing experiment
JavaScript
5
star
75

uses

DSL to use your values as you wish.
JavaScript
5
star
76

lt.plugins.match-highlighter

Light Table plugin for highlighting matcking tokens
JavaScript
5
star
77

match.flow

Poor man's pattern matching in flow
JavaScript
5
star
78

duo-node

JavaScript
5
star
79

about-downloads-addon

Download manager in the tab.
JavaScript
5
star
80

dom-reduce

Reducible DOM APIs
JavaScript
5
star
81

taskhub

github based tasks app
JavaScript
5
star
82

http-streamer

streamer 4 http
JavaScript
5
star
83

lt.plugins.bracketglow

Light Table plugin that adds visual glowing pulse to a (un)matching brackets
CSS
5
star
84

maybe.flow

Library for representing values that may or may not exist
JavaScript
5
star
85

lt.plugins.palette

Light Table plugin for visualizing CSS color values
JavaScript
5
star
86

fs-streamer

streamer 4 fs
JavaScript
4
star
87

photoroom

Lightroom plugin for open photo
Lua
4
star
88

powerset

Utility for creating a powerset in form of array
JavaScript
4
star
89

reflex-todo-mvc

TodoMVC app implementation in reflex
JavaScript
4
star
90

decentralized-web

Nursery of ideas for decentralized web
4
star
91

reflection-js

JS reflection APIs
JavaScript
4
star
92

signalize

Signals in a functional reactive style
JavaScript
4
star
93

tape-ext

A command line tool (inspired by tape) to test web extensions
JavaScript
4
star
94

ace-teleported

Demo ace package
JavaScript
4
star
95

phantomify

Phantomify lets you headlessly browserify
JavaScript
4
star
96

syntax-stroop

Syntax Stroop is a syntax highlighter module for ServerJS
JavaScript
3
star
97

tree-reduce

Reducible tree walk for an arbitrary data structures
JavaScript
3
star
98

value-result

Library for managing results of computation that may fail
JavaScript
3
star
99

models

M form MVC
JavaScript
3
star
100

.vim

my vim config
Vim Script
3
star