pull-stream / pull-stream-examples

MIT License
96 stars 8 forks source link

Simplified `values` function with basic example #7

Closed dmitriz closed 5 years ago

dmitriz commented 6 years ago

Just an obvious simplification and really basic example to see how pulling data works, could be helpful for a beginner trying to understand how it work.

I wonder if you would accept this PR and similar style simplification for other functions?

dmitriz commented 5 years ago

Is this project dead?

dominictarr commented 5 years ago

oh thats quite nice, but some people file ternaries and arrow functions confusing.... I think it's valuable to have another example of how you can write the same thing (although your version mutates the array) but I'm not sure one is better than the other. I'd be happy to either also include your versions, or link to your repo written in your style.

it's not dead, it's just finished. I get lots of issue emails, and didn't notice your first one go past, sorry

dmitriz commented 5 years ago

Hi, Thank you for your comment, please see the new https://github.com/dominictarr/pull-stream-examples/pull/8, let me know if that is ok.

I mention pull-streams as an inspiration in my new library here: https://github.com/dmitriz/cpsfy/blob/HEAD/DOCUMENTATION.md#pull-streams

Would love to hear your opinion on it!

dmitriz commented 5 years ago

Just noticed you mentioned my version mutates the array, will look into it...

dmitriz commented 5 years ago

oh thats quite nice, but some people file ternaries and arrow functions confusing....

This is interesting to hear, because I am often rewriting code with arrows and ternaries for myself to make it more understandable and less confusing for me, I suppose these things are very subjective :)

dominictarr commented 5 years ago

yes, I guess I have spent a lot of time looking at function () {} so I'm very used to that. I like ternaries though, because they are expressions - I generally avoid using them when they do side effects though. I think it's valuable to look at a pattern and know "this has no side effects" value = test ? if_true : if_false etc. I don't think there is one code style that is "readable" for everyone

dominictarr commented 5 years ago

oh your CPS thing looks like the same pattern as https://www.npmjs.com/package/continuable your style works very well for this: opts => cb => async(opts, cb) !

dmitriz commented 5 years ago

oh your CPS thing looks like the same pattern as https://www.npmjs.com/package/continuable

Thanks, I noticed it also links to some of your projects, I wonder what happened to them...

I have been looking for related projects for inspiration and validations for cpsfy but only found operators accepting single callbacks. I feel it is significant limitation because it does not allow for the promise-style error handling at the end of the pipeline chain.

With promises you have the separate 2nd callback for error handling. However, I find it still limiting, because different kinds of errors require different handling that might not be convenient to handle in the same functions. It still forces me to bloat my error handling functions instead of separating concerns.

This was one of the motivation for cpsfy to allow any number of callbacks, and I wonder if there are any existing projects doing that.

your style works very well for this: opts => cb => async(opts, cb) !

Thanks, the idea was indeed to try to make it dead easy to drop any cb-function into cpsfy :)

dominictarr commented 5 years ago

hmm, async programming is really about error handling. so that makes me curious about your approach. for functional programming, I just want a consistent approach, say always callbacks etc...

but as every error is unique ... when you say any number of callbacks do you mean more the cb(err, value) pattern, but just subscribe more than one callback so that you can still use a cb for error handling and another for the happy path?

dmitriz commented 5 years ago

hmm, async programming is really about error handling. so that makes me curious about your approach. for functional programming, I just want a consistent approach, say always callbacks etc...

My main point is, JS functions are multi-argument by design, so we can embrace and exploit this existing functionality with relatively little efforts. Totally with you about consistency, all CPS functions have the same uniform signature:

(...calbacks) => { ... any code here ... }

My aim is to embrace only parts of what is associated with functional programming. One often mentioned principle is that of purity or side-effect-free, which is somewhat orthogonal here. As JS functions are allowed to be non-pure by design, so does the cpsfy API. It is then fully up to the user which functions to pass, side-effect-free or not. I think embracing side-effects can make the use broader and more powerful without sacrificing the benefits.

Another FP part that I regard as orthogonal is typing. It is again up to user to type the functions or not, which cpsfy uniformly allows all JS functions that are untyped by design.

On the other hand, the compositional part of the FP is fully embraced, but it is unfortunately not as developed for multi-argument functions, and I am trying to fill this hole :)

but as every error is unique ... when you say any number of callbacks do you mean more the cb(err, value) pattern, but just subscribe more than one callback so that you can still use a cb for error handling and another for the happy path?

I mean writing your CPS functions like that:

const cpsFn = (onResult, onSyntaxError, onServerError) => { ... }

which extends the promise 2-callbacks-only approach. And yes, potentially the user is allowed to use any number of callbacks, just as JF by design allow any number of arguments, and the cpsfy operators should still work consistently.

So instead of single callback call cb(err, value) you would have separately either onResult(value) or onError(err) getting called. I find this makes my functions simpler and more focused, no more error handling in the same callback function. And I can handle all my errors at the end like with promises.

And taking this to the next level, you can use any number of callbacks with any number of arguments. So if you have two result values, just pass them directly as onResults(res1, res2) rather than wrapping and unwrapping in arrays or objects.

I have been trying to write some comprehensive introduction in https://github.com/dmitriz/cpsfy/blob/master/DOCUMENTATION.md but imagine it is still full of confusion points and greatly appreciate any feedback how to make it more understandable and useful.

dmitriz commented 5 years ago

Closing in favour of https://github.com/pull-stream/pull-stream-examples/pull/8