Skip to content
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

How to implement custom operators? #182

Open
flensrocker opened this issue Nov 9, 2024 · 6 comments
Open

How to implement custom operators? #182

flensrocker opened this issue Nov 9, 2024 · 6 comments

Comments

@flensrocker
Copy link
Contributor

My context: Angular application developer since v2, so I have seen various versions of rxjs etc. (but also forgot how it worked in the past).

In my applications I tend to write some custom operators to encapsulate some reusable behaviour.
How is the implementation story with this API?

Extending the prototype (something I'm not very familiar with) seems to be a source of conflict, if different libraries would like to provide custom operators.

rxjs introduced the pipe function to easily chain operators to new "higher" operators, which also helps me as a TypeScript user to get the right types further down the chain.

(Thanks for this API to all involved. I really appreciate this and am looking forward to finally be able to combine different libraries with their different observable implementations!)

@domfarolino
Copy link
Collaborator

I would love @benlesh's thoughts on this, as I learned about the possibly type issues with what was my default understanding of how developers would solve this: by patching the prototype.

@benlesh
Copy link
Collaborator

benlesh commented Nov 12, 2024

RxJS is already moving this direction. Version 8 (still alpha) added an rx function that effectively is just this:

function rx(source, ...fns) {
  return from(source).pipe(...fns);
}

So with this, RxJS 8 (once using native observable types) will have a function that is implemented like so:

function rx(source, ...fns) {
  const o = Observable.from(source);

  return fns.reduce((prev, fn) => fn(prev), o);
}

This allows you to write functions in the "pipeable" way and use them.

function mapTwice(fn) {
  return (source) => source.map(fn).map(fn)
}

function nevermindJustComplete() {
  return (source) => new Observable(subscriber => {
    source.subscribe({ next: () => subscriber.complete() }, { signal: subscriber.signal });
  });
}

rx(someObservable, mapTwice(x => x + x), nevermindJustComplete())

Of course, the native functions will need pipeable analogs, which a library like RxJS can provide.

@bakkot
Copy link
Contributor

bakkot commented Nov 12, 2024

For iterator helpers, I'm considering proposing an Iterator.prototype.into which is literally just Iterator.prototype.into = function (f) { return f(this); }.

Then you could do

array.values()
  .map(whatever)
  .into(function*(iter) {
    for (let item of iter) {
      yield item + 1;
    }
  })
  .filter(etc)

(Or .into any other function which takes an iterator or iteraable, of course.)

Something to consider here, maybe?

@flensrocker
Copy link
Contributor Author

Of course, the native functions will need pipeable analogs, which a library like RxJS can provide.

Or should they be static functions on Observable? Or would that be too confusing as it makes not really sense to provide them on the platform?

Looking forward to rxjs 8! 🙂
Since I will use rxjs anyway, this will be ok for me.

Thank you!

@ducin
Copy link

ducin commented Nov 16, 2024

How about providing a pipe method to the built-in API in order to make seamless extensibility with the ecosystem?

import { operatorX as operatorXLibA } from 'libA'
import { operatorX as operatorXLibB } from 'libB'

array.values()
  .map(whatever) // native
  .filter(whatever) // native
  .pipe(
    // either, or, or even both:
    operatorXLibA(whatever),
    operatorXLibB(whatever)
  )
  .map(whatever) // native

@LcsGa
Copy link

LcsGa commented Nov 17, 2024

How about providing a pipe method to the built-in API in order to make seamless extensibility with the ecosystem?

import { operatorX as operatorXLibA } from 'libA'
import { operatorX as operatorXLibB } from 'libB'

array.values()
  .map(whatever) // native
  .filter(whatever) // native
  .pipe(
    // either, or, or even both:
    operatorXLibA(whatever),
    operatorXLibB(whatever)
  )
  .map(whatever) // native

I'd like that too! Basic operators could be within the prototype + another method would let us create and use our own operators like we would with the rxjs pipe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants