Another thing that I cannot get my head around, is the relation between argument and result types and the protocol. They don't look like associated types, because they are specified from the outside. To me they look more like generic parameters. But Swift currently does not have generic protocols, and, as far as I understood, is not planning to have.
Improving the UI of generics - #125 by anandabits mentions the following syntax, which may somewhat resolve this:
typealias CollectionOf<T> = Collection where Self.Element == T
Should we say that (Int) -> Void
is a syntax sugar for Callable where Self.Args == (Int), Self.Result == Void
? Or (Int) -> Void
is a non-nominal protocol and notions of both generic args and associated types do not apply.
As an example, let's say we want to write a generic middleware that prints all the args and result of a function call.
With generic arguments, it would look something like this (variadic
keyword is from swift-evolution/XXXX-variadic-generics.md at variadic-generics · technicated/swift-evolution · GitHub):
struct FunctionLogger<variadic Args, Result, F: (Arg) -> Result = any (Args) -> Result>: (Args) -> Result {
var base: F
func callAsFunction(_ args: Args) -> Result {
print(args)...
let result = base(args)
print(result)
return result
}
}
And with associated types:
struct FunctionLogger<F: Callable>: Callbable {
typealias Args = F.Args
variadic typealias Result = F.Result
var base: F
func callAsFunction(_ args: Args) -> Result {
print(args)...
let result = base(args)
print(result)
return result
}
}
Can the same struct conform to multiple function protocols? With generic arguments - yes, because (Int) -> Void
and (Float) -> Void
are different protocols. With associated types, there is only one protocol Callable
and there can be only one conformance.