Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unlock Existential Types for All Protocols #1176

Conversation

filip-sakel
Copy link
Contributor

@filip-sakel filip-sakel commented Sep 4, 2020

This proposal aims to add support for existential types derived from any protocol.

Forums Thread: https://forums.swift.org/t/lifting-the-self-or-associated-type-constraint-on-existentials/18025

Implementation: swiftlang/swift#33767

@filip-sakel filip-sakel marked this pull request as ready for review September 4, 2020 14:35
@theblixguy
Copy link
Contributor

theblixguy commented Sep 11, 2020

Looks great! I think we can pitch it on the forums soon once all suggestions above (plus ones mentioned in forums private messages, like warnings about compositions with conflicting constraints) have been addressed, in order to start collecting any feedback.

Added a note about how a composition of protocols that share same-name associated types with different implementations will emit a warning per the forums discussion.
Comments on the forums pitch indicated interest in adding a section discussing how standard library existential types could be transformed under this proposal. The section discusses relying on the automatically-generated existential types and adding conformance to the "base" protocol as a special case – similar to error.
Feedback on the forums pitch suggested that the proposal utilize real-world use-cases as examples instead of 'Identifiable'. The use of 'IntegerStridable' is intentional as knowing the concrete 'Stride' type is unnecessary.
@benrimmington
Copy link
Contributor

You'll need to change the base branch of this pull request to main.

@filip-sakel filip-sakel changed the base branch from master to main November 12, 2020 15:03
@filip-sakel filip-sakel marked this pull request as draft November 13, 2020 06:15
Copy link
Contributor

@AnthonyLatsis AnthonyLatsis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you guys think? Unless you have been planning to add something, I think we're set up to ask Joe to schedule the SE review after this session.

@theblixguy
Copy link
Contributor

What do you guys think? Unless you have been planning to add something, I think we're set up to ask Joe to schedule the SE review after this session.

Sounds good! I’ll have another look at it today (sorry, I had taken a break from compiler/SE contributions but I’m back now) & then I think it should be good to go!

@filip-sakel
Copy link
Contributor Author

I had been quite busy too; now, though, I have more free time. So, yes, I think we can request a review or create a pitch thread — I'm not sure if we should first do the latter.


The current semantics regarding existential types prevent language users, especially library authors, from inheriting useful protocols due to the high cost of maintaining a type erasure construct. Namely, the high maintenance cost is a result of type erasure containers constituting hard-to-understand boilerplate code that entails thorough testing and a significant effort when expanding the protocol at hand. Furthermore, regular language users are often confused by the current restriction, and often lack the expertise or resources required for creating correct type-erasure contructs.

## Future Directions
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that we should add a future direction for opening an existential, and then wrapping it into a type-erasure container? For instance, init(_ box: Hashable) could be added to AnyHashable. I'm not sure, though, if this addition could be briefly stated in an existing section, or if we'd have to create a new one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bullet point for "automatically opening existentials when passed as generic arguments" at the bottom. That would allow the generic initializer of AnyHashable to accept a Hashable value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would require some implementation work; at the same time, users will be left unable to initialize standard-library-provided type erasers with their language-provided protocol counterparts. I'm not sure if this feature should be part of the proposal, because I think it could create a major usability issue, due to the lack of a feature that can be easily added to the standard library.

Sorry, if I've missed some aspect of the proposed feature that already enables this behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, the part about "automatically opening existential values" is a future direction (it was also mentioned as such a couple of times in the forums); we are not proposing or implementing it now.

users will be left unable to initialize standard-library-provided type erasers with their language-provided protocol counterparts.

In case this is still relevant — why is that? Self-conformance does allow you to initialize an AnyProtocol wrapper with a Protocol value, among other stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't very clear.

I'm arguing that implementing "self-conforming" existential types would require some implementation work, which is — obviously — not in scope for this proposal.

Yet, the current proposal (without self-conformance) creates serious usability issues — provided that I haven't missed anything — since users will be unable to write something like AnyHashable(1 as Hashable).

What I propose we do, is include:

extension AnyHashable {
  public init(_ box: Hashable) {
    self = _openExistential(box, do: { unboxed in
      AnyHashable(unboxed)
    })
  }
}

Similarly, we could provide such an initializer for AnyCollection and any other type-erasing, standard-library wrappers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. But AnyCollection is not really an option, because we need to somehow represent the Element type once we get the wrapper back.

Right. This, then, leaves us just with AnyHashable.

I have mixed feelings about a permanent workaround to a temporary issue, given that users can add the workaround themselves if they happen to need a new Hashable -> AnyHashable conversion in the meantime. You are totally welcome to add it as a future direction though.

I understand your concern. I think whether a workaround will be implemented depends on the timing of the follow-up proposal for automatically opening existentials.

How about the following for a future direction bullet:

Add an init(_ box: Hashable) initializer to AnyHashable to alleviate confusion and aid in usability. This initializer should be treated as a workaround, and removed once automatic opening of existential types becomes available.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds fine, but

...and removed once automatic opening of existential types becomes available.

this would break the ABI (we cannot remove something public once added).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I meant "deprecated". Then, after a major version of being deprecated, it could be removed — if that's an option. That, of course, entails that init(_ box: Hashable) be marked @_disfavoredOverload, in addition to deprecated.

Is this okay?

Add an init(_ box: Hashable) initializer to AnyHashable to alleviate confusion and aid in usability. This initializer should be treated as a workaround, and deprecated once automatic opening of existential types becomes available.

Copy link
Contributor

@AnthonyLatsis AnthonyLatsis Mar 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • "should" -> "would"
  • "once" -> "if/when"

LGTM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just changed if/when ... becomes available to should ... become available.

@AnthonyLatsis
Copy link
Contributor

So, yes, I think we can request a review or create a pitch thread — I'm not sure if we should first do the latter.

I reckon we're fine with two pitch threads and that much discourse 🙂.

@filip-sakel filip-sakel marked this pull request as ready for review March 24, 2021 15:53
Copy link
Contributor

@theblixguy theblixguy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fantastic! I only have some very minor comments!

# Unlock existentials for all protocols

* Proposal: [SE-NNNN](NNNN-unlock-existential-types-for-all-protocols.md)
* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis), [Filip Sakel](https://github.com/filip-sakel), [Joe Groff](https://github.com/jckarter), [Suyash Srijan](https://github.com/theblixguy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate it, but I don't think I really need author credit for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants