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

Merged
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
a4a5adf
Added Draft.
filip-sakel Sep 4, 2020
a91f478
Review Updates
filip-sakel Sep 5, 2020
445c7f3
Review Updates.
filip-sakel Sep 5, 2020
40b05a8
Review Updates.
filip-sakel Sep 5, 2020
323a25a
Removed first example from Detailed Design.
filip-sakel Sep 6, 2020
a0057fe
Changes per review.
filip-sakel Sep 10, 2020
b80a579
Clarified current limitations in Motivation.
filip-sakel Sep 11, 2020
d27230f
Updated 'error message'.
filip-sakel Sep 11, 2020
1aa61d1
Updated formatting.
filip-sakel Sep 11, 2020
50a9916
Updated Formatting.
filip-sakel Sep 11, 2020
ecbbf4c
Replaced dots(...) with mock types.
filip-sakel Sep 11, 2020
97640d2
Fixed typo and clarified existential qualifiaction
filip-sakel Sep 12, 2020
8f198a8
Updated naming.
filip-sakel Sep 12, 2020
700ec25
Added note for Conflicting Types in Compositions.
filip-sakel Sep 25, 2020
3aa4b1f
Updated formatting.
filip-sakel Oct 18, 2020
86bb68c
Added Existentials in the Standard Library section
filip-sakel Oct 18, 2020
d4b9f44
Strengthened motivation by using Strideable.
filip-sakel Oct 18, 2020
65daf57
Rewrote proposal; this is a draft.
filip-sakel Nov 24, 2020
5218234
Added a link to SwiftUI.
filip-sakel Dec 3, 2020
2518bd3
Updated API Resilience.
filip-sakel Dec 3, 2020
1ca02f9
Added API/ABI Resilience Argument
filip-sakel Dec 3, 2020
29c19fd
Removed redundant words / Heterogenous Collections
filip-sakel Dec 3, 2020
e8cc29d
Removed unnecessary list of supported existentials
filip-sakel Dec 3, 2020
c919e8c
Removed unnecessary list of supported existentials
filip-sakel Dec 3, 2020
8e53926
Removed unnecessary connector in Motivation.
filip-sakel Dec 3, 2020
6eb13b0
Fixed type pointed out by Dave.
filip-sakel Dec 9, 2020
412dc49
Undo incorrect commit.
filip-sakel Dec 9, 2020
3b819cc
Removed unneeded Heterogenous Collections info.
filip-sakel Jan 3, 2021
be0d18d
Replaced "synthesized" with "generated".
filip-sakel Jan 3, 2021
e151181
Clarified existentials definition in Introduction.
filip-sakel Jan 3, 2021
bace022
Clarified Introduction section.
filip-sakel Jan 3, 2021
4b58873
A first round of amendments to the motivation
AnthonyLatsis Jan 23, 2021
0588dc1
Rephrase the introduction to clarify our intentions
AnthonyLatsis Jan 25, 2021
24a3ec2
Address review comments
AnthonyLatsis Feb 2, 2021
16e27a9
Rework the proposed solution and most of the motivation
AnthonyLatsis Feb 7, 2021
0e6f003
Reduce the Effects sections
AnthonyLatsis Feb 8, 2021
13bcd2c
Changes in wording and typo corrections.
filip-sakel Feb 9, 2021
93e80ca
Downside of -> Downside to
filip-sakel Feb 13, 2021
c6d414e
Update proposals/NNNN-unlock-existential-types-for-all-protocols.md
AnthonyLatsis Feb 14, 2021
7b3db88
Revert to "of existential type".
filip-sakel Feb 14, 2021
ffcbed6
Make Motivation more specific.
filip-sakel Feb 14, 2021
42e7918
Add the alternative of leaving the language as is.
filip-sakel Feb 24, 2021
3c93a1f
Update NNNN-unlock-existential-types-for-all-protocols.md
AnthonyLatsis Mar 19, 2021
1e584d4
Update NNNN-unlock-existential-types-for-all-protocols.md
AnthonyLatsis Mar 19, 2021
927fec1
Remove Heterogenous Collections section.
filip-sakel Mar 23, 2021
6056aba
Remove section for deemphasizing existentials.
filip-sakel Mar 23, 2021
be883bd
Decrease section about simplifying existentials.
filip-sakel Mar 24, 2021
016c458
Update NNNN-unlock-existential-types-for-all-protocols.md
AnthonyLatsis Mar 24, 2021
0ebd95c
Update NNNN-unlock-existential-types-for-all-protocols.md
AnthonyLatsis Mar 25, 2021
7ec8f02
Capitalize "c" in Non-conformable Existentials
filip-sakel Mar 25, 2021
d49cc87
Remove "Leave the Language in Its Existing State".
filip-sakel Mar 25, 2021
0534ab0
Revert title alteration.
filip-sakel Mar 25, 2021
df75054
Add direction for AnyHashable existential init.
filip-sakel Mar 25, 2021
61f6a8e
Update NNNN-unlock-existential-types-for-all-protocols.md
AnthonyLatsis Mar 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove section for deemphasizing existentials.
  • Loading branch information
filip-sakel authored Mar 23, 2021
commit 6056aba4e2517857746098f1e8e9b2c4f6a160a7
66 changes: 0 additions & 66 deletions proposals/NNNN-unlock-existential-types-for-all-protocols.md
Original file line number Diff line number Diff line change
@@ -310,72 +310,6 @@ The current semantics regarding existential types prevent language users, especi

## 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.


### Deemphasize Existential Types Through Diagnostics and Literature

It is often that people reach for existential types when they should be employing generic contraints — "should" not merely for performance reasons, but because they truly do not need or intend for any type erasure. Even though the compiler is sometimes able to back us up performance-wise by turning existential code into generic code (as in `func foo(s: Sequence)` vs `func foo<S: Sequence>(s: S)`), there is an important difference between the two abstractions. Existential types provide value-level abstraction, that is, they eliminate the type-level distinction between different values of the type, and cannot maintain type relationships between independent existential values. Under most cirumstances, value-level abstraction only really makes sense in mutable state, in the elements of heterogeneous containers, or within larger type-erasing constructs — *unless* our support of [`some` types](https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md) can turn the tide.

The language documentation may play a crucial role in deemphasizing value-level abstraction early on. Namely, when one goes to the swift documentation site, under the protocols section, they’ll be greeted with a subsection called "[Protocols as Types](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID275)", which contains this example:

```swift
class Dice {

let sides: Int

let generator: RandomNumberGenerator
// Here, ‘generator’ is bound to the existential
// type of ‘RandomNumberGenerator’


init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}


func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}

}
```

The authors of this section are not to blame for the inappropriate use of an existential type, as this section was written before [opaque types]() where introduced. However, now it seems that documentation and diagnostics can be improved to mitigate this problem. Therefore, we could encourage the use of opaque result types:

```swift
let randomNumberGenerator: some RandomNumberGenerator = …
```

Or offer different diagnostics for the following code:

```swift
struct Dice {

let generator: some RandomNumberGenerator ❌
// Error: Property declares an opaque return type,
// but has no initializer expression from which to infer
// an underlying type.

init(sides: Int, generator: some RandomNumberGenerator) { ❌
// Error: 'some' types are only implemented for the declared
// type of properties and subscripts and the return type of functions

self.sides = sides
self.generator = generator
}


}
```

Maybe, we could provide a fix-it message that prompts us to use generics:

> Did you mean to use a generic parameter conforming to `RandomNumberGenerator` in struct `Dice`?

As for the second case, such syntax could be made valid in a future proposal, as it is quite unambiguous and could be equivalent to `<NumberGenerator : RandomNumberGenerator>`.

Lastly, the overall documentation could use existential types only when appropriate and note that it's different from generics.

### Simplify the Implementation of Type-Erasing Wrappers

Currently, in the standard library there's a custom existential type for `Hashable`, which is called [`AnyHashable`](https://developer.apple.com/documentation/swift/anyhashable). The current implementation of `AnyHashable` is quite complex and unintuitive. However, with the proposed change it could be simplfied and from 306 to 237 lines of code, while having a much simpler mental medal. The important thing to note about `AnyHashable` is that it conforms to the protocol it is an existential of: `Hashable`, which is something that the compiler can't automatically provide. Furthermore, `Equatable` has `Self` requirements in non-covariant positions, which means that such requirements are inaccessible through the existential type. To combat the last limitation, an internal extension to `Equatable` could be added: