Should the function be called `map` or `flatMap`?

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:

19 Likes