Skip to content

Producer.produceTo and Consumer #24

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

Closed
benjchristensen opened this issue Apr 15, 2014 · 9 comments
Closed

Producer.produceTo and Consumer #24

benjchristensen opened this issue Apr 15, 2014 · 9 comments

Comments

@benjchristensen
Copy link
Contributor

What is the reason for the API not using the same subscription model as the SPI? I believe it's about trying to allow libraries to be as generic as possible, but it seems to have gone to an extreme that loses all point of having types since these communicate little to nothing (particularly the empty Consumer type).

The Producer.produceTo(consumer) signature does not communicate lifecycle at all (since Consumer has no methods on it) and this is a critical aspect of a stream since terminal states (onComplete and onError) can and will occur.

If the intent is to have Producer/Consumer types exposed to library users, at this point they are so generic as to be useless in communicating any intent. Thus, each library will need to hide these behind their own interfaces and they become little different than the SPI.

In short, what's the point of the API types (Consumer and Producer) in their current form?

@benjchristensen
Copy link
Contributor Author

For the record, I have read the README :-)

API components

The purpose of the API is to provide the types that users interact with directly. SPI methods and interfaces should not be exposed expect for the purpose of writing Reactive Streams implementations.

The API counterpart for Publisher is Producer and for Subscriber is Consumer. The combination of these two—a stream processing element with asynchronous input and output—is called Processor.

The only operation supported by any Producer–Consumer pair is their ability to establish a connection for the purpose of transferring the stream of elements from Producer to Consumer; this is achieved by the method produceTo(). Concrete implementations of Reactive Streams are expected to offer a rich set of combinators and transformations, but these are not the subject of this specification. The reason is that implementations shall have the freedom to formulate the end-user API in an idiomatic fashion for the respective platform, language and use-case they target.

In addition there is one method each on Producer and Consumer to obtain a reference to the underlying Publisher or Subscriber, respectively. These are necessary for implementations, but is not to be considered end-user API.

@benjchristensen
Copy link
Contributor Author

Perhaps I can clarify what I'm trying to say ... since the reason for the API types being anemic is to not hinder the libraries from creating their own idiomatic interfaces, why bother having the Consumer and Producer types anyways? They are not needed for a library to connect "Producer to Consumer". In RxJava it will continue to be rx.Observable and rx.Subscriber and neither of these are going to start implementing the Producer and Consumer types.

Interoperability between libraries will still require exposing the SPI types (Publisher, Subscriber and Subscription), so why not just leave it at those types and let libraries hide or expose them however much they wish?

@rkuhn
Copy link
Member

rkuhn commented Apr 15, 2014

I believe I answered that in #23 already. The reason for not ever showing Publisher/Subscription/Subscriber to the end users is that neither implementing nor calling them is desirable for that audience, which gives rise to the need for user-facing names for these elements. Consumer might not have any methods, but it does have extremely clear semantics, also concerning lifecycle management.

One difference to Rx is that Reactive Streams do not allow management “from the outside”: once a stream has been set in motion, it can only flow or terminate from the inside (or its ends).

@benjchristensen
Copy link
Contributor Author

The reason for not ever showing Publisher/Subscription/Subscriber to the end users

Except that it does show it to the end users.

The Consumer type exposes Subscriber:

public interface Consumer<T> {

  /**
   * Get the underlying {@link org.reactivestreams.spi.Subscriber Subscriber} for this Consumer. This method should only be used by
   * implementations of this API.
   * @return the underlying subscriber for this consumer
   */
  public Subscriber<T> getSubscriber();
}

and the Producer type exposes Publisher:

public interface Producer<T> {

  /**
   * Get the underlying {@link org.reactivestreams.spi.Publisher Publisher} for this Producer. This method should only be used by
   * implementations of this API.
   * @return the underlying publisher for this producer
   */
  public Publisher<T> getPublisher();

  /**
   * Connect the given consumer to this producer. This means that the
   * Subscriber underlying the {@link org.reactivestreams.api.Consumer Consumer} subscribes to this Producer’s
   * underlying {@link org.reactivestreams.spi.Publisher Publisher}, which will initiate the transfer of the produced
   * stream of elements from producer to consumer until either of three things
   * happen:
   * <p>
   * <ul>
   * <li>The stream ends normally (no more elements available).</li>
   * <li>The producer encounters a fatal error condition.</li>
   * <li>The consumer cancels the reception of more elements.</li>
   * </ul>
   * @param consumer The consumer to register with this producer.
   */
  public void produceTo(Consumer<T> consumer);
}

Saying those methods "should only be used by implementations of this API" does not change the fact that they are exposed and in fact the only things exposed so users will see these and use them if they come looking since they are what actually communicate the contract, not Consumer/Producer/

@rkuhn
Copy link
Member

rkuhn commented Apr 15, 2014

The question is not what is suboptimal about the current solution, more interesting is whether you have a better idea? I would love to hide these methods, but given Java’s visibility primitives I do not see a way to achieve that.

@smaldini
Copy link
Contributor

I kind of support the idea of only having publisher/subscriber. I don't know for you but in reactor we have a few places where we just implement both subscriber and consumer for instance. Also there is a case for avoiding confusion, when you read the tck and general explanation it seems easily confused.

E.g the tck doc states than Processor is just a Publisher AND a Subscriber. Shouldn't it be both a Consumer and Producer ?

Sent from my iPhone

On 15 Apr 2014, at 16:51, Roland Kuhn [email protected] wrote:

The question is not what is suboptimal about the current solution, more interesting is whether you have a better idea? I would love to hide these methods, but given Java’s visibility primitives I do not see a way to achieve that.


Reply to this email directly or view it on GitHub.

@benjchristensen
Copy link
Contributor Author

The question is not what is suboptimal about the current solution, more interesting is whether you have a better idea?

I'm suggesting eliminating Consumer/Processor/Producer and just directly using Publisher/Subscriber/Subscription since these types are intended purely for library interop and not exposing to users.

These types end up being exposed anyways so let's just be clear and make them the public API/SPI for interop instead of having a level of indirection.

@rkuhn
Copy link
Member

rkuhn commented Apr 17, 2014

Close this one as well?

@benjchristensen
Copy link
Contributor Author

This conversation has long since been superseded by discussions in #37 and #19

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

3 participants