Is there a way to avoid using AnyPublisher/eraseToAnyPublisher all over the place?

后端 未结 3 1600
太阳男子
太阳男子 2021-02-08 11:46

I\'m just learning how to use Combine. I have experience with Rx (RxSwift and RxJava) and I\'m noticing that it\'s quite similar.

However, one thing that is quite differ

3条回答
  •  栀梦
    栀梦 (楼主)
    2021-02-08 12:06

    Swift, as of this writing, doesn't have the feature you want. Joe Groff specifically describes what is missing in the section titled “Type-level abstraction is missing for function returns” of his “Improving the UI of generics” document:

    However, it's common to want to abstract a return type chosen by the implementation from the caller. For instance, a function may produce a collection, but not want to reveal the details of exactly what kind of collection it is. This may be because the implementer wants to reserve the right to change the collection type in future versions, or because the implementation uses composed lazy transforms and doesn't want to expose a long, brittle, confusing return type in its interface. At first, one might try to use an existential in this situation:

    func evenValues(in collection: C) -> Collection where C.Element == Int {
      return collection.lazy.filter { $0 % 2 == 0 }
    }
    

    but Swift will tell you today that Collection can only be used as a generic constraint, leading someone to naturally try this instead:

    func evenValues(in collection: C) -> Output
      where C.Element == Int, Output.Element == Int
    {  
      return collection.lazy.filter { $0 % 2 == 0 }
    }
    

    but this doesn't work either, because as noted above, the Output generic argument is chosen by the caller—this function signature is claiming to be able to return any kind of collection the caller asks for, instead of one specific kind of collection used by the implementation.

    It's possible that the opaque return type syntax (some Publisher) will be extended to support this use someday.

    So you have two options today:

    • Change your return type to be the actual generic-heavy type of your publisher (like Publishers.FlatMap>>).
    • Erase your publisher to AnyPublisher and return that.

    Usually you go with the second option, because it's much easier to read and write. However, you will sometimes see a method that uses the first option. For example, Combine's own combineLatest operator has a variant that takes a closure to transform the combined values, and it returns Publishers.Map, T> instead of erasing to AnyPublisher.

    If you don't like spelling out eraseToAnyPublisher all over the place, you can give it your own, shorter name:

    extension Publisher {
        var erased: AnyPublisher { eraseToAnyPublisher() }
    }
    

提交回复
热议问题