Namespacing protocols to other types

I have tweaked the type checker to allow that. I have also tweaked getContextSubstitutions() to substitute the types, although I am not sure if I have done that correctly.

protocol A {
  struct B {}
}

Inside IRGen, dumping the struct gives:

struct_decl range=[/Users/suyashsrijan/Desktop/test.swift:2:3 - line:2:13] "B" interface type='A.B.Type' access=internal non-resilient
  (constructor_decl implicit range=[/Users/suyashsrijan/Desktop/test.swift:2:10 - line:2:10] "init()" interface type='<Self where Self : A> (A.B.Type) -> () -> A.B' access=internal designated
    (parameter "self" interface type='A.B' inout)
    (parameter_list)
    (brace_stmt implicit
      (return_stmt implicit))))

I have also put emitNestedTypeDecls(protocol->getMembers()) inside emitProtocolDecl, however it has trouble emitting the metadata for the struct (crashes in type converter). If you change the struct to a class, then the crash is in useConformance (because the protocol conformance ref is invalid).

Seems like I am missing something or the type substitution isn't working correctly.

The type of 'self' inside a method of A.B must not be A.B, but Self.B where Self is the outer type parameter from the protocol A. However NominalType cannot model that right now.

I think a first step would be the planned changes to unify BoundGenericNominalType and NominalType, and change BoundGenericNominalType to store a substitution map instead of an array of generic arguments together with a parent type. Then the requisite changes to handle substitutions would be trivial.

1 Like

Hmm when will the planned changes happen? Is it worth waiting or could we just go with nested protocols first and build upon it later?

I don't think there are any concrete plans yet, but if you're interested, merging NominalType into BoundGenericNominalType would be a good first step.

And yeah, I think nesting protocols inside non-generic concrete types is much easier than the other direction and should not require any major representational changes.

2 Likes

This proposal has all our hearts desires about nesting protocols and the reverse: Ease restrictions on protocol nesting by karwa · Pull Request #552 · apple/swift-evolution · GitHub

Unfortunately it hasn't moved since Swift 3, even though everyone seems to agree about it there. I don't know why it was closed either for no good reason. Can we re-open it and push it along further?

Hi @Karl, your PR hasn‘t moved forward for several month. I wonder if you still intend to push it forward or would you like someone else to take over it (maybe @suyashsrijan)?!

Thank you for the update in advance.

I have something working, just need to write some tests. I’ll try to do that in the next few days.

It’s pretty basic - just putting protocols inside other types, but that’s a good start and would be nice to get it merged first before we think about putting other types inside protocols (esp. since its related to generic protocols).

4 Likes

That is still great news. Looking forward to it.

2 Likes

Is this topic dead ? I still have the need for namespacing everytime my project starts to reach a certain size.

1 Like

Everyone does. But any discussion inevitably sinks in "but what should we do with generics?" or something like that.

Modules already provide a good solution to namespacing.

If that's true then why are there so many uninhabited enums used as namespaces floating around?

4 Likes

They actually provide a fairly terrible solution. The only time they perform adequately is when using someone else's module. When writing your own (your app is a single module, by default), you might find that namespacing is useful for organization purposes, but that the overhead of a module is not worth the cost. This usually leads to language hacks such as uninhabited enums.

Personally, I have come across many instances where I had a collection of related static functions with which I did not want to pollute the global namespace. Also, I wanted it clear that the functions are indeed related. So I use uninhabited enums. I'm not going to create a module for half-a-dozen or fewer functions.

6 Likes

Currently the best hack to allow nesting protocols inside of other types is to use a typealias. So given the common MyView.Delegate use-case, you could write something like:

protocol _MyViewDelegate: AnyObject {
}

struct MyView {
  typealias Delegate = _MyViewDelegate
}

class SomeDelegate: MyView.Delegate { // works!
}

But yeah, it would be a lot nicer if we could write that the way we write other nested types. I also still really want nested types in protocols.

9 Likes

Karl's example has nothing to do with my use case, and your pejorative is unwarranted.

4 Likes

This looks like an excellent suggestion to me, cc: @Avi!