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

Drop the label of SignalProducer initializers that take a sequence. #120

Merged
merged 3 commits into from
Dec 13, 2016

Conversation

andersio
Copy link
Member

@andersio andersio commented Nov 24, 2016

Since this is not a lossy conversion, and there is no conflicting signature, the signal label does not give any extra clarity at call site.

Copy link
Member

@NachoSoto NachoSoto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this would be better, it always adds clarity in call sites.

@andersio
Copy link
Member Author

andersio commented Nov 26, 2016

I'd argue that the variable names usually have conveyed that they are signals, e.g. SignalProducer(signal: usernameSignal), SignalProducer(signal: occupationValues), which makes the signal label look redundant. Labeled initializer is also used only when it comes to type narrowing, although in our case we need them to prevent conflict with values: ... and friends too.

@sharplet
Copy link
Contributor

Most of the time I use this initialiser to compose a signal with other producers, or to use producer-specific operators on it. This means that it's usually part of some kind of operator chain, where a mess of keywords, operators and other syntax can make things dense and hard to parse. I personally find that the signal: label doesn't add much other than redundancy, which for me reduces clarity / increases visual noise.

@sharplet
Copy link
Contributor

But, it would be good to show some real examples of how this changes code at the point of use.

@andersio
Copy link
Member Author

andersio commented Nov 28, 2016

TBH I haven't used it in my projects with all the stuff we have introduced since 4.2.0. The last time I've ever used it is probably in our codebase. As you might have seen in the diff, the label doesn't feel like adding clarity at all but verbosity. Though maybe if we rename had the the label been named to forwarding it would have made a better sense.

 	public convenience init(initial: Value, then signal: Signal<Value, NoError>) {
 -		self.init(unsafeProducer: SignalProducer(signal: signal).prefix(value: initial),
 +		self.init(unsafeProducer: SignalProducer(signal).prefix(value: initial),
  		          capturing: [])
  	}

@mdiep
Copy link
Contributor

mdiep commented Nov 29, 2016

I'm pretty ambivalent about this. It's nice on the one hand—I always prefer unnamed arguments when they're unambiguous—but this initializer doesn't seem widely used, so it might be a little confusing.

@mdiep mdiep added the proposal label Dec 2, 2016
@andersio andersio changed the title Rename SignalProducer(signal:) to SignalProducer(_:). Drop the label of SignalProducer initializers that take a sequence. Dec 2, 2016
@andersio
Copy link
Member Author

andersio commented Dec 2, 2016

In a short discussion with @sharplet, we came up with a more generalised idea of dropping the label for all initializers that take a sequence.

The argument was:

Do we need to distinguish it here (the variants of initializer) though? Both are conceptually (taking) a sequence, and results in a producer that emits a sequence of values.

It has to be distinguished, IMO, only if it would result in different behavior, or has loss of information e.g. type narrowing. Moreover, at the call site, one should already have known what it is when it comes to type conversion or initialization. Otherwise, garbage in garbage out (?).

Summing up with @sharplet's words:

Yeah I'd agree with that. You could say that these convert a time-based sequence of events to a SignalProducer, where the Sequence version is immediate and the Signal version is streamed.

So the PR has been updated with two SignalProducer(_:) overloads, one taking a Signal and another taking Sequence.

Not sure if we should count the vararg initializer as one though.

@sharplet
Copy link
Contributor

sharplet commented Dec 2, 2016

Not sure if we should count the vararg initializer as one though.

I'd recommend avoiding that ambiguity if we can.

@@ -469,7 +469,7 @@ public final class Property<Value>: PropertyProtocol {
/// - initialValue: Starting value for the property.
/// - signal: A signal that will send values to the property.
public convenience init(initial: Value, then signal: Signal<Value, NoError>) {
self.init(unsafeProducer: SignalProducer(signal: signal).prefix(value: initial))
self.init(unsafeProducer: SignalProducer(signal).prefix(value: initial))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about renaming the local variable to values from signal? I like the direction of naming the signal to describe its values, rather than just restating that it's a signal (which we know from the types).

@@ -29,7 +29,7 @@ public struct SignalProducer<Value, Error: Swift.Error> {
///
/// - parameters:
/// - signal: A signal to observe after starting the producer.
public init<S: SignalProtocol>(signal: S) where S.Value == Value, S.Error == Error {
public init<S: SignalProtocol>(_ signal: S) where S.Value == Value, S.Error == Error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, what do you think about naming this values internally?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd better stay signal, since it has more than just values but also termination events.

@@ -100,9 +100,9 @@ public struct SignalProducer<Value, Error: Swift.Error> {
/// - parameters:
/// - values: A sequence of values that a `Signal` will send as separate
/// `value` events and then complete.
public init<S: Sequence>(values: S) where S.Iterator.Element == Value {
public init<S: Sequence>(_ sequence: S) where S.Iterator.Element == Value {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this also could remain values.

@@ -122,7 +122,7 @@ public struct SignalProducer<Value, Error: Swift.Error> {
/// - second: Second value for the `Signal` to send.
/// - tail: Rest of the values to be sent by the `Signal`.
public init(values first: Value, _ second: Value, _ tail: Value...) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to me to keep the values: label here.

@sharplet
Copy link
Contributor

sharplet commented Dec 2, 2016

I'd like to add that the discussion was informed specifically by this section of the API design guidelines.

Copy link
Contributor

@sharplet sharplet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a fan of this change 👍

Copy link
Contributor

@mdiep mdiep left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes some sense to me under the type-conversion section of the Swift API guidelines that @sharplet linked to.

What do you think, @NachoSoto?

@mdiep mdiep requested a review from NachoSoto December 8, 2016 02:08
@andersio andersio added this to the 1.0 milestone Dec 8, 2016
@@ -100,7 +100,7 @@ public struct SignalProducer<Value, Error: Swift.Error> {
/// - parameters:
/// - values: A sequence of values that a `Signal` will send as separate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not a fan of any of this because users are left to the expense of the type inference to determine which initializer to use, rather than being able to explicitly pick one.
If Swift's type inference and diagnostics were great I might not mind, but considering how poor they are, it's my opinion that this will bring more problems than benefit (in fact I can't think of any benefit other than conciseness, which is not necessarily an advantage).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't feel like the type checker could ever cause any chaos here though.

SignalProducer is a discrete time sequence, while Sequence is immediate. This means we would likely never conform SignalProducer to Sequence, or in other words the two signatures would never collide in any sense.

@mdiep
Copy link
Contributor

mdiep commented Dec 13, 2016

I wish we could have unanimity here, but we do have consensus and—more importantly—the Swift API guidelines seem pretty clear on this point. So I'm going to go ahead and merge.

@mdiep mdiep merged commit a92032f into master Dec 13, 2016
@mdiep mdiep deleted the producer-init branch December 13, 2016 02:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants