Calling type(of:) on an opaque function argument (some P)

I have a question for @hborla and @Slava_Pestov about your (excellent!) WWDC 2022 talks Embrace Swift generics and Design protocol interfaces in Swift. I though I'd ask this here instead of on the Apple Developer Forums because it's 100% about a language feature.

In them, you have this feedAnimal method that calls type(of: animal) on a some Animal value in order to get at the associated type FeedType:

extension Farm {
  private func feedAnimal(_ animal: some Animal) {
    let crop = type(of: animal).FeedType.grow()
    let feed = crop.harvest()
    animal.eat(feed)
  }
}

I understand that you wanted to show the new some Animal syntax, but to me this looks like a code smell because type(of:) is a runtime construct in my understanding.

I would have written this with a named type parameter, which would allow me to get rid of the type(of:):

func feedAnimal<A: Animal>(_ animal: A) {
  let crop = A.FeedType.grow()
  …
}

Question: is there a difference between these two variants in terms of the generated code? Is the version using type(of:) less efficient? Or are they identical?

8 Likes

type(of:) is always identical to the explicit version when the type(of:) is the base of a member type lookup.

6 Likes

That's good to know, thanks!

Thanks for Ole's interesting question and Slava's informative reply!

I played around with a simplified version of the code with Compiler Explorer using the Swift 5.7 nightly.

In the no-flags version, there is always an extra call to swift_getDynamicType@PLT in there for the type(of:) version. But if you add -O for optimization they are indeed the same. If fact one method just aliases the other! :star_struck::exploding_head::star_struck:

I changed the signature of the method just slightly so I could compare. Yep, they're the same.

7 Likes