Swift's dot shorthand for static factories is wonderful! Unfortunately, it currently requires a concrete type context in order to work. It is possible to mimic these forms of shorthand using a wrapper:
struct Sugar<T> {
let value: T
}
extension Sugar where T == S {
static var s: Sugar<S> { return .init(value: S()) }
}
func foo<T: P>(_ t: Sugar<T>) {
// use t.value (and maybe T.self)
}
foo(.s)
struct MetaSugar<T> {}
extension MetaSugar where T == S {
static var s: MetaSugar<S> { return .init() }
}
func bar<T: P>(_ t: MetaSugar<T>) {
// use T.self
}
bar(.s)
This approach works in some use cases, such as DSLs where all arguments are expected to be created inline using the dot shorthand. However, this approach is very suboptimal when users need to pass values directly (they would need to wrap the value before passing it).
For this reason, a library wishing to provide this kind of syntactic sugar will likely resort to providing two overloads for each API (one to support dot shorthand and another for directly passed values that come from call-site variables). Unfortunately, this leads to a lot of boilerplate and only works for APIs shipped with a library, not extension APIs users provide themselves (unless users can be convinced to provide the boilerplate as well).
The solution is to lift the limitation and directly support dot shorthand in generic and existential contexts so code such as the following works:
protocol P {}
struct S: P {
static let s = S()
}
func foo(_ p: P) {}
func bar<T: P>(_ t: T) {}
foo(.s)
bar(.s)
In generic contexts, the compiler would infer the type T
based on the factory that was used. If two types meeting the constraints provided identical factories an ambiguity error would be produced.
This variant of dot shorthand could also support cases where a metatype is required. This can make it much more convenient to pass metatypes when they may be much more verbose than necessary given a particular constrained context.
extension S {
static var s: S.Type { return S.self }
}
func bar<T: P>(_ t: T.Type) {}
bar(.s) // instead of `bar(S.self)`
In the above example, imagine that S
has a very verbose type name, but P
determines a context where a very short name is sufficiently clear. It might be possible to use a typealias
to shorten the type name, but a typealias
would need to live at global scope to be concise enough for this purpose.
Finally, specifying type names like Int.self
in a call aiming for DSL-like syntax is not nearly as elegant as the dot shorthand form .int
. (note: the desire to support the latter in an API is what led to my discovery that the "sugar" wrapper workaround exists)