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

AnyHashableSendable ergonomics #47

Merged
merged 4 commits into from
Nov 11, 2024
Merged

Conversation

stephencelis
Copy link
Member

@stephencelis stephencelis commented Oct 23, 2024

While working with this type I noticed a few deficiencies:

  • First, the initializer takes an opaque type rather than an existential, and while existentials are typically automatically opened, there are cases in which this initializer will fail, e.g.:

    let x: (any Hashable & Sendable)?
    x.map(AnyHashableSendable.init)  // 🛑

    We can fix this by updating the initializer to be an any already.

  • Second, comparing an AnyHashableSendable with another AnyHashable fails because of the underlying type, but there is an underscored public protocol in the standard library we can take advantage of that is called when a hashable type is cast to AnyHashable, and if we return the base value then things like this start to work:

    AnyHashableSendable(1) == 1 as AnyHashable  // true
  • Third, while probably not common in practice, we can box up certain literals into AnyHashableSendable automatically, so let's do it. I do not think it's possible to box array or dictionary literals, but we can cover the more common cases.

While working with this type I noticed a couple deficiencies:

  - First, the initializer takes an opaque type rather than an
    existential, and while existentials are typically automatically
    opened, there are cases in which this initializer will fail,
    _e.g._:

    ```swift
    let x: (any Hashable & Sendable)?
    x.map(AnyHashableSendable.init)  // 🛑
    ```

    We can fix this by updating the initializer to be an `any` already.

  - Second, comparing an `AnyHashableSendable` with another
    `AnyHashable` fails because of the underlying type, but there is an
    underscored public protocol in the standard library we can take
    advantage of that is called when a hashable type is cast to
    `AnyHashable`, and if we return the base value then things like this
    start to work:

    ```swift
    AnyHashableSendable(1) == 1 as AnyHashable  // true
    ```
Comment on lines +45 to +47
public func _toCustomAnyHashable() -> AnyHashable? {
base as? AnyHashable
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Now the one behavior change to consider here is there's no way to recover the sendability anymore:

let x: AnyHashableSendable
let y: AnyHashable = x
y.base as? AnyHashableSendable      // `nil` now, previously not
y.base as? any Hashable & Sendable  // 🛑

🛑 Marker protocol 'Sendable' cannot be used in a conditional cast

@stephencelis stephencelis merged commit 6b0576e into main Nov 11, 2024
7 checks passed
@stephencelis stephencelis deleted the any-hashable-sendable-flexibility branch November 11, 2024 18:05
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.

2 participants