-
Notifications
You must be signed in to change notification settings - Fork 44
Official spec for transformer protocol #20
Comments
This is a very good point. I'm going to open a new PR on Ramda to remove the Symbol until this is worked out. It's not being exposed, nor documented, nor otherwise used internally other than to initialize |
Prompted by cognitect-labs/transducers-js#20 from @tgriesser, removing the internal transformer symbol until issues are worked out. This symbol is currently only supported in jlongster/transducers.js and interoperability is questionable as (Symbol('transformer') !== Symbol('transformer')). This feature was not documented, nor do the tests depend on it, and only effects an undocumented feature of into. Better to remove it now until details are worked out. If an "official spec" is agreed upon, I will open a new PR.
Prompted by cognitect-labs/transducers-js#20 from @tgriesser, removing the internal transformer symbol until issues are worked out. This symbol is currently only supported in jlongster/transducers.js and interoperability is questionable as (Symbol('transformer') !== Symbol('transformer')). This feature was not documented, nor do the tests depend on it, and only effects an undocumented feature of into. Better to remove it now until details are worked out. If an "official spec" is agreed upon, I will open a new PR.
Prompted by cognitect-labs/transducers-js#20 from @tgriesser, removing the internal transformer symbol until issues are worked out. This symbol is currently only supported in jlongster/transducers.js and interoperability is questionable as (Symbol('transformer') !== Symbol('transformer')). This feature was not documented, nor do the tests depend on it, and only effects an undocumented feature of into. Better to remove it now until details are worked out. If an "official spec" is agreed upon, I will open a new PR.
Seems to me the logic should be more like the following: var TRANSFORMER = null;
if(typeof Symbol != "undefined") {
TRANSFORMER = Symbol.for("cognitect/transformer");
} else {
TRANSFORMER = "@@cognitect/transformer";
} Globally stealing names seems like a really bad idea. |
Aren't we looking for something which behaves similar to I agree that stealing top-level names via |
@tgriesser I'm suggesting that Symbols are the right path forward, using var ITransducer = {
reduced: Symbol.for("cognitect/reduced"),
transformer: Symbol.for("cognitect/transformer"),
/* ... */
}; This seems like a good approach to me. Perhaps this is what @jlongster already did? |
I thought I had done that but I know I reworked the protocol in v2 of my lib so I must have dropped that: https://github.com/jlongster/transducers.js/blob/master/transducers.js#L8
|
I would suggest maybe we could have a separate library that defines these symbols that we all use, but that requires an equality check. And because npm loves to duplicate libraries, I don't think that will really work. Bah. |
@jlongster |
Yeah, this is the main thing I'm looking for, just a way of making it library agnostic... Then the second concern was whether the value is a function which defines/returns the transformer (which makes things a bit more flexible and is similar to an iterator), or just a plain object. |
Libraries could do the following: var stringProp = "@@cognitect/transformer",
symProp = typeof Symbol != "undefined" ? Symbol.for("cognitect/transformer") : stringProp;
x[stringProp] = x[symProp] = function() {
/* ... */
}; |
Ah I see. Semantically I didn't see any benefits but that's true.
Is using |
Also I'm personally not against |
Afaik, yes.
Agreed, the benefit here is that if you're eventually able to subclass vector, you would only need to define the transformer once at the top-level Vector object and it'd be inherited properly (and create one of that instance when |
Here's an idea: we break apart the object into three functions on the prototype: SomeObject.prototype['@@transducer/init'] = function() {
return new this.constructor();
};
SomeObject.prototype['@@transducer/step'] = function(result, arr) {
return result.set(arr[0], arr[1]);
};
SomeObject.prototype['@@transducer/result'] = function(obj) {
return obj;
}; If we are going to namespace the symbols anyway. If that |
Also with the above I think 95% of data structures would just need to implement the |
That approach sounds great to me!
I wouldn't mind - though if performance is what we're after, I don't believe the latest benchmarks are leaning in favor of symbols (for now). |
@jlongster I'm assuming we also want |
|
@jlongster well transducers need to detect it, wether they use a library function to do it shouldn't matter. Just saying that if we're defining the required properties it seems they should all follow the same naming convention. Speaking of which it seems to me we probably need to supply the name to get the reduced value, |
@swannodette This protocol is for implementing the "bottom" transducer though in a data structure so that's it's "transducable". Detecting if something is reduced isn't data structure-specific, right? I guess I have a hard time seeing why this (https://github.com/jlongster/transducers.js/blob/master/transducers.js#L111) belongs here Edit: oops messed up line numbers |
@jlongster but that's obviously broken right if not handled carefully? If you wrap your value in your own Reduced how can a different transducer from a different library detect it and extract the value? Sorry for not being clear, I think the protocol needs to be more general than the bottom transducer otherwise we're not all going to be able to compose the way we want. |
@swannodette That's what the |
@jlongster yep just saying we should rename to make this whole thing uniform. So the protocol is not just about the transformers, but how to deal with reduced values as well. Then you can really mix and match libraries. |
Absolutely! Sorry for the confusion. So we're in agreement?
|
@jlongster I think the value should be stored in |
I would like that, but would rather use different function signature |
I would also like to propose that target[Symbol.for("transducer/reduce")](step, initial, collection) Note that |
I was thinking about it a little differently. The idea is that the reducible object does not need to be concerned with transducers or transformers. It would be defined similar to function reduce(xf, init, coll){
if(isArray(coll)){
return arrayReduce(xf, init, coll)
}
if(isFunction(coll[Symbol.for('transducer/reduce')])){
return methodReduce(xf, init, coll)
}
// whatever else
return iteratorReduce(xf, init, coll)
}
function methodReduce(xf, init, coll){
var result = coll[Symbol.for('transducer/reduce')](function(result, value){
return xf.step(result, value)
}, init)
return xf.result(result)
} |
... And the reducible object would just have to be concerned with implementing a contract similar to function eduction(t, coll) {
return new Eduction(t, coll)
}
function Eduction(t, coll){
this.t = t
this.coll = coll
}
Eduction.prototype[Symbo.iterator] = function(){
return sequence(this.t, this.coll)[Symbol.iterator]()
}
Eduction.prototype[Symbol.for('transducer/reduce')] = function(rf, init){
return transduce(this.t, rf, init, this.coll)
} |
@kevinbeaty going to leave |
Implemented and released. Thanks everyone! |
Same here! My project conforms. |
https://github.com/gozala/transducers also uses this interface ;) |
Support Official Transformer Protocol cognitect-labs/transducers-js#20
@swannodette if you recall, this was something I had asked about and while I don't recall the exact reason for not having a transformer protocol implemented in transducers-js, I do remember there was a reason.
I've been thinking about it a bit lately, and I do think it's worthwhile to be able to define, particularly for JavaScript where it's much simpler to bake this into the prototype rather than defining as a lookup map of handlers as in transit-js.
As more libraries in JavaScript begin implementing this protocol (this was prompted by @kevinbeaty's excellent transducer PR on the ramda.js project), I wanted to see if there could be an agreed upon spec for the
transformer
before things get too far along.This is the implementation kicked off by @jlongster
For starters - the use of a
Symbol('transformer')
is problematic asSymbol()
creates a unique value (Symbol('transformer') !== Symbol('transformer')
), and so you lose any interop when defined independently in multiple libraries.I'd propose all transformer protocols be implemented as a
@@transformer
string (similar to@@iterator
) until if/when the transformer is officially recognized in the well known symbols list.I'd also propose that the spec behave similarly to an
iterator
, in that it is a function which returns thetransformer
, rather than as just an object proposed above:This makes the implementation more useful, as it can refer to the current object in the
init
to know what value makes toinit
, should the object be subclassed:The text was updated successfully, but these errors were encountered: