You're being sarcastic, but I meant to suggest that there would be value in running those layers independently so that you don't combine errors. If you present it as F[X] then you've thrown away potentially interesting information. IO (IO ()) is pretty valuable, for instance.
Just curious, what is the advantage? Near as I can tell EmailProvider will return one of two sorts of errors - either a ServiceError (the microservice was unable to send) or a NetworkError (unable to talk to micro service). These will be represented in the class of the exception the future wraps.
What benefit does two levels of futures provide? I was sarcastic before, but now I'm honestly asking.
1. If you don't assume exception throwing (which
may be valuable) then you can more easily distinguish
one failure from the other.
2. It reflects in the type a more accurate representation
of the effect going on. If such distinctions are
undesirable it's a trivial `join` to eliminate them
but if the distinction is never exposed then you can't
get it back.
So really, my thought is that given greater opportunity to distinguish between end-user meaningful effects it's worth exposing them by default. It should be a deliberate choice to hide them with join.