Before opaque types (pre-some) in Swift the type system worked by the rule that in the let x: <follows_after_:> expression that <follows_after_:> represents a concrete type which would mean the compiler has strong guarantees about what it can do with x and that it can combine it with any other y or z in any operation, provided all of them had the same string in their <follows_after_:>; These are generally referred to as complete types;
Protocols with associated-types or self-requirements are incomplete types so naturally you would get that dreaded error message Protocol P can only be used as a generic constraint because it has associated types or self requirements whenever you tried to declare such a variable;
Then opaque types came along and suddenly you could put the some keyword in the <follows_after_:> part;
This now changes the definition of <follows_after_:> to not represent concrete type here anymore but more generically type info here and it can either be a concrete type like in the beginning or an incomplete type by prepending some in front of a PAT protocol;
So when you write
let x: some Protocol
it's the same as if you would write
let x: unknown_type_but_conforms_to Protocol
thereby stripping the compiler of any strong guarantees about the underlying type of x; The compiler has to play it safe now and you may not combine x with any y or z in any operation even though they have the same string in <follows_after_:> i.e.
let y: some Protocol = ....
let z: some Protocol = ....
even though you as a programmer know that x, y and z have the same type because you get them from the same function.
Also the rule that with
func f() -> <f_return_type_info> {
....
}
the following declarations are equivalent
let x = f()
let y = f()
let x: f_return_type_info = f()
let y: f_return_type_info = f()
remains valid only for concrete / complete types in f_return_type_info
In reality it's not like the compiler transforms:
let fetcher3 = FetcherFactory.getFetcher()
let fetcher4 = FetcherFactory.getFetcher()
into:
let fetcher3: some ContentFetcher = FetcherFactory.getFetcher()
let fetcher4: some ContentFetcher = FetcherFactory.getFetcher()
but rather into something like:
let fetcher3: unknown_but_complete_from_FetcherFactory.getFetcher = FetcherFactory.getFetcher()
let fetcher4: unknown_but_complete_from_FetcherFactory.getFetcher = FetcherFactory.getFetcher()
And because of the constrictions -> some Protocol imposes on its functions the compiler that type is complete but unknown. Now the compiler knows that it can play safely and it can at least let you can combine fetcher3, fetcher4 into any operation because they have the same underlying type.
So here lies your confusion:
The only thing you're making specific is the fact that fetcher1 and fetcher2 have unknown_type_but_... types to the compiler;
Or in other words you're making non-specificness specific. I think this is because your mind is still accustomed with the old rule of what <follows_after_:> represents;
So nowadays
<follows_after_:> can mean complete type or incomplete type
<return_type_info> can mean complete type
or incomplete_type (if you specifiy incomplete_type in <follows_after_:>)
or complete_but_unknown_from_function***** (if you omit <follows_after:>)