-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add fluent API #137
Comments
@Irisjay, this bothered me as well. As far as I remember, I ended up using There was a package that allows to turn any fp-interface to fluent, but I can't find it in my stars, nor in @paldepind stars. There was a mention of it somewhere, but I can't find id, so sad. It takes and object of |
That sounds great! Let me look for it. But does the package allow chaining, as in, is a thru-ed stream capable of being thru-ed again? That is the essential thing, I think. I also updated the imaginary implementation 2, to accomodate for not wrapping the arguments in []. It appeared to be a common, if trivial mistake. Asides, how do you think this issue relates to the aesthetics of flyd? tbh I think a fluent interface comes hand-in-hand with the functional argument order and modularized combinators. Especially annoying about the lack of fluent interface here is that .map is in the fluent interface-ish order, wheras everything is opposite. |
As I said before, I miss fluent sometimes. And I use fluent extensively on native |
The thing is that fluent api is more than some remnant of OOP that some user of this FP library somehow requests, just for him to remain in familiar territory. A fluent api as in a |
I second with @StreetStrider to use Ramda.pipe (equivalent to _.flow) and currying in this situation. flyd already has a data/stream in last position API. The code layout becomes nearly same as with fluent apis, but gets the additional benefit of being more declarative without introducing a feature coming from an OOP style.
|
When I wrote Flyd I was very much against methods. But these days I definitely think that fluent APIs do have their benefits. I think the biggest issue is that Flyd tries to be modular. But, with a fluent API all methods will have to be available at the prototype. I think @squiddle's suggestion of using
That sounds very interesting! Definitely share it if you find it. |
@StreetStrider, I just had a though that maybe it's dot-compose you're thinking of? |
@paldepind, ye, that's it. ☝️ |
I would add my vote to support the fluent version. I found it for instance, more elegant to write Would anyone know how to achieve it? Another interesting advantage is when dealing with unknown or parametrized factory, or even with different factories at the same time. Say I am using third party library also providing streams, with possibly different api. But with fluent style, I can write Also perhaps the |
@dmitriz It's not really feasible. Adding methods is best done by modifying a prototype. But, a flyd stream is a function. That is why you can do |
What about transforming the Then you would set all the instance methods on that object: const protoObj = {
scan: (...args) => scan(...args, this)
} Here is a real situation, taken from https://github.com/dmitriz/un/blob/master/index.js, where the fluent syntax might be superior. The fluent version would have the linear flow starting from the stream at the top: actionStream
.scan(reducer, initState)
.map(state => view(createElements)(state, actions)),
.map(vnode => createRender(el)(vnode)) Note how explicit and easy to see are the values entering each function. In contrast, the R.pipe(
actions => flyd.scan(reducer, initState, actions),
flyd.map(state => view(createElements)(state, actions), state)),
flyd.map(vnode => createRender(el)(vnode))
)(actionStream) This is no more linear as it requires jumping back and forth between the lines, |
This is the working code (with tests passing): /**
* Set prototype object for all instances created by the Factory
* (can be used to provide additional instance methods)
*/
const setInstanceProto = protoObj => Factory =>
(...args) => {
const factoryObj = Factory(...args)
let newObj = Object.create(protoObj)
Object.assign(newObj, factoryObj)
return newObj
} https://github.com/dmitriz/un/blob/master/adapters/factory.js |
Sharing my experience on how I'm facing this issue. Firstly, I don't understand why some of you are still mentioning changing prototypes being a hassle or whatever. Just look at my explanation of the kind of fluent API I proposed, and my proposed one-liner implementation. What I wanted was just one single method which is capable of dynamically calling any other function on the stream itself, so there will just be one single dispatching method to maintain, ever, which IMHO is pretty simple, and is the way several libraries such as I mentioned, mostjs, implemented it. To work around this, I'm honestly not that satisfied with the solution of using
And if you wanted to explicitly use/mutate something with the stream, just do:
Voila! |
#166 adds |
Often it's really annoying to rewrite long chained operations in an functional style. The https://github.com/mindeavor/es-pipeline-operator would help with this when it arrives, but for now, expressions like
flyd .on (..., filter (..., filter (..., target_range)) .map (...))
are absolutely unreadable, especially with complex functions in between. I switched from most.js, and their style oftarget_range .filter (...) .filter (...) .map (...) .observe (...)
is much more pleasing and easier to use. I propose to add the following line to amend this problem.or perhaps
Which would also allow usages like
stream.thru(filter,[isOdd]).map(square).thru(flyd.transduce, R.take(5))
The text was updated successfully, but these errors were encountered: