Group Group Group Group Group Group Group Group Group

Why same-type constraint on the protocol is not enough to define associated type?

#1

Hi guys

Can someone please explain me why I cannot declare “b” variable in the example below?

protocol A {
    associatedtype Example
}

protocol B : A where Example == Int {
}

var b: B?

The associated type is no longer an unknown type, it is defined in the “where” clause and it is a concrete type Int known at compile time.

Any code that will try to override the Example type to a different type will get you an error which means compiler knows that Example is always Int.
The two below will end in a compile time error:

protocol C : B where Example == String {}
struct G : B {
  typealias Example = Double
}
#2

@skotnicki Do you still have issues with this?

#3

Thanks for an interest in this question. I don’t quite see any reason why it shouldn’t work so if you have some insight please share :slight_smile:

#4

I believe the rule about “a protocol with an associated type can only be used as a generic constraint” is built into the compiler, and applies to all such protocols, even if the type is knowable. You can’t use the protocol directly as the type of a variable or a parameter.

You can do this, though:

struct C: A {
  typealias Example = Int //typealias required
}

struct D: B {
  // no typealias needed
}

That illustrates that it is able to deduce the associated type of protocol B. It still requires something to conform to protocol B in order to use it, though.

#5

Hi @skotnicki,
I am sure you have a valid reason for trying what you are trying to get it working. If you could try to explain what it is that you are trying to achieve maybe you could get some help.

In regards to Generic filtering, if you have a concrete type then it defeats the purpose of having a generic, you can simply have it as is.

cheers,

#6

Let’s leave structs and other concrete types, I know it works there.

I believe the rule … is built into the compiler, and applies to all such protocols, even if the type is knowable.

Too bad :slightly_frowning_face:. Is there any example against it? Would it really mess things up if it was allowed to use protocol B as a normal protocol?

I know generalized existentials are not a thing in Swift, and neither this:
typealias B = A where A.Example == Int
or this
typealias AnyA<T> = A where A.Example == T
is allowed (at least yet).
But this is:
protocol B : A where Example == Int

Just theoretically - I guess that if there was a form of a typealias that could be used (like PAT) as a generic constraint only then it would also work (at least the typealias B, because AnyA would be more of a type eraser created by a compiler).

But unlike typealias (which would only give a concrete name), protocol B can also define other requirements for the Int case of the associated type.
I guess that this is the main reason why it is allowed to declare such a protocol?

It is sad and weird that “B” is still only usable as a generic constraint.
On top of that I need to use a type eraser to use “B” as “normal”.
This is very funny because there is nothing to erase, the associated type is already an Int… so the type eraser won’t be even generic it will be just AnyB : B. It is even bigger boilerplate code than usually.

@jayantvarma

I am sure you have a valid reason for trying what you are trying to get it working.

if you have a concrete type then it defeats the purpose of having a generic, you can simply have it as is.

It defeats the purpose of having a generic, but it does not defeat the purpose of having a non-generic protocol.
It is still more flexible and abstract to have a normal protocol instead of a concrete type or a type eraser which I always need to wrap things into. If protocol A declares a lot of requirements then writing a type eraser for it is a real nightmare and boilerplate code which I thought I could avoid by creating this protocol B :wink:

I didn’t want to solve a particular issue I had, I was just curious why “B” is still usable as a generic constraint only even though it has its associated type defined.
I was hoping to find an explanation other than: compiler treats it as a PAT no matter what. Is there a counter example? An example where protocol B would make things complicated/unclear etc. if it was allowed?