| Jens Jens Persson
June 1 |
- | - |
This thread is an attempt to see if we can address the following (emphasis mine):
How does
associatedtype
inference workOh, and my conclusion is that no one should use typealiases in constrained protocol extensions until this gets worked out, i.e. until there's an actual design. But that's just my opinion.
No need to look into that thread, suffices to look at this little example program and answer the questions (without compiling the program first!):
protocol P { associatedtype A = Bool } extension P where Self: BinaryFloatingPoint { typealias A = Float } func foo<T: P>(_: T.Type) { print(T.A.self) } extension Double: P {} extension String: P {} foo(Double.self) foo(String.self) print(Double.A.self) print(String.A.self)
Questions:
Should this program compile?
Yes
If it should, what (four lines) should it print?
I expect after thinking about it for a little while and skimming the thread but not compiling or running the code and only having the first cup of coffee:
Float
Bool
Float
Bool
(Where the extension P on Double uses the extension P where Self: BinaryFloatingPoint conformance because Double conforms to protocol BinaryFloatingPoint and the typealias A matches up with the associatedtype A in the protocol P.) String does conform to BinaryFloatingPoint so it does not pick up the extension and just uses the associatedtype in protocol P.
I think that’s what you are also expecting by posing the question but peoples’ intuitions or expectations vary kind of a lot in this area.
Couple of things, some of which are introspecting my intuitions and may or may not be what you’re looking for, and are on the first cup of coffee of the day:
-
It is not that obvious that the typealias with the same name replaces/overrides/whatever the associatedtype. It’s kind of a how-else-would-you-do-it kind of thing. It might be clearer if you put ‘associatedtype’ instead of ’typealias’ in the extension and explained that it was replacing or implementing the one in the protocol. Or something like that. It feels like a rough edge in there. This is independent of generalized existentials I think—it’s just that the syntax (with some semantics) has a specific rule here that you just have to know (where typealias in extension goes to associatedtype with same name in protocol.)
-
The extension … where for a conditional conformance (maybe not the right terms exactly) has always felt a little odd to me also. My initial intuition on conditional conformances was not that they were conditional, maybe because the where is after the extension. It felt like the extension is always applied (the way it would be if there were no ‘where’) but you get something extra if the ‘where’ applies. Which does not really make sense but it’s where my mind went at first. If the syntax were something like “if Self : BinaryFloatingPoint then extension P …” it would be gross but more initially intuitive. Once your intuition is guided by experience it’s clear.
-
I think 'x where y’ is similar to ‘if y then x’ in type terms (maybe identical) but that’s not a fully-formed thought. If that or something like it were set as a core syntactic principle of the language it might sit clearer.
-
My intuition wants everything to be like class inheritance where same name in subclass means use it instead (to use short words) but for extensions, conformances and protocols the rules are not as built in. For the associatedtype and typealias thing, the correct rule is that same name replaces but the label next to it is different so you think that the same-name rule may not apply. For conditional conformances same-name extends but only under conditions in the where clause.
Now that I wrote all that down extension … where seems a lot more intuitive to me, which shows how much intuition can vary, since I just started thinking about this at all in the last half-hour or so.
I wrote a lot there, hope that’s OK and that some of it helps.
—Dan