In my work I've found that if I'm working with external dependencies that return effects, that I need to receive values on the main queue before making UI updates. However if I do that, I often need to re-erase to an effect (for example):
let reducer = MyReducer { state, action, env in
switch action {
case .onAppear:
return env.api
.loadData()
.map(.dataLoaded)
.receive(on: env.mainScheduler)
.eraseToEffect()
case .dataLoaded(let data):
state.data = data
return .none
}
In this case, if loadData returned an Effect, it wouldn't matter since there's no receive(on:) overload on Effect, which means the return value is just a publisher that needs to be erased again. If Effect had a custom receive(on:) overload that specified an Effect as a return value, I could take advantage of dependencies that return effects and make my reducer code a little bit less verbose by omitting eraseToEffect calls in more places.
Is this something that would be useful or wanted in TCA itself?
So the motivation is essentially just to remove the need to eraseToEffect at the end? I don't think that would pass the bar to be included in the core framework — it isn't necessary for composition. That's just my opinion, though.
Definitely an interesting idea! We already overload map, so overloading receive(on:) could be worth it, given how often it's done. Let's start thinking about it.
Passing a scheduler into loadData is possible, but that seems like it'd just be reinventing receive(on:) in a less-extensible or reusable way. I'd still have to erase to effect inside the implementation of the loadData method, and i guess from a more ideological viewpoint, it seems superfluous, since you may not want to force the api consumer to choose a scheduler to run things on when you call loadData, since it's pretty well known you can call receive(on:) at any point in a combine operator chain.