Given this code:
let a = PassthroughSubject<Int, Never>()
let b = PassthroughSubject<Int, Never>()
let result = a.merge(with:b).eraseToAnyPublisher()
I would think that result
would have the type AnyPublisher<Int, Never>
, but instead it looks like Xcode is seeing it as (white-space formatting mine):
AnyPublisher<
Publishers.MergeMany<
PassthroughSubject<Int, Never>
>.Output,
Publishers.MergeMany<
PassthroughSubject<Int, Never>
>.Failure
> (aka 'AnyPublisher<Int, Never>')
And that is just not what I expected. I guess there's some implementation being hidden, but it's not nearly what I'd hope for. And clearly, given the "aka" part, there's some acknowledgment that Xcode knows that it's equivalent to 'AnyPublisher<Int, Never>', but why isn't it type-erased to that, entirely?
I even tried type-erasing earlier in the chain, like so:
let a = PassthroughSubject<Int, Never>().eraseToAnyPublisher()
let b = PassthroughSubject<Int, Never>().eraseToAnyPublisher()
let result = a.merge(with:b).eraseToAnyPublisher()
And that's only slightly better, as Xcode thinks result
has type:
AnyPublisher<
Publishers.MergeMany<
AnyPublisher<Int, Never>
>.Output,
Publishers.MergeMany<
AnyPublisher<Int, Never>
>.Failure
> (aka 'AnyPublisher<Int, Never>')
CodePudding user response:
This is because eraseToAnyPublisher
is declared as:
func eraseToAnyPublisher() -> AnyPublisher<Self.Output, Self.Failure>
In your case, Self
is the type Publishers.MergeMany<PassthroughSubject<Int, Never>>>
, as that is what merge
returns. After a simple substitution, replacing Self
with the type that merge
returns, the return type of eraseToAnyPublisher
becomes:
AnyPublisher<
Publishers.MergeMany<PassthroughSubject<Int, Never>>.Output,
Publishers.MergeMany<PassthroughSubject<Int, Never>>.Failure
>
Of course, if you follow through all the type aliases, this is the same type as AnyPublisher<Int, Never>
.
Xcode could have been designed to only show the "aka" type, i.e. the type after all the type aliases have been resolved, but that kind of goes against the whole point of type aliases, which is that the type should be called by another name. If I have:
typealias Money = Decimal
func deposit(amount: Money) { ... }
I certainly do not want this function to show up in Xcode only as deposit(amount: Decimal)
!
Side note: on iOS, you can also cast to an existential type as an alternative to eraseToAnyPublisher
.
a.merge(with:b) as any Publisher<Int, Never>
Calling eraseToAnyPublisher
on the above will actually give you a "short" type name, but there really is no point :)