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

New API to work with single value streams #667

Closed
leonid-s-usov opened this issue Aug 17, 2018 · 3 comments
Closed

New API to work with single value streams #667

leonid-s-usov opened this issue Aug 17, 2018 · 3 comments

Comments

@leonid-s-usov
Copy link
Contributor

leonid-s-usov commented Aug 17, 2018

This issue is created in an attempt to help proceed with the discussion happening in the Stream of one event #661 proposal.

The issue is around providing a better API for cases like

public protocol ChatService {
    func listAll() -> S<[Conversation], ChatError>
    func fetchMessages(for conversation: Conversation.ID) -> S<[Message], ChatError>
}

Today RAS gives us two options to put instead of S: Signal and SignalProducer. Both options aren't ideal: Signal isn't fine for the reason that there are no guarantees that we will not miss the result. SignalProducer is the only real option but it doesn't fit the semantics of the protocol - in order for SignalProducer to fit the protocol should be changed to

public protocol ChatServiceAccessor {
    func allLister() -> SignalProducer<[Conversation], ChatError>
    func messagesFetcher(for conversation: Conversation.ID) -> SignalProducer<[Message], ChatError>
}

Last but not the least - both Signal and SignalProducer fail totally at advertising that the observer will never receive multiple events - there will always be just one value, or an error (actually, there might be also an interruption, depending on whether we decide to allow it here)


During the discussion in #661 there was a lot said about the wanted cold / warm nature of the suggested new API but I believe that the best way of summarising the effort there would be to copy here a set of requirements in front of the potential API

  1. We want a concrete type (or several related types) which guarantees to return only a single value, or an error
  2. We want to have a cold single value stream to control when and how the operation is initiated
  3. We want to have a warm single value stream to await for the value without caring about when and how the operation has been initiated, similar to how Future or Promise works
  4. We want a clearer (and more efficient) flattenMap strategy when chaining streams of single value
  5. We want other single value oriented operators, which would be more efficient, readable and safe
  6. We want to be able to easily convert the new classes back to a SignalProducer for use in some generic operators
@leonid-s-usov
Copy link
Contributor Author

leonid-s-usov commented Aug 18, 2018

See a POC implementation at #668
Usage highlights

var seed = 0

let seedProducer = SignalProducer<Int, NoError> { obs, _ in
    obs.send(value: seed)
}


let value = AsyncValueProducer(seedProducer)
    .map {
        // try changing the below between AsyncValue and AsyncValueProducer
        AsyncValueProducer($0 + 10)
    }
    .when(ready: { print("resolved with value \($0)") })
    
print("incrementing seed")
seed += 1

print("awaiting result")
    value.await(notify: { print("await notified with \($0)") })

@leonid-s-usov
Copy link
Contributor Author

leonid-s-usov commented Aug 23, 2018

@jjoelson wrote:

In #668, it seems the example would look almost exactly the same with a SignalProducer except the need to provide a flatten strategy is gone, on has been renamed when, and start has been renamed await. Isn't a major goal of a framework like ReactiveSwift to have a common vocabulary of types and operators that can represent streams of values over time? Bracketing off a particular subset of use cases and changing all of the names seems counter to the mission as I understand it.

The ability to have flatMap without a flatten strategy is a real gain, and I'd like to understand what other improvements like that can be had by introducing a new type to represent a single value stream. Apart from things like that, I think the public API for this new type ought to look as much like SignalProducer as possible.

I would agree with on / when, but start / await have different meaning in terms of the expected number of the callback invocations. Also, await has a side effect of actually starting the producer if called on an instance of AsyncValueProducer, but doesn't start anything if called on AsyncValue.

Having that said, personally I'm not married to any naming. My only strict criteria is that I believe if we choose to change something we need to be consistent and not create a mixture of patterns.
I think that if there is any functional difference in otherwise similar methods, these methods should be named differently.

@RuiAAPeres
Copy link
Member

Hello. 👋 Thanks for opening this issue. Due to inactivity, we will soft close the issue. If you feel that it should remain open, please let us know. 😄

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

2 participants