Lifting the "Self or associated type" constraint on existentials

...but it is the status quo. And if we evolve in such a way that a new requirement with Self or associated type requirements merely requires a protocol opt in to the existing spelling via annotation, then even that would not be source-breaking.

If we were going to introduce a new syntax for existentials, I think it'd be much less confusing if we did so uniformly, deprecating the existing syntax after a late-enough language version, than to have two syntaxes where one only works some of the time. That seems to me like it'd only make things more confusing, personally.

9 Likes

Is there a reason (besides inertia) to not "declare these protocols differently"?

It isn't clear that they are fundamentally different, and one "kind" can morph into the other over time.

1 Like

Does "over time" mean "during execution", "as code evolves", or something else?

As code evolves, sorry.

Apps aren't the end of the line either. Unit tests often add dummy types; the Swift runtime supports dynamically-loaded in-process plug-ins; and you can even declare new types or add new conformances in the debugger if you want.

11 Likes

Cool! I did not know that.

Here is a good example of a genuine use case where our current restriction would have saved some confusion if the protocol in question had Self or associated type requirements:

That's a bug (I misunderstood, disregard) The non-self-conformance issue is also a good indication of why the existing restriction already doesn't really hide the complexity.

1 Like

(See other thread.)

I "loved" this comment when I saw it originally, but now I'm having second thoughts, because an idea I buried in an aside earlier is growing on me. Suppose:

  1. Self-conforming-protocol existentials can be spelled with just the protocol name
  2. Other existentials always need a where clause, which may be “empty:” Collection where _.

I think that might be enough to solve my problem with implicitly-unavailable APIs, and it wouldn't punish many uses of generalized existentials, which will have a where clause anyway. Finally, if we decide this was all a mistake, we can always lift restriction #2.

I realize this doesn't give us an immediate way to fix the technical problems @Joe_Groff is trying to address with this pitch, but it is an eventual future I think I could support. If we can agree that it's a good idea, maybe we can find a reasonable evolution path to get there that includes an immediate fix for the technical problems.

Thoughts?

1 Like

If We Could Do It All Over Again, it might've been nice to make the type system a Lisp-2 and put protocols and types in separate namespaces. That would give you the ability to express the "simple existential" idea as a typealias, e.g.:

protocol Runcible {
  mutating func runce()
}

typealias Runcible = Any<Runcible>

or spell the canonical type eraser for a less trivial protocol:

protocol Collection { ... }

struct Collection<T>: Collection {
  var _value: Any<Collection where .Element == T>

  /* Collection conformance here */
}

As for where we are today, since "self-conforming-protocol existentials" don't exist now except in limited circumstances, and declaring that self-conformance when desired will necessarily be explicit, making a rule that non-conforming existentials must be spelled differently doesn't really help the immediate source compatibility problems, since non-self-conforming existentials already exist and are spelled without decoration.

2 Likes

I very much hate separate namespaces but you can have the required Any without that.

  • I agree with @jrose so don't see the point of what you're saying. Is it about the namespaces or the required Any?
  • I recognize my proposal doesn't solve any immediate source-compatibility problems; in fact it creates an eventual source-compatibility problem. The questions I'm trying to ask are:
    1. How does my proposal sound as a place to end up?

      Note that I think Foo where _ does more to call out the unknown types and therefore the possible unavailability of API than Any<Foo> does, and it doesn't burden constrained existentials with the extra Any<…> syntax which AFAICT only benefits the unconstrained existential case.

      Note also that I include solving the technical problems your proposal addresses as part of the goal.

    2. If “good,” then what would the source evolution story be?

    3. What does that story imply about immediate next steps in the language?

1 Like

Thinking through this over the holiday and reading through the arguments, ultimately I think I come down in favor of @Joe_Groff's pitch. tl;dr: I don't think we should let the perfect be the enemy of the good.

@dabrahams raises a lot of good points but IMHO relaxing the artificial restriction on existentials doesn't change the status quo (we already run into these issues and relaxing the rules solves some real problems). From what I can tell, source compatibility means future versions of the language must solve this problem in a way that isn't seriously constrained by this pitch and the restriction imposes its own set of artificial design constraints that are no worse than the theoretical ones Dave raises (though reasonable people might disagree on that).

This discussion has been enlightening; it certainly raises the desire to tackle the existential problem in Swift 6.

(Life's existential problems are left as an exercise for the reader)

7 Likes

A type system that needs these kind of hack is at best unfinished.. ... ABI stability serves Apple’s needs, but does not make the language usable for people coming from more complete type systems (no .. that does not define python).
Btw... this is JUST an opinion

I'd love to see this restriction lifted as well. Has there been any movement on including this in Swift 6?

3 Likes

agree, type erasure can't help us forever

2 Likes

I'd love to see this pitch get more traction.

2 Likes
Terms of Service

Privacy Policy

Cookie Policy