[Proposal] Generic and `throw`ing subscripts


(Beta) #1

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert Widmann <https://github.com/codafi>
Status: Awaiting review <https://github.com/typelift/SwiftCheck/pull/168#rationale>
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale

On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.


(Matthew Johnson) #2

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

+1. Thank you for bringing forward this proposal. I ran into these limitations in exactly the context you mention in the proposal.

Another context where this will be useful is emulating higher-rank "callable" types (using subscript for function invocation).

···

Sent from my iPhone

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Cheers,

~Robert Widmann

Generic and Throwing Subscripts
Proposal: SE-NNNN
Author(s): Harlan Haskins and Robert Widmann
Status: Awaiting review
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale
On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#3

I am no expert in throws, but since there's no closure involved I think that 'rethrows' doesn't apply here.

Dany

···

Le 20 juin 2016 à 14:10, Robert Widmann via swift-evolution <swift-evolution@swift.org> a écrit :

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type


(David Hart) #4

I may be wrong, but I think this proposal will not be merged or scheduled for review until August as it seems like post-Swift 3 material.

···

On 20 Jun 2016, at 20:10, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert Widmann <https://github.com/codafi>
Status: Awaiting review <https://github.com/typelift/SwiftCheck/pull/168#rationale>
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale

On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #5

I mentioned this in the PR, but for everyone’s benefit: Swift 3 is in its late end game. This is a very important release, and we need to stay focused on the goals for the release, at least until we pivot and start working on releases that follow it. I expect this time to be sometime around August. Until then, any evolution proposals that are strictly additive will need very strong rationale, explaining why they are critical for inclusion in Swift 3.

Here is my previous email about this:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160516/017701.html

-Chris

···

On Jun 20, 2016, at 11:10 AM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.


(plx) #6

Prefacing the below with a “I am well-aware this proposal likely won’t make it into Swift 3”:

A feature like this would be nice to use, but before I could get behind any proposal along these lines it I’d want to see it include an explicit strategy for disambiguation.

EG: in your example, your generic subscript uses `self[key]`, and presumably expects that to use the “original” subscript…and not the generic subscript being defined.

I think that’s reasonable in that specific case, but it doesn’t seem unreasonable to anticipate this proposal introducing ambiguities that would need explicit disambiguation…and for which explicit type annotation may not always be adequate to resolve (I could be wrong here, though).

This would need addressing (either showing they won’t be an issue, or providing a reliable disambiguation mechanism).

Relatedly, in-re: “rethrows”: if the syntax supported it, this kind of thing would be another way of tackling the "JSON problem":

  subscript<T>(key: Key, transform: (Value) throws -> T) rethrows -> T {
    guard let value = self[key] else { throw JSON.MissingKey(…) }
    return try transform(value)
  }

…so that e.g. you can write typical parsing-code as

  let asUserID = UserID.init(untrustedString:) // <- assume this is a "throwing constructor"
  let sender = try json[“sender”,asUserID]
  let recipient = try json[“recipient”,asUserID]

…(modulo any syntax errors, etc.), which would benefit from a `rethrows` declaration.

That’s my 2c; thankfully (IMHO) there’s clearly a lot of time for this proposal to simmer.

···

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert Widmann <https://github.com/codafi>
Status: Awaiting review <https://github.com/typelift/SwiftCheck/pull/168#rationale>
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale

On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Beta) #7

Closures are a valid argument to pass to a subscript. Granted, I’m not sure of a use-case, but it’s certainly something that is possible.

···

On Jun 20, 2016, at 5:54 PM, Dany St-Amant <dsa.mls@icloud.com> wrote:

Le 20 juin 2016 à 14:10, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type

I am no expert in throws, but since there's no closure involved I think that 'rethrows' doesn't apply here.

Dany


(David Rönnqvist) #8

Would this proposal in any way be related to `throw`ing properties (more specifically throwing setters)? Or would that be a completely different discussion/proposal?

- David

···

On 20 Jun 2016, at 21:27, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

+1. Thank you for bringing forward this proposal. I ran into these limitations in exactly the context you mention in the proposal.

Another context where this will be useful is emulating higher-rank "callable" types (using subscript for function invocation).

Cheers,

~Robert Widmann

Generic and Throwing Subscripts
Proposal: SE-NNNN
Author(s): Harlan Haskins and Robert Widmann
Status: Awaiting review
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale
On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #9

I will pop this out to its own thread, since it is more general than this topic.

···

On Jun 21, 2016, at 10:41 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 20, 2016, at 11:10 AM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

I mentioned this in the PR, but for everyone’s benefit: Swift 3 is in its late end game. This is a very important release, and we need to stay focused on the goals for the release, at least until we pivot and start working on releases that follow it. I expect this time to be sometime around August. Until then, any evolution proposals that are strictly additive will need very strong rationale, explaining why they are critical for inclusion in Swift 3.

Here is my previous email about this:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160516/017701.html


(TJ Usiyan) #10

plx: wouldn't the same overload resolution strategy be appropriate here?
"most specific choice, otherwise diagnostic"

separately:
Are there any subscripts in the standard library that would be
throwing/generic but can't be?

···

On Wed, Jun 22, 2016 at 9:13 AM, plx via swift-evolution < swift-evolution@swift.org> wrote:

Prefacing the below with a “I am well-aware this proposal likely won’t
make it into Swift 3”:

A feature like this would be nice to use, but before I could get behind
any proposal along these lines it I’d want to see it include an explicit
strategy for disambiguation.

EG: in your example, your generic subscript uses `self[key]`, and
presumably expects that to use the “original” subscript…and not the generic
subscript being defined.

I think that’s reasonable in that specific case, but it doesn’t seem
unreasonable to anticipate this proposal introducing ambiguities that would
need explicit disambiguation…and for which explicit type annotation may not
always be adequate to resolve (I could be wrong here, though).

This would need addressing (either showing they won’t be an issue, or
providing a reliable disambiguation mechanism).

Relatedly, in-re: “rethrows”: if the syntax supported it, this kind of
thing would be another way of tackling the "JSON problem":

  subscript<T>(key: Key, transform: (Value) throws -> T) rethrows -> T {
    guard let value = self[key] else { throw JSON.MissingKey(…) }
    return try transform(value)
  }

…so that e.g. you can write typical parsing-code as

  let asUserID = UserID.init(untrustedString:) // <- assume this is a
"throwing constructor"
  let sender = try json[“sender”,asUserID]
  let recipient = try json[“recipient”,asUserID]

…(modulo any syntax errors, etc.), which would benefit from a `rethrows`
declaration.

That’s my 2c; thankfully (IMHO) there’s clearly a lot of time for this
proposal to simmer.

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be
submitting shortly about adding generic and `throw`ing subscript
declarations to the language.

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

   - Proposal: SE-NNNN
   <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
   - Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert
   Widmann <https://github.com/codafi>
   - Status: Awaiting review
   <https://github.com/typelift/SwiftCheck/pull/168#rationale>
   - Review manager: TBD

Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare
new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged
functions, so we propose
adding generic constraints and throwing semantics to subscripts.
Motivation

On the throwing side, currently there are two ways to express a failing
subscript:

   - Return an Optional, failing with nil.
   - Call fatalError(_:slight_smile: on failure.

Both of these throw out useful information about the cause of the
underlying error that using Swift's error handling mechanism can otherwise
provide.

As for generics, to take an example, it has become a common pattern among
JSON decoding DSL libraries to express a throwing generic extension on
Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}

Given this, one can decode JSON with the full support of native type
inference and exception handling. But when working with the DSL, one would
expect to be able to express this as a subscript on Dictionary, allowing
the following:

//...
extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

We believe this is an even more natural way to write these kinds of
libraries in Swift and that bringing subscript member declarations up to
par with functions is a useful addition to the language as a whole.
Proposed solution

Add the ability to introduce new generic parameters and mark throws and
rethrows on subscript members.
Detailed design

This change will modify and add the following productions in the Swift
grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause+subscript-result → -> attributes(opt) throws(opt) type+subscript-result → -> attributes(opt) rethrows(opt) type

------------------------------
Rationale
On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Beta) #11

They are quite related considering a subscript can have a getter-setter pair. Brent already has a proposal to cover throwing properties (and subscripts), so we’re overlapping in that regard.

···

On Jun 20, 2016, at 1:33 PM, David Rönnqvist <david.ronnqvist@gmail.com> wrote:

Would this proposal in any way be related to `throw`ing properties (more specifically throwing setters)? Or would that be a completely different discussion/proposal?

- David

On 20 Jun 2016, at 21:27, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

+1. Thank you for bringing forward this proposal. I ran into these limitations in exactly the context you mention in the proposal.

Another context where this will be useful is emulating higher-rank "callable" types (using subscript for function invocation).

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert Widmann <https://github.com/codafi>
Status: Awaiting review <https://github.com/typelift/SwiftCheck/pull/168#rationale>
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale

On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(plx) #12

plx: wouldn't the same overload resolution strategy be appropriate here? "most specific choice, otherwise diagnostic”

I’d assume it’d be good enough for most cases, certainly; I’d love to turn out to be needlessly scaremongering here, too.

But (hopefully unintentionally!) it seems like you could easily wind up with scenarios like this:

  // from proposal:
  public subscript<T>(key: Key) throws -> T {
    guard let value = self[key] else { throw ... }
    guard let ofType = value as? T else { throw ... }
    return ofType
  }

  // an annoying addition:
  public subscript<T:RawRepresentable>(key: Key) throws -> T {
    guard let v = self[key] else { throw ... }
    guard let rawValue = v as? T.RawValue else { throw ... }
    guard let converted = T(rawValue: rawValue) else { throw ... }
    return converted
  }

  // assume `Foo:RawRepresentable`:
  let foo: Foo = json["foo"]

…and similar, where I’d assume the `T:RawRepresentable` would “win”, but I’m not sure *how* you could force use of the *other* subscript in the above.

This isn’t really a new problem, but it seems likelier to be encountered if generic subscripts become allowed, due to all subscripts having the same “name”.

But again I’d like to be wrong about the issue (or at least the severity).

···

On Jun 22, 2016, at 8:28 AM, T.J. Usiyan <griotspeak@gmail.com> wrote:

separately:
Are there any subscripts in the standard library that would be throwing/generic but can't be?

On Wed, Jun 22, 2016 at 9:13 AM, plx via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Prefacing the below with a “I am well-aware this proposal likely won’t make it into Swift 3”:

A feature like this would be nice to use, but before I could get behind any proposal along these lines it I’d want to see it include an explicit strategy for disambiguation.

EG: in your example, your generic subscript uses `self[key]`, and presumably expects that to use the “original” subscript…and not the generic subscript being defined.

I think that’s reasonable in that specific case, but it doesn’t seem unreasonable to anticipate this proposal introducing ambiguities that would need explicit disambiguation…and for which explicit type annotation may not always be adequate to resolve (I could be wrong here, though).

This would need addressing (either showing they won’t be an issue, or providing a reliable disambiguation mechanism).

Relatedly, in-re: “rethrows”: if the syntax supported it, this kind of thing would be another way of tackling the "JSON problem":

  subscript<T>(key: Key, transform: (Value) throws -> T) rethrows -> T {
    guard let value = self[key] else { throw JSON.MissingKey(…) }
    return try transform(value)
  }

…so that e.g. you can write typical parsing-code as

  let asUserID = UserID.init(untrustedString:) // <- assume this is a "throwing constructor"
  let sender = try json[“sender”,asUserID]
  let recipient = try json[“recipient”,asUserID]

…(modulo any syntax errors, etc.), which would benefit from a `rethrows` declaration.

That’s my 2c; thankfully (IMHO) there’s clearly a lot of time for this proposal to simmer.

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert Widmann <https://github.com/codafi>
Status: Awaiting review <https://github.com/typelift/SwiftCheck/pull/168#rationale>
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale

On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #13

plx: wouldn't the same overload resolution strategy be appropriate here? "most specific choice, otherwise diagnostic”

I’d assume it’d be good enough for most cases, certainly; I’d love to turn out to be needlessly scaremongering here, too.

But (hopefully unintentionally!) it seems like you could easily wind up with scenarios like this:

  // from proposal:
  public subscript<T>(key: Key) throws -> T {
    guard let value = self[key] else { throw ... }
    guard let ofType = value as? T else { throw ... }
    return ofType
  }

  // an annoying addition:
  public subscript<T:RawRepresentable>(key: Key) throws -> T {
    guard let v = self[key] else { throw ... }
    guard let rawValue = v as? T.RawValue else { throw ... }
    guard let converted = T(rawValue: rawValue) else { throw ... }
    return converted
  }

  // assume `Foo:RawRepresentable`:
  let foo: Foo = json["foo"]

…and similar, where I’d assume the `T:RawRepresentable` would “win”, but I’m not sure *how* you could force use of the *other* subscript in the above.

This isn’t really a new problem, but it seems likelier to be encountered if generic subscripts become allowed, due to all subscripts having the same “name”.

This isn’t exactly true. External argument labels are considered part of the “name” in Swift. Subscripts parameters don’t get external labels automatically like other functions / methods do, but you can still add one if you *want* the ability to disambiguate.

Of course this won’t help if you require the ability to use both subscripts without a label but is worth noting. It is also worth noting that this behavior is no different from that of any other function or method - if the name (including external argument labels) matches the most specific overload will always be selected.

One way to make your example work properly when `T` is `RawRepresentable` and the dictionary actually contains an instance of `T` is to add an extra check for that case:

  public subscript<T:RawRepresentable>(key: Key) throws -> T {
    guard let v = self[key] else { throw … }

    // extra check here in case the value is *already* T and therefore does not require conversion.
    if let value = v as? T { return value }

    guard let rawValue = v as? T.RawValue else { throw ... }
    guard let converted = T(rawValue: rawValue) else { throw ... }
    return converted
  }

···

On Jun 22, 2016, at 9:11 AM, plx via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 22, 2016, at 8:28 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

But again I’d like to be wrong about the issue (or at least the severity).

separately:
Are there any subscripts in the standard library that would be throwing/generic but can't be?

On Wed, Jun 22, 2016 at 9:13 AM, plx via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Prefacing the below with a “I am well-aware this proposal likely won’t make it into Swift 3”:

A feature like this would be nice to use, but before I could get behind any proposal along these lines it I’d want to see it include an explicit strategy for disambiguation.

EG: in your example, your generic subscript uses `self[key]`, and presumably expects that to use the “original” subscript…and not the generic subscript being defined.

I think that’s reasonable in that specific case, but it doesn’t seem unreasonable to anticipate this proposal introducing ambiguities that would need explicit disambiguation…and for which explicit type annotation may not always be adequate to resolve (I could be wrong here, though).

This would need addressing (either showing they won’t be an issue, or providing a reliable disambiguation mechanism).

Relatedly, in-re: “rethrows”: if the syntax supported it, this kind of thing would be another way of tackling the "JSON problem":

  subscript<T>(key: Key, transform: (Value) throws -> T) rethrows -> T {
    guard let value = self[key] else { throw JSON.MissingKey(…) }
    return try transform(value)
  }

…so that e.g. you can write typical parsing-code as

  let asUserID = UserID.init(untrustedString:) // <- assume this is a "throwing constructor"
  let sender = try json[“sender”,asUserID]
  let recipient = try json[“recipient”,asUserID]

…(modulo any syntax errors, etc.), which would benefit from a `rethrows` declaration.

That’s my 2c; thankfully (IMHO) there’s clearly a lot of time for this proposal to simmer.

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert Widmann <https://github.com/codafi>
Status: Awaiting review <https://github.com/typelift/SwiftCheck/pull/168#rationale>
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale

On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(plx) #14

You’re right on the specifics, of course.

To be clear I am more concerned about accidental shadowing with such subscripts; picture starting initial development with only the generic subscript from the proposal, writing some code that uses it…and then at some later point in time importing a module that defines the `RawRepresentable` variant, which would subtly shift how your code got interpreted.

This isn’t anything that couldn’t happen today with plain-old functions, of course — and it can be worked around in various ways, if you know it happens — but given the IMHO much higher likelihood of "name collisions” (you’re correct you can label the subscript arguments, but I doubt most subscripts will opt to do so)…it seems far likelier we’ll see resolution-modifying collisions under a proposal like this, should it be introduced.

But I could be very wrong, and in any case this proposal should probably wait until at least August, as per suggestion.

···

On Jun 22, 2016, at 9:29 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jun 22, 2016, at 9:11 AM, plx via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 22, 2016, at 8:28 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

plx: wouldn't the same overload resolution strategy be appropriate here? "most specific choice, otherwise diagnostic”

I’d assume it’d be good enough for most cases, certainly; I’d love to turn out to be needlessly scaremongering here, too.

But (hopefully unintentionally!) it seems like you could easily wind up with scenarios like this:

  // from proposal:
  public subscript<T>(key: Key) throws -> T {
    guard let value = self[key] else { throw ... }
    guard let ofType = value as? T else { throw ... }
    return ofType
  }

  // an annoying addition:
  public subscript<T:RawRepresentable>(key: Key) throws -> T {
    guard let v = self[key] else { throw ... }
    guard let rawValue = v as? T.RawValue else { throw ... }
    guard let converted = T(rawValue: rawValue) else { throw ... }
    return converted
  }

  // assume `Foo:RawRepresentable`:
  let foo: Foo = json["foo"]

…and similar, where I’d assume the `T:RawRepresentable` would “win”, but I’m not sure *how* you could force use of the *other* subscript in the above.

This isn’t really a new problem, but it seems likelier to be encountered if generic subscripts become allowed, due to all subscripts having the same “name”.

This isn’t exactly true. External argument labels are considered part of the “name” in Swift. Subscripts parameters don’t get external labels automatically like other functions / methods do, but you can still add one if you *want* the ability to disambiguate.

Of course this won’t help if you require the ability to use both subscripts without a label but is worth noting. It is also worth noting that this behavior is no different from that of any other function or method - if the name (including external argument labels) matches the most specific overload will always be selected.

One way to make your example work properly when `T` is `RawRepresentable` and the dictionary actually contains an instance of `T` is to add an extra check for that case:

  public subscript<T:RawRepresentable>(key: Key) throws -> T {
    guard let v = self[key] else { throw … }

    // extra check here in case the value is *already* T and therefore does not require conversion.
    if let value = v as? T { return value }

    guard let rawValue = v as? T.RawValue else { throw ... }
    guard let converted = T(rawValue: rawValue) else { throw ... }
    return converted
  }

But again I’d like to be wrong about the issue (or at least the severity).

separately:
Are there any subscripts in the standard library that would be throwing/generic but can't be?

On Wed, Jun 22, 2016 at 9:13 AM, plx via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Prefacing the below with a “I am well-aware this proposal likely won’t make it into Swift 3”:

A feature like this would be nice to use, but before I could get behind any proposal along these lines it I’d want to see it include an explicit strategy for disambiguation.

EG: in your example, your generic subscript uses `self[key]`, and presumably expects that to use the “original” subscript…and not the generic subscript being defined.

I think that’s reasonable in that specific case, but it doesn’t seem unreasonable to anticipate this proposal introducing ambiguities that would need explicit disambiguation…and for which explicit type annotation may not always be adequate to resolve (I could be wrong here, though).

This would need addressing (either showing they won’t be an issue, or providing a reliable disambiguation mechanism).

Relatedly, in-re: “rethrows”: if the syntax supported it, this kind of thing would be another way of tackling the "JSON problem":

  subscript<T>(key: Key, transform: (Value) throws -> T) rethrows -> T {
    guard let value = self[key] else { throw JSON.MissingKey(…) }
    return try transform(value)
  }

…so that e.g. you can write typical parsing-code as

  let asUserID = UserID.init(untrustedString:) // <- assume this is a "throwing constructor"
  let sender = try json[“sender”,asUserID]
  let recipient = try json[“recipient”,asUserID]

…(modulo any syntax errors, etc.), which would benefit from a `rethrows` declaration.

That’s my 2c; thankfully (IMHO) there’s clearly a lot of time for this proposal to simmer.

On Jun 20, 2016, at 1:10 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Good morning all. Attached is the proposal Harlan Haskins and I will be submitting shortly about adding generic and `throw`ing subscript declarations to the language.

Cheers,

~Robert Widmann

Generic and Throwing Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Harlan Haskins <https://github.com/harlanhaskins> and Robert Widmann <https://github.com/codafi>
Status: Awaiting review <https://github.com/typelift/SwiftCheck/pull/168#rationale>
Review manager: TBD
Introduction

Currently, subscripts cannot be declared [re]throws and cannot declare new generic parameters.
There isn't a clear reason why they aren't as capable as full-fledged functions, so we propose
adding generic constraints and throwing semantics to subscripts.

Motivation

On the throwing side, currently there are two ways to express a failing subscript:

Return an Optional, failing with nil.
Call fatalError(_:slight_smile: on failure.
Both of these throw out useful information about the cause of the underlying error that using Swift's error handling mechanism can otherwise provide.

As for generics, to take an example, it has become a common pattern among JSON decoding DSL libraries to express a throwing generic extension on Dictionary like so

extension Dictionary {
    public func parse<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}

public enum JSONError: ErrorType, CustomStringConvertible {
    case InvalidKey(key: String, expectedType: Any.Type, foundType: Any.Type)
    case MissingKey(String)
    public var description: String {
        switch self {
        case .InvalidKey(let key, let expected, let found):
            return "Invalid key \"\(key)\". Expected value of type \"\(expected)\", found \"\(found)\"."
        case .MissingKey(let key):
            return "Key \(key) not found."
        }
    }
}
Given this, one can decode JSON with the full support of native type inference and exception handling. But when working with the DSL, one would expect to be able to express this as a subscript on Dictionary, allowing the following:

//...

extension Dictionary {
    public subscript<T>(key: Key) throws -> T {
        guard let value = self[key] else {
            throw JSONError.MissingKey("\(key)")
        }
        guard let ofType = value as? T else {
            throw JSONError.InvalidKey(key: "\(key)", expectedType: T.self, foundType: value.dynamicType)
        }
        return ofType
    }
}
We believe this is an even more natural way to write these kinds of libraries in Swift and that bringing subscript member declarations up to par with functions is a useful addition to the language as a whole.

Proposed solution

Add the ability to introduce new generic parameters and mark throws and rethrows on subscript members.

Detailed design

This change will modify and add the following productions in the Swift grammar

GRAMMAR OF A SUBSCRIPT DECLARATION

subscript-declaration → subscript-head subscript-result code-block
subscript-declaration → subscript-head subscript-result getter-setter-block
subscript-declaration → subscript-head subscript-result getter-setter-keyword-block
-subscript-head → attributes(opt) declaration-modifiers(opt) subscript parameter-clause
+subscript-head → attributes(opt) declaration-modifiers(opt) generic-parameter-clause(opt) subscript parameter-clause
+subscript-result → -> attributes(opt) throws(opt) type
+subscript-result → -> attributes(opt) rethrows(opt) type
Rationale

On [Date], the core team decided to (TBD) this proposal.
When the core team makes a decision regarding this proposal,
their rationale for the decision will be written here.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution