ReactiveCocoa/ReactiveSwift

Potential Effects of SE-0286 on ReactiveSwift

grantjbutler opened this issue · 8 comments

I've recently updated to Xcode 12 beta 5. Since doing this, I've noticed in a number of places where I'm doing Signal.merge across a number of Signal instances that I've started getting errors such as Cannot convert value of type 'Signal<AnalyticData, Never>' to expected argument type 'Signal<(ConversationContext, String) -> AnalyticData, Never>'. After doing some investigation, my best guess is that this is the result of SE-0286 being added in Xcode 12 beta 5. When I have something like intSignal.map { String($0) }, in some cases the compiler chooses map(value:) over the map that takes a closure. I'm not entirely sure on what all causes the compiler to make that decision, but the end result is that I have a Signal<(Int) -> String, Error> instead of a Signal<String, Error>.

I've attached a sample project that demonstrates a few of the cases where I've run into this. I'm not sure exactly what the right outcome of this is. That is, if the fix for this is to make a change to ReactiveSwift, or if the fix is to report this back to the Swift team about the impact of SE-0286. At the very least, I'm creating this ticket to document this behavior and make you (the maintainers of ReactiveSwift) aware of it.

ReactiveSwift SE-0286.zip

1oo7 commented

Same here. Based on this I think Apple should roll back multiple trailing closure syntax. Sugar should never be a breaking change...

That overload resolution bug in the compiler had happened before but was resolved. Seems like it is time to file it again.

Also related, there's a recent bug open: https://bugs.swift.org/browse/SR-13415

I filed https://bugs.swift.org/browse/SR-13453, although what I reproduced doesn't seem to manifest the same way as described by OP.

We could mitigate this by applying @_disfavoredOverload, although I don't know if it would have any undesired side effect.

I've found that I can work around this in most cases by explicitly ignoring the closure argument, for example changing:

/// Compiler sees as `Signal<() -> User?, Never>`
.map { AppEnvironment.current.currentUser }

to:

/// Compiler sees as `Signal<User?, Never>`
.map { _ in AppEnvironment.current.currentUser }

This is a little risky though because the compiler actually sometimes builds both of these successfully but the behaviour is different/unexpected so I'm not sure how to be sure that I've managed to catch all of the instances where this is happening.

Are there any better workarounds perhaps?

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. 😄