This formulation is effectively what I'm saying, and as @Michael_Ilseman said elsewhere. The caller chooses the type for the parameters, and the callee chooses the type for the result. It's the same thing with values: the caller chooses the values passes into the parameters, and the callee chooses the value that's returned.
When this has come up before, folks have pointed at the motivation for the similar impl Trait feature in Rust. This phrase really stood out for me:
But, on the other hand, programmers have a very deep intuition around the difference between arguments and return values, and "who" provides which (amongst caller and callee).
I think that is absolutely true here. As to this...
We do allow inference when the generic parameter is stated in the return type
func f() -> T
but it seems more and more like designing functions like that is considered poor form, because you always need type context to disambiguate. I tend to see more generic functions following the approach taken by unsafeBitCast(_:to:), where you ensure that you can deduce all generic arguments from the argument types:
func unsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U
Right, it could be written as func do(with value: some Any) but it's not that much better.
Let's say I write a function like this:
func g(value: SomeClass) -> SomeClass
As a caller, I can provide a SomeClass or some subclass of it. Only I know the specific type. The value I get back from calling g will be a SomeClass or some subclass of it. Only g knows the specific type. This is not a contradiction, it's how arguments and results work, consistently throughout Swift and many other languages.
... this is contravariance of parameters at work. foo takes a function which accepts a closure taking a specific Numeric type and returning nothing. Unless you have a value of that specific Numeric type, you won't be able to call it. f takes a value of some specific caller-chosen type that is Numeric.
The simple "why" is that it falls out of the direct translation of some in parameter position into generics. The SE-0328 decision had two bits of reasoning: the potential conflict with this proposal (i.e., we could choose for those cases SE-0328 banned to be treated as callee-chosen generic arguments as in those proposal) and because the semantics you'd get from SE-0328 are really not that useful. I suppose you could say that the second bit of reasoning could apply to this proposal as well: because the caller is choosing the parameter type of the closure parameter in your foo example, and you don't have another way to name that type, you can't really call that closure. We could say that that's enough reason to also ban the use of some P in covariant positions within parameters.
Doug