Cannot find static func in scope in generic implementation

protocol Computable {
 static func f(_ x: Self) -> Self
}

func compute<T: Computable>(_ x: T) -> T {
  let a = T.f(x) // OK
  let b = f(x) // error: cannot find 'f' in scope
  }

What do I need to understand about generics to understand this? Is there a way to say this that doesn't require the explicit scope? I'm in the process of translating a lot of very old C++ template code to Swift; any pointers to read about the differences between C++ templates and Swift generics would be appreciated.

This does not have anything to do with generics at all, actually, although you are right that Swift generics are very different from C++ templates.

You’ve declared a static member requirement for a protocol, but it’s no different if you had declared a static member for a class. Inside any global function like compute, or really anywhere (other than inside another static member of the relevant type), you need the type to be written out in some way, because you are accessing a static member. There is nothing in the global scope named f.

There is a leading dot notation (called an “implicit member expression”) where the type can be omitted (but not the dot) if the type can otherwise be inferred. But that’s not germane to your understanding here that f is not a global function.

If you’re new to Swift from C++, I would highly recommend perusing The Swift Programming Language or another introductory book, because there is much more than just generics that is familiar but actually not the same. It will make your experience using Swift much less mystifying.

3 Likes

Basically what @xwu said, but note that you can make this work by declaring a global function:

protocol Computable {
 static func f(_ x: Self) -> Self
}

func f<T: Computable>(_ x: T) -> T {
  T.f(x)
}

func compute<T: Computable>(_ x: T) -> T {
  let b = f(x) // OK, gets T from x.
  }

Generally speaking, Swift doesn't use this style much, but it's occasionally useful.

Another option is to move your compute function into an extension on Computable, rather than keeping it a global function.

protocol Computable {
 static func f(_ x: Self) -> Self
}

extension Computable {
  static func compute(_ x: Self) -> Self {
    let b = f(x) // OK, we're in a static context, so name lookup on f works here.
  }
}

Which of these (or if either of these) is appropriate depends on specifics of what you're doing that aren't really clear from your original question.

5 Likes

Thank you. That clarifies my confusion and options.

Thank you. That explains my confusion. Am I correct that there is no way for a protocol requirement to specify the existence of a global function?

One more noob question: Is operator lookup different? The protocol requirement static func +(a: Self, b: Self) -> Self looks similar to declaring a static member function requirement, but works as expected.

That change was made in SE-0091. The thread provides some explanation about what's going on and why it was changed.

1 Like