There is a very general concept that accomplishes this known as "traversal". It completely generalizes the ideas of "flipping" nested containers around. In its most general form it looks like (using Haskell syntax):
(a -> f b) -> t a -> f (t b)
Notice that if you use f = Optional
and t = Notification
this is precisely your function:
(A -> B?) -> Notifcation<A> -> Notification<B>?
More information can be found here:
http://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Traversable.html
http://hackage.haskell.org/package/lens-4.16.1/docs/Control-Lens-Traversal.html
So if you wanted to go with that precedent, the name would be called traverse
. The interesting thing though is that you can use other f
type constructors besides Optional
and still have something traverse
-like:
// f = Array
(A -> [B]) -> Notifcation<A> -> [Notification<B>]
// f = Result
(A -> Result<E, B>) -> Notifcation<A> -> Result<E, Notification<B>>
// f = Future/Promise/Task
(A -> Future<B>) -> Notifcation<A> -> Future<Notification<B>>
It's kinda hard to see from the fully general form of (a -> f b) -> t a -> f (t b)
, but the crux of what we are trying to express is how to flip around nested container types: t (f a) -> f (t a)
. With your types this boils down to transformations:
Notification<A?> -> Notification<A>?
Notification<[A]> -> [Notification<A>]
Notification<Future<A>> -> Future<Notification<A>>
It also just happens that this is a really great example of what "higher-kinded types" in Swift could give us. You could essentially implement one single interface and get an implementation for all of these intertwining functions (and so much more) for free. I wrote a bit about this in another thread: