Declaring an extension on a typealias provided by a protocol leads to errors

I have some code that looks like this:

struct HelperType<T> {}

struct A {
  typealias Helper = HelperType<A>
}

struct B {
  typealias Helper = HelperType<B>
}

struct C {
  typealias Helper = HelperType<C>
}


extension A.Helper {
    // A-specific stuff
}
extension B.Helper {}
extension C.Helper {}
// etc...

I wanted to consolidate the repeated typealias declarations by using a protocol:

protocol P {
  typealias Helper = HelperType<Self>
}

struct A: P {}
struct B: P {}
struct C: P {}

But when I do that…

// Error: Extension of type 'A.Helper' (aka 'HelperType<A>') 
// must be declared as an extension of 'HelperType<A>'
extension A.Helper {}

Is there a reason we can't allow the extension declaration on the typealiased name here when the typealias was provided by a protocol? The actual name I'm using in my source code is quite a bit longer than "HelperType", and it would be a significant ergonomic improvement to refer to it via the typealias.

2 Likes

Resolving a protocol type alias as a member of a concrete base type requires being able to perform associated type inference. Extension binding happens very early in type checking before any other name lookups are performed so it cannot depend on the rest of the type checker in arbitrary ways -- because the rest of the type checker relies on being able to perform name lookups, including finding members of extensions. So we prohibit this specific case and a couple of others to ensure that extensions can be resolved in a simple enough way.

3 Likes

Ah, I see. That's unfortunate for my particular use case; I guess I'll just have to stick with repeating the typealias declarations for now.

Can you move your type alias to global scope and then make it explicitly generic over T?

typealias Helper<T> = HelperType<T>

extension Helper<A> { ... }

Yeah, that's possible. The challenge is that the obvious top-level name conflicts with an existing standard-library type, so I'd have to give it a different name.

What I'm really doing is making it easy to create predicates for Core Data types:

The code in question

// This is what I want to be able to do:
let entries = Entry.fetch(in: context, matching: .starred(true), .createdAfter(someDate))

// And a sketch of the implementation:
extension NSManagedObject {
  struct Predicate<T> {
    var nsPredicate: NSPredicate
    init(_ format: String, _ args: CVarArg…) { ... }
  }
}

extension Entry {
  typealias Predicate = NSManagedObject.Predicate<Entry>
}

extension Entry.Predicate {
  static func createdAfter(_ date: Date) -> Self {
    .init("date > %@", date)
  }

  static func starred(_ value: Bool) -> Self {
    .init("starred = %@", value)
  }
}


protocol NSManagedObjectHelpers {
  typealias Predicate = NSManagedObject.Predicate<Self>
}
extension NSManagedObjectHelpers {
  func fetch(in context: NSManagedObjectContext, matching: Self.Predicate...) -> [Self] {
    // Use the predicates to fetch matching objects
  }
}

extension NSManagedObject: NSManagedObjectHelpers {}

So my top level type would really want to be called Predicate<T>… but there's already a Predicate<T> type in Swift. So I've got my type nested as NSManagedObject.Predicate<T>, which is kinda a mouthful. It's doable, but I was hoping to do something shorter.

Being able to call it MyType.Predicate or whatever would keep it shorter and to the point, without conflicting with the existing standard library type.

1 Like

i really sympathize, this is a problem i also struggle with a lot. i wish swift had support for using namespaces like in C++. over time i’ve just accepted that swift types are naturally going to have very long, bulky qualified names that need to be parroted everywhere in full; i haven’t really found any great solutions.

3 Likes

As the rest of the language holes get filled out, a lack of C++ style namespaces are quickly becoming the big problem with Swift.

2 Likes