Skip to content

Commit

Permalink
Merge pull request #27 from transduce/remove-transformer-symbol
Browse files Browse the repository at this point in the history
Support Official Transformer Protocol cognitect-labs/transducers-js#20
  • Loading branch information
kevinbeaty committed Apr 4, 2015
2 parents 5d480c5 + 9262f20 commit dd0fb65
Show file tree
Hide file tree
Showing 49 changed files with 617 additions and 878 deletions.
97 changes: 61 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ A two arity function, `rf`, appropriate for passing to `reduce`. The first argum
The initial accumulator value, `init` to use with Reduce.

##### Transformer
An object that provides a reducing function, `step`, initial value function, `init`, and result extraction function, `result`. Combines the steps of reduce into a single object.
An object that provides a reducing function, `@@transducer/step`, initial value function, `@@transducer/init`, and result extraction function, `@@transducer/result`. Combines the steps of reduce into a single object.

##### Reduce
A function that folds over an input source to produce an Output Source. Accepts a Reducing Function or Transformer, `xf`, as the first argument, an optional initial value, `init`, as the second argument and an Input Source, `coll` as the third argument.

Also known as `foldLeft` or `foldl` in other languages and libraries.

The function begins with calling the Reducing Function of `xf`, `step`, with the initial accumulator, `init`, and the first item of Input Source, `coll`. The return value from the reducing function is used as the next accumulator with the next item in `coll`. The process repeats until either `coll` is exhausted or `xf` indicates early termination with `reduced`. Finally, the result extraction function of `xf`, `result`, is called with the final accumulator to perform potentially delayed actions and optionally convert the accumulator to the Output Source.
The function begins with calling the Reducing Function of `xf`, `@@transducer/step`, with the initial accumulator, `init`, and the first item of Input Source, `coll`. The return value from the reducing function is used as the next accumulator with the next item in `coll`. The process repeats until either `coll` is exhausted or `xf` indicates early termination with `reduced`. Finally, the result extraction function of `xf`, `@@transducer/result`, is called with the final accumulator to perform potentially delayed actions and optionally convert the accumulator to the Output Source.

Reduce defines a Transducible Process.

Expand Down Expand Up @@ -134,10 +134,17 @@ unreduced: function(value)
completing: function(rf, result?)
transformer: function(value)
iterable: function(value)
transducer: function(step?, result?, init?)

protocols: {
iterator: Symbol.iterator || '@@iterator'
transformer: Symbol('transformer') || '@@transformer'
transducer: {
init: '@@transducer/init',
step: '@@transducer/step',
result: '@@transducer/result',
reduced: '@@transducer/reduced',
value: '@@transducer/value'
}
}

// transducers
Expand All @@ -155,7 +162,6 @@ partitionBy: function(f)
dedupe: function()
unique: function(f?)
tap: function(interceptor)
transformStep: function(xfStep)

array {
forEach: function(callback)
Expand Down Expand Up @@ -287,8 +293,57 @@ The iterable is an object that has a function identified by `protocols.iterator`
##### protocols.iterator
Symbol (or a string that acts as symbols) for `@@iterator` you can use to configure your custom objects.
##### protocols.transformer
Symbol (or a string that acts as symbols) for [`@@transformer`][10] you can use to configure your custom objects.
##### protocols.transducer
String that acts as symbols for supporting the transducer protocol. Used to define transformers and reduced values.
##### transducer(step?, result?, init?)
Creates a transducer from a reducing function, `step`, result extraction function `result` and initial value function `init`. If any function is `null` or `undefined`, default is to forward directly to wrapped transformer.
The `@@transducer/step` function of the resulting transformer calls `step(xfStep, result, input)` bound to an empty context, where `xfStep` calls `step` on the wrapped transformer. The `init` and `result` functions behave similarly.
All functions are called with a bound context (`this` parameter) that contains `init`, `step` and `result` functions that forward to wrapped transformer. The context can be used to implement stateful transducers.
```javascript
// Map from a step function
function map(callback) {
return transducer(function(step, value, input) {
return step(value, callback(input))
})
}

// using reduced
function takeWhile(p){
return transducer(function(step, value, input){
return p(input) ? step(value, input) : reduced(value)
})
}

// using context for stateful transducers
function drop(n){
return transducer(function(step, value, item){
if(this.n === void 0) this.n = n
return (--this.n < 0) ? step(value, item) : value
})
}

// using custom result
function some(predicate) {
return transducer(
function(step, value, input){
if(predicate(input)){
this.found = true
return reduced(step(value, true))
}
return value
},
function(result, value){
if(!this.found){
value = this.step(value, false)
}
return result(value)
})
}
```
#### Transducers
Common transducers mixed into `transduce` directly or available by explictly requiring from `transduce/transducers`. The following are equivalent:
Expand Down Expand Up @@ -338,36 +393,6 @@ Produce a duplicate-free version of the transformation. If `f` is passed, it wil
##### tap(interceptor)
Transducer that invokes interceptor with each result and input, and then passes through input. The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain. Executes interceptor with current result and input.
##### transformStep(xfStep)
Creates a transducer from a function called on every `step`. The `step` function of the transducer delegates to `xfStep(xf, result, input)` bound to an empty context. This is useful for creating custom transducers defined by a step function. `init` returns `xf.init()` and `result` returns `xf.result(result)`.
```javascript
// Map from a step function
function map(f) {
return tr.transformStep(function(xf, result, input){
return xf.step(result, f(input))
})
}
// using reduced
function takeWhile(p){
return tr.transformStep(function(xf, result, item){
return p(item) ? xf.step(result, item) : tr.reduced(result)
})
}
// using context for stateful transducers
function drop(n){
return tr.transformStep(function(xf, result, item){
if(this.n === void 0) this.n = n
if(--this.n < 0){
result = xf.step(result, item)
}
return result
})
}
```
#### Array
Use Array methods as Transducers. Treats each stepped item as an item in the array, and defines transducers that step items with the same contract as array methods.
Expand Down
43 changes: 16 additions & 27 deletions array/every.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,21 @@
'use strict'
var reduced = require('../core/reduced')
var transducer = require('../core/transducer'),
reduced = require('../core/reduced')

// Determine whether all of the elements match a truth test.
// Early termination if item does not match predicate.
module.exports =
function every(predicate) {
return function(xf){
return new Every(predicate, xf)
}
}
function Every(f, xf) {
this.xf = xf
this.f = f
this.found = false
}
Every.prototype.init = function(){
return this.xf.init()
}
Every.prototype.result = function(result){
if(!this.found){
result = this.xf.step(result, true)
}
return this.xf.result(result)
}
Every.prototype.step = function(result, input) {
if(!this.f(input)){
this.found = true
return reduced(this.xf.step(result, false))
}
return result
return transducer(
function(step, value, input){
if(!predicate(input)){
this.found = true
return reduced(step(value, false))
}
return value
},
function(result, value){
if(!this.found){
value = this.step(value, true)
}
return result(value)
})
}
28 changes: 8 additions & 20 deletions array/find.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
'use strict'
var reduced = require('../core/reduced')
var transducer = require('../core/transducer'),
reduced = require('../core/reduced')

// Return the first value which passes a truth test. Aliased as `detect`.
module.exports =
function find(predicate) {
return function(xf){
return new Find(predicate, xf)
}
}
function Find(f, xf) {
this.xf = xf
this.f = f
}
Find.prototype.init = function(){
return this.xf.init()
}
Find.prototype.result = function(result){
return this.xf.result(result)
}
Find.prototype.step = function(result, input) {
if(this.f(input)){
result = reduced(this.xf.step(result, input))
}
return result
return transducer(function(step, value, input){
if(predicate(input)){
value = reduced(step(value, input))
}
return value
})
}
26 changes: 8 additions & 18 deletions array/forEach.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
'use strict'
var transducer = require('../core/transducer')

// Executes f with f(input, idx, result) for forEach item
// passed through transducer without changing the result.
module.exports =
function forEach(f) {
return function(xf){
return new ForEach(f, xf)
}
}
function ForEach(f, xf) {
this.xf = xf
this.f = f
this.i = 0
}
ForEach.prototype.init = function(){
return this.xf.init()
}
ForEach.prototype.result = function(result){
return this.xf.result(result)
}
ForEach.prototype.step = function(result, input) {
this.f(input, this.i++, result)
return this.xf.step(result, input)
return transducer(function(step, value, input){
if(this.idx === void 0){
this.idx = 0
}
f(input, this.idx++, value)
return step(value, input)
})
}
51 changes: 23 additions & 28 deletions array/initial.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'
var isReduced = require('../core/isReduced'),
var transducer = require('../core/transducer'),
isReduced = require('../core/isReduced'),
unreduced = require('../core/unreduced')

// Returns everything but the last entry. Passing **n** will return all the values
Expand All @@ -8,31 +9,25 @@ var isReduced = require('../core/isReduced'),
module.exports =
function initial(n) {
n = (n === void 0) ? 1 : (n > 0) ? n : 0
return function(xf){
return new Initial(n, xf)
}
}
function Initial(n, xf) {
this.xf = xf
this.n = n
this.idx = 0
this.buffer = []
}
Initial.prototype.init = function(){
return this.xf.init()
}
Initial.prototype.result = function(result){
var idx = 0, count = this.idx - this.n, buffer = this.buffer
for(idx = 0; idx < count; idx++){
result = this.xf.step(result, buffer[idx])
if(isReduced(result)){
result = unreduced(result)
break
}
}
return this.xf.result(result)
}
Initial.prototype.step = function(result, input){
this.buffer[this.idx++] = input
return result
return transducer(
function(step, value, input){
if(this.buffer === void 0){
this.n = n
this.idx = 0
this.buffer = []
}
this.buffer[this.idx++] = input
return value
},
function(result, value){
var idx = 0, count = this.idx - this.n, buffer = this.buffer
for(idx = 0; idx < count; idx++){
value = this.step(value, buffer[idx])
if(isReduced(value)){
value = unreduced(value)
break
}
}
return result(value)
})
}
59 changes: 27 additions & 32 deletions array/last.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'
var isReduced = require('../core/isReduced'),
var transducer = require('../core/transducer'),
isReduced = require('../core/isReduced'),
unreduced = require('../core/unreduced')

// Get the last element. Passing **n** will return the last N values.
Expand All @@ -11,35 +12,29 @@ function last(n) {
} else {
n = (n > 0) ? n : 0
}
return function(xf){
return new Last(n, xf)
}
}
function Last(n, xf) {
this.xf = xf
this.n = n
this.idx = 0
this.buffer = []
}
Last.prototype.init = function(){
return this.xf.init()
}
Last.prototype.result = function(result){
var n = this.n, count = n, buffer=this.buffer, idx=this.idx
if(idx < count){
count = idx
idx = 0
}
while(count--){
result = this.xf.step(result, buffer[idx++ % n])
if(isReduced(result)){
result = unreduced(result)
break
}
}
return this.xf.result(result)
}
Last.prototype.step = function(result, input){
this.buffer[this.idx++ % this.n] = input
return result
return transducer(
function(step, value, input){
if(this.buffer === void 0){
this.n = n
this.idx = 0
this.buffer = []
}
this.buffer[this.idx++ % this.n] = input
return value
},
function(result, value){
var n = this.n, count = n, buffer=this.buffer, idx=this.idx
if(idx < count){
count = idx
idx = 0
}
while(count--){
value = this.step(value, buffer[idx++ % n])
if(isReduced(value)){
value = unreduced(value)
break
}
}
return result(value)
})
}
Loading

0 comments on commit dd0fb65

Please sign in to comment.