Closed rpominov closed 9 years ago
Certainly. Right now map
/filter
/etc just return a function that take another reducer. Usually if you are calling it manually you are giving it the "bottom" reducer that actually appends it to the data structure.
For example:
var trans = map(x => x + 1);
[1, 2, 3, 4].reduce(trans(function(result, x) {
result.push(x);
}), []);
(untested code, sorry.) This is likely going to change, however, because there are a few properties of transducers that we are missing, and it may become more like an object with a few methods. Will resolve that within the next week or two.
Thanks! I will wait for final spec for transducer object.
I think, it would be best for JavaScript ecosystem if we'd agree on some library independent transducers protocol. The easiest way to do that is just port as close as we can original Rich's specification.
Fortunately it quite easy in JavaScript. With exception for Reduced
wrapper. In clojure they already have global reduced
, which they can use in transducers, but in JavaScript we don't. Using a class as reduced
wrapper instantly makes transducers library dependent, which is not good.
I propose use simple object as a reduced
wrapper, i.e. {__transducersReduced__: true, val: val}
(of course you can add helper functions reduced()
and isReduced()
to work with it).
If we'd agreed on library independent transducers protocol, it would be easy, for example, for underscore and LoDash to support transducers. Then, for example, FRP libraries could implement transducers support as a protocol and not as a support of transducers.js library.
I think it would be very cool to have such ability for different libraries to work together via transducers protocol.
+1 I agree, if we follow Rich's spec from clojure as closely as possible, that would be great.
Now the Cognitect has release their own transducers lib, this discussion is even more important. This will I will think about this and see how we can work with them.
fwiw, I tweaked my transducers to produce objects with the same API as theirs, so in theory they should be swappable. We had actually converged on basically the same things (I only had to change finalize
to result
).
Just read this over again, and yes, we have hit this problem over in https://github.com/ubolonton/js-csp. Reduced
is the only problem, as we need to do a direct instanceof
check on that class.
This library follows the semantics of Clojure's reducers 100% (as far as I know), but we need to form our own standards for JS APIs. I'm not sure what that will look like. @pozadi's idea of having an object with a special property might work. It depends on how Cognitect wants to do it; hopefully they are open to standardizing some sort of protocol.
+1 for the object with a special property.
A heads up, we've chosen __transducers_reduced__
as the property.
I think, we should also agree on the name of property where wrapped value will be stored.
@pozadi I think that key stores the value also: {__transducers_reduced__: val}
.
Yes the property name is the only thing required
Now I understand. Thanks!
+1. Will update my lib soon
I just thought it would be much faster to check x && x.__transducers_reduced__
than '__transducers_reduced__' in x
.
@pozadi yes, probably won't do the in
check. Just need to make sure the property access doesn't hurt the JIT also.
But wrapped result might be any value including false
, null
, undefined
etc. The in
check is the only option here, as far as I understand.
True. It doesn't make much sense to early terminate with a falsey value though... @swannodette is that true? Can it just be part of the protocol that the value on __transducers_reduced__
must be truthy?
I have to focus on work right now so these are just quick thoughts. Can't respond much more until later.
@jlongster not sure what is under discussion here.
This is our isReduced
check:
transducers.isReduced = function(x) {
return (x instanceof transducers.Reduced) || (x && x.__transducers_reduced__);
};
I benchmarked this - there's no interesting performance degradation as far as I could tell.
@swannodette cool. @pozadi's point was that since we store the value in __transducers_reduce__
, if the value is false
it will fail, but I don't think that really matters it just might be worth mentioning in the protocol docs that Reduced
only works with truthy values. (assuming we're not doing instanceof
on our own type since that doesn't work across libs)
Ah sorry I understand now. Reduced values can definitely be false-y. I've changed transducers-js so that __transducers_reduced__
is now a boolean flag, and the value is stored in value
.
Awesome
I really like the new transducer protocol! Just used it to add transducers support to my small FRP lib. Ironically I didn't even had to use the __transducers_reduced__
property.
Here is the complete implementation in my library if anyone interested: https://github.com/pozadi/kefir/blob/6b5fe3ebc3394a1fb161b3aa774edde1a08d83c2/src/one-source.js#L85-128
Is it possible to use a transducer without provided functions
sequence
,transduce
, orinto
? I.e. is there any public API for objects returned bymap()
,filter()
, etc.?