Why is a T.Type parameter needed sometimes?

I see generic methods that seem to require an additional type argument, and here is a SwiftUI example:

nonisolated
func onGeometryChange<T>(
    for type: T.Type,
    of transform: @escaping (GeometryProxy) -> T,
    action: @escaping (T) -> Void
) -> some View where T : Equatable

At first I looked at it and I couldn't think of any reason for having the for type: T.Type parameter, except if some inference could not work in some situations. But a small example I made demonstrates that inference works just fine based on the two callbacks:

➜  ~ xcrun swift repl
Welcome to Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1).
Type :help for assistance.
  1> struct T {
  2.   struct P {
  3.     var x: Int
  4.     init(x: Int = 3) {
  5.       self.x = x
  6.     }
  7.   }
  8.   var p: P
  9.
 10.   init() {
 11.     self.p = .init()
 12.   }
 13.
 14.   func f<T>(arg: (P) -> T, f: (T) -> Void) -> Void {
 15.     f(arg(p))
 16.   }
 17. }
 18> T().f {p in String("\(p.x) times")} f: { s in print(s) }
3 times
 19>

So what gives? Can someone explain what possible reasons could have required the authors of the API at the top to add the T.Type parameter? Couldn't they have just used T.Type in the body of T.f if needed? Is this answer one possible explanation?

2 Likes

Yes.

3 Likes

The parameter is good, but the lack of giving it a default is unacceptable.

for type: T.Type,

should be

for type: T.Type = T.self,

. Instead, we need to maintain our own overload.

extension View {
  nonisolated func onGeometryChange<T: Equatable>(
    of transform: @escaping (GeometryProxy) -> T,
    action: @escaping (T) -> Void
  ) -> some View {
    onGeometryChange(for: T.self, of: transform, action: action)
  }
}
2 Likes