[Pitch] Named subscripts


(Adrian Zubarev) #1

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {
         
    public func double(at index: Int) -> Double? {
         
        guard self.startIndex <= index && index < self.endIndex else { return nil }
         
        if case .double(let double) = self[index] {
             
            return double
        }
        return nil
    }
     
    …
}
This function is used to query the array and check if the element at the given index is of a specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at index: Int) -> …. You can easily imagine that such subscripts would look ugly array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and clean. array.scopedJavaScript[at: 213] = …

This also might be the opportunity to rethink the labeling rule on subscripts, but this shall not be the main focus of this pitch.

···

--
Adrian Zubarev
Sent with Airmail


(Dave Abrahams) #2

You do that by giving your Array a scopedJavaScript property, and
making that indexable.

···

on Thu Nov 17 2016, Adrian Zubarev <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {

    public func double(at index: Int) -> Double? {

        guard self.startIndex <= index && index < self.endIndex else { return nil }

        if case .double(let double) = self[index] {

            return double
        }
        return nil
    }

    …
}
This function is used to query the array and check if the element at the given index is of a
specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript
the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at
index: Int) -> …. You can easily imagine that such subscripts would look ugly
array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript
a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and
clean. array.scopedJavaScript[at: 213] = …

--
-Dave


(Haravikk) #3

Could this be addressed by allowing generic constraints on subscripts?
For example, with methods we can currently do:

struct Foo {
  var values:[Any] = []

  func get<T>(at:Int) -> T? {
    return values.indices.contains(at) ? values[at] as? T : nil
  }

  func get<T>(at:Int, as theType:T.Type) -> T? {
    return values.indices.contains(at) ? values[at] as? T : nil
  }

  mutating func set<T>(at:Int, to:T) {
    if values.indices.contains(at) { values[at] = to }
  }
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo.get(at: 0, as: Double.self)
let b:Double = foo.get(at: 1)!
let c:Int? = foo.get(at: 2)
let d = foo.get(at: 3, as: Double.self)
let e:Int = foo.get(at: 4)!
let f = foo.get(at: 5, as: Int.self)
i.e- the type is inferred from the call-site either with an explicit variable type, or by passing in the expected type as the second argument, which I think is a pretty neat way to do it.

If we could do the same with subscripts we could do something like:

struct Foo {
  var values:[Any] = []

  subscript<T>(_ at:Int) -> T? {
    get { return values.indices.contains(at) ? values[at] as? T : nil }
    set { if values.indices.contains(at) { values[at] = newValue } }
  }

  subscript<T>(_ at:Int, as theType:T.Type) -> T? {
    return values.indices.contains(at) ? values[at] as? T : nil
  }
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo[0, as: Double.self]
let b:Double = foo[1]!
let c:Int? = foo[2]
let d = foo[3, as: Double.self]
let e:Int = foo[4]!
let f = foo[5, as: Int.self]

Are generic constraints on subscripts part of the generics manifesto?

···

On 17 Nov 2016, at 20:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {
         
    public func double(at index: Int) -> Double? {
         
        guard self.startIndex <= index && index < self.endIndex else { return nil }
         
        if case .double(let double) = self[index] {
             
            return double
        }
        return nil
    }
     
    …
}
This function is used to query the array and check if the element at the given index is of a specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at index: Int) -> …. You can easily imagine that such subscripts would look ugly array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and clean. array.scopedJavaScript[at: 213] = …

This also might be the opportunity to rethink the labeling rule on subscripts, but this shall not be the main focus of this pitch.

--
Adrian Zubarev
Sent with Airmail

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


(Adrian Zubarev) #4

Hi Dave,

Thank you for your answer. I have to admit this is a ‘workaround’ but it will make everything even worse.

From:

public func scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
To:

public subscript(at index: Int) -> (javaScript: String, scope: Document)?

public var scopedJavaScript: Array {
    get { return self }
    set { /* implementation artifact */ }
}
Now I could write code like array.scopedJavaScript.scopedJavaScript.scopedJavaScript and so one, which makes no sense any more.

Where we could simply allow:

public subscript scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
This would ensure that the user can only write something like:

array.scopedJavaScript[at: 42] // get the value
array.scopedJavaScript[at: 42] = (…, …) // set the value
Is there anything that speaks against optionally named subscripts?
Technical reasons?
Swiftiness?

···

--
Adrian Zubarev
Sent with Airmail

Am 17. November 2016 um 23:33:44, Dave Abrahams via swift-evolution (swift-evolution@swift.org) schrieb:

on Thu Nov 17 2016, Adrian Zubarev <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {

public func double(at index: Int) -> Double? {

guard self.startIndex <= index && index < self.endIndex else { return nil }

if case .double(let double) = self[index] {

return double
}
return nil
}


}
This function is used to query the array and check if the element at the given index is of a
specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript
the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at
index: Int) -> …. You can easily imagine that such subscripts would look ugly
array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript
a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and
clean. array.scopedJavaScript[at: 213] = …

You do that by giving your Array a scopedJavaScript property, and
making that indexable.

--
-Dave

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


(Xiaodi Wu) #5

I believe Dave had a design more like this in mind:

struct _View<T> {
    let array: Array<Any>
    subscript(index: Int) -> T? {
        guard index >= 0 && index < array.count else { return nil }
        return array[index] as? T
    }
}

extension Array {
    var double: _View<Double> {
        return _View(array: self)
    }
}
···

On Fri, Nov 18, 2016 at 2:00 AM, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

Hi Dave,

Thank you for your answer. I have to admit this is a ‘workaround’ but it
will make everything even worse.

From:

public func scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?

To:

public subscript(at index: Int) -> (javaScript: String, scope: Document)?

public var scopedJavaScript: Array {
    get { return self }
    set { /* implementation artifact */ }
}

Now I could write code like array.scopedJavaScript.scopedJ
avaScript.scopedJavaScript and so one, which makes no sense any more.

Where we could simply allow:

public subscript scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?

This would ensure that the user can only write something like:

array.scopedJavaScript[at: 42] // get the value
array.scopedJavaScript[at: 42] = (…, …) // set the value

   - Is there anything that speaks against optionally named subscripts?
   - Technical reasons?
   - Swiftiness?

--
Adrian Zubarev
Sent with Airmail

Am 17. November 2016 um 23:33:44, Dave Abrahams via swift-evolution (
swift-evolution@swift.org) schrieb:

on Thu Nov 17 2016, Adrian Zubarev <swift-evolution@swift.org> wrote:

> Dear Swift community,
>
> while building a framework for BSON I had the following idea.
>
> Here is a snippet of some code I do have in my module:
>
> extension Array where Element == Document.Value {
>
> public func double(at index: Int) -> Double? {
>
> guard self.startIndex <= index && index < self.endIndex else { return
nil }
>
> if case .double(let double) = self[index] {
>
> return double
> }
> return nil
> }
>
> …
> }
> This function is used to query the array and check if the element at the
given index is of a
> specific type. Now I would like also to implement a semi-schema setter.
>
> The problem that I see, is the ugliness of the subscript I’d create.
>
> Currently the code would read nicely let d = array.double(at: 42), but
after change to a subscript
> the API would look odd array[doubleAt: 42] = 5.0.
>
> Don’t get me wrong here, I also have methods with larger names like
public func scopedJavaScript(at
> index: Int) -> …. You can easily imagine that such subscripts would look
ugly
> array[scopedJavaScriptAt: 123] = ….
>
> I propose to align the design of subscript with functions where one
could optionally give subscript
> a name.
>
> func name(label parameter: Type) -> ReturnType
>
> subscript optionalName(label parameter: Type) -> ReturnType
> This change would make my API nice and
> clean. array.scopedJavaScript[at: 213] = …

You do that by giving your Array a scopedJavaScript property, and
making that indexable.

--
-Dave

_______________________________________________
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


(Dave Abrahams) #6

Yup :grinning:

···

Sent from my moss-covered three-handled family gradunza

On Nov 18, 2016, at 12:18 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I believe Dave had a design more like this in mind:

struct _View<T> {
    let array: Array<Any>
    subscript(index: Int) -> T? {
        guard index >= 0 && index < array.count else { return nil }
        return array[index] as? T
    }
}

extension Array {
    var double: _View<Double> {
        return _View(array: self)
    }
}

On Fri, Nov 18, 2016 at 2:00 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
Hi Dave,

Thank you for your answer. I have to admit this is a ‘workaround’ but it will make everything even worse.

From:

public func scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
To:

public subscript(at index: Int) -> (javaScript: String, scope: Document)?

public var scopedJavaScript: Array {
    get { return self }
    set { /* implementation artifact */ }
}
Now I could write code like array.scopedJavaScript.scopedJavaScript.scopedJavaScript and so one, which makes no sense any more.

Where we could simply allow:

public subscript scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
This would ensure that the user can only write something like:

array.scopedJavaScript[at: 42] // get the value
array.scopedJavaScript[at: 42] = (…, …) // set the value
Is there anything that speaks against optionally named subscripts?
Technical reasons?
Swiftiness?

--
Adrian Zubarev
Sent with Airmail

Am 17. November 2016 um 23:33:44, Dave Abrahams via swift-evolution (swift-evolution@swift.org) schrieb:

on Thu Nov 17 2016, Adrian Zubarev <swift-evolution@swift.org> wrote:

> Dear Swift community,
>
> while building a framework for BSON I had the following idea.
>
> Here is a snippet of some code I do have in my module:
>
> extension Array where Element == Document.Value {
>
> public func double(at index: Int) -> Double? {
>
> guard self.startIndex <= index && index < self.endIndex else { return nil }
>
> if case .double(let double) = self[index] {
>
> return double
> }
> return nil
> }
>
> …
> }
> This function is used to query the array and check if the element at the given index is of a
> specific type. Now I would like also to implement a semi-schema setter.
>
> The problem that I see, is the ugliness of the subscript I’d create.
>
> Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript
> the API would look odd array[doubleAt: 42] = 5.0.
>
> Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at
> index: Int) -> …. You can easily imagine that such subscripts would look ugly
> array[scopedJavaScriptAt: 123] = ….
>
> I propose to align the design of subscript with functions where one could optionally give subscript
> a name.
>
> func name(label parameter: Type) -> ReturnType
>
> subscript optionalName(label parameter: Type) -> ReturnType
> This change would make my API nice and
> clean. array.scopedJavaScript[at: 213] = …

You do that by giving your Array a scopedJavaScript property, and
making that indexable.

--
-Dave

_______________________________________________
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


(Adrian Zubarev) #7

Okay this is a view, but this design also implies multiple copies of the array, for the getter and even more for the setter, which could be expensive in some cases.

However the named subscript would allow me to mutate in-place.

Plus another side-effect of such a view design is the ability of replacing the whole storage by swapping out two similar typed views.

var string1 = "hello"
var string2 = "goodbye"

let chars1 = string1.characters
let chars2 = string2.characters

string1.characters = chars2
string2.characters = chars1
// string1 == "goodbye"
// string2 == "hello"

···

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:19:20, Xiaodi Wu (xiaodi.wu@gmail.com) schrieb:

I believe Dave had a design more like this in mind:

struct _View<T> {
    let array: Array<Any>
    subscript(index: Int) -> T? {
        guard index >= 0 && index < array.count else { return nil }
        return array[index] as? T
    }
}

extension Array {
    var double: _View<Double> {
        return _View(array: self)
    }
}

On Fri, Nov 18, 2016 at 2:00 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
Hi Dave,

Thank you for your answer. I have to admit this is a ‘workaround’ but it will make everything even worse.

From:

public func scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
To:

public subscript(at index: Int) -> (javaScript: String, scope: Document)?

public var scopedJavaScript: Array {
    get { return self }
    set { /* implementation artifact */ }
}
Now I could write code like array.scopedJavaScript.scopedJavaScript.scopedJavaScript and so one, which makes no sense any more.

Where we could simply allow:

public subscript scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
This would ensure that the user can only write something like:

array.scopedJavaScript[at: 42] // get the value
array.scopedJavaScript[at: 42] = (…, …) // set the value
Is there anything that speaks against optionally named subscripts?
Technical reasons?
Swiftiness?

--
Adrian Zubarev
Sent with Airmail

Am 17. November 2016 um 23:33:44, Dave Abrahams via swift-evolution (swift-evolution@swift.org) schrieb:

on Thu Nov 17 2016, Adrian Zubarev <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {

public func double(at index: Int) -> Double? {

guard self.startIndex <= index && index < self.endIndex else { return nil }

if case .double(let double) = self[index] {

return double
}
return nil
}


}
This function is used to query the array and check if the element at the given index is of a
specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript
the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at
index: Int) -> …. You can easily imagine that such subscripts would look ugly
array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript
a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and
clean. array.scopedJavaScript[at: 213] = …

You do that by giving your Array a scopedJavaScript property, and
making that indexable.

--
-Dave

_______________________________________________
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


(Adrian Zubarev) #8

Thank you guys for all your suggestions so far.

I understand the idea behind the generic subscript here, they are neat and highly needed, but even this approach won’t solve my issue of clarity here.

The Array I extend here has an Element of type Value which is an enum that wraps other types around (part of BSON).

I’d have to insert a huge pattern matching switch into that generic subscript and unwrap every possible type. Don’t get me wrong, this would work, because the result type is an optional, where I just can return nil if nothing matches.

But again I lose the clarity from the readers prospective, because I don’t know by reading code like array[at: 123] = someValue what kind of subscript I’m using here.

As already suggested, the view workaround would result in the exact the same syntax I look for, but it has it own downsides as I already mentioned (+ every time you’d need to instantiate a new view).

···

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:55:00, Haravikk (swift-evolution@haravikk.me) schrieb:

Could this be addressed by allowing generic constraints on subscripts?
For example, with methods we can currently do:

struct Foo {
var values:[Any] = []

func get<T>(at:Int) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

func get<T>(at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

mutating func set<T>(at:Int, to:T) {
if values.indices.contains(at) { values[at] = to }
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo.get(at: 0, as: Double.self)
let b:Double = foo.get(at: 1)!
let c:Int? = foo.get(at: 2)
let d = foo.get(at: 3, as: Double.self)
let e:Int = foo.get(at: 4)!
let f = foo.get(at: 5, as: Int.self)
i.e- the type is inferred from the call-site either with an explicit variable type, or by passing in the expected type as the second argument, which I think is a pretty neat way to do it.

If we could do the same with subscripts we could do something like:

struct Foo {
var values:[Any] = []

subscript<T>(_ at:Int) -> T? {
get { return values.indices.contains(at) ? values[at] as? T : nil }
set { if values.indices.contains(at) { values[at] = newValue } }
}

subscript<T>(_ at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo[0, as: Double.self]
let b:Double = foo[1]!
let c:Int? = foo[2]
let d = foo[3, as: Double.self]
let e:Int = foo[4]!
let f = foo[5, as: Int.self]

Are generic constraints on subscripts part of the generics manifesto?

On 17 Nov 2016, at 20:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {
          
    public func double(at index: Int) -> Double? {
          
        guard self.startIndex <= index && index < self.endIndex else { return nil }
          
        if case .double(let double) = self[index] {
              
            return double
        }
        return nil
    }
      
    …
}
This function is used to query the array and check if the element at the given index is of a specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at index: Int) -> …. You can easily imagine that such subscripts would look ugly array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and clean. array.scopedJavaScript[at: 213] = …

This also might be the opportunity to rethink the labeling rule on subscripts, but this shall not be the main focus of this pitch.

--
Adrian Zubarev
Sent with Airmail

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


(Xiaodi Wu) #9

Arrays are copy-on-write, and you don't need to allow the storage to be
replaced if you don't want to, because the properties can be read-only.

···

On Fri, Nov 18, 2016 at 02:34 Adrian Zubarev < adrian.zubarev@devandartist.com> wrote:

Okay this is a view, but this design also implies multiple copies of the
array, for the getter and even more for the setter, which could be
expensive in some cases.

However the named subscript would allow me to mutate in-place.

Plus another side-effect of such a view design is the ability of replacing
the whole storage by swapping out two similar typed views.

var string1 = "hello"
var string2 = "goodbye"

let chars1 = string1.characters
let chars2 = string2.characters

string1.characters = chars2
string2.characters = chars1
// string1 == "goodbye"
// string2 == "hello"

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:19:20, Xiaodi Wu (xiaodi.wu@gmail.com) schrieb:

I believe Dave had a design more like this in mind:

struct _View<T> {
    let array: Array<Any>
    subscript(index: Int) -> T? {
        guard index >= 0 && index < array.count else { return nil }
        return array[index] as? T
    }
}

extension Array {
    var double: _View<Double> {
        return _View(array: self)
    }
}

On Fri, Nov 18, 2016 at 2:00 AM, Adrian Zubarev via swift-evolution < > swift-evolution@swift.org> wrote:

Hi Dave,

Thank you for your answer. I have to admit this is a ‘workaround’ but it
will make everything even worse.

From:

public func scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?

To:

public subscript(at index: Int) -> (javaScript: String, scope: Document)?

public var scopedJavaScript: Array {
    get { return self }
    set { /* implementation artifact */ }
}

Now I could write code like
array.scopedJavaScript.scopedJavaScript.scopedJavaScript and so one,
which makes no sense any more.

Where we could simply allow:

public subscript scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?

This would ensure that the user can only write something like:

array.scopedJavaScript[at: 42] // get the value
array.scopedJavaScript[at: 42] = (…, …) // set the value

   - Is there anything that speaks against optionally named subscripts?
   - Technical reasons?
   - Swiftiness?

--
Adrian Zubarev
Sent with Airmail

Am 17. November 2016 um 23:33:44, Dave Abrahams via swift-evolution (
swift-evolution@swift.org) schrieb:

on Thu Nov 17 2016, Adrian Zubarev <swift-evolution@swift.org> wrote:

> Dear Swift community,
>
> while building a framework for BSON I had the following idea.
>
> Here is a snippet of some code I do have in my module:
>
> extension Array where Element == Document.Value {
>
> public func double(at index: Int) -> Double? {
>
> guard self.startIndex <= index && index < self.endIndex else { return
nil }
>
> if case .double(let double) = self[index] {
>
> return double
> }
> return nil
> }
>
> …
> }
> This function is used to query the array and check if the element at the
given index is of a
> specific type. Now I would like also to implement a semi-schema setter.
>
> The problem that I see, is the ugliness of the subscript I’d create.
>
> Currently the code would read nicely let d = array.double(at: 42), but
after change to a subscript
> the API would look odd array[doubleAt: 42] = 5.0.
>
> Don’t get me wrong here, I also have methods with larger names like
public func scopedJavaScript(at
> index: Int) -> …. You can easily imagine that such subscripts would look
ugly
> array[scopedJavaScriptAt: 123] = ….
>
> I propose to align the design of subscript with functions where one
could optionally give subscript
> a name.
>
> func name(label parameter: Type) -> ReturnType
>
> subscript optionalName(label parameter: Type) -> ReturnType
> This change would make my API nice and
> clean. array.scopedJavaScript[at: 213] = …

You do that by giving your Array a scopedJavaScript property, and
making that indexable.

--
-Dave

_______________________________________________
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


(Xiaodi Wu) #10

Sorry, can you explain what you mean when you say you must have a setter?
Why would you mutate the view and not the array itself (`foo[42] =
.double(42)` as opposed to `foo.double[42] = 42`)?

···

On Fri, Nov 18, 2016 at 03:25 Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

Thank you guys for all your suggestions so far.

I understand the idea behind the generic subscript here, they are neat and
highly needed, but even this approach won’t solve my issue of clarity here.

The Array I extend here has an Element of type Value which is an enum
that wraps other types around (part of BSON).

I’d have to insert a huge pattern matching switch into that generic
subscript and unwrap every possible type. Don’t get me wrong, this would
work, because the result type is an optional, where I just can return nil
if nothing matches.

But again I lose the clarity from the readers prospective, because I don’t
know by reading code like array[at: 123] = someValue what kind of
subscript I’m using here.

As already suggested, the view workaround would result in the exact the
same syntax I look for, but it has it own downsides as I already mentioned
(+ every time you’d need to instantiate a new view).

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:55:00, Haravikk (swift-evolution@haravikk.me)
schrieb:

Could this be addressed by allowing generic constraints on subscripts?
For example, with methods we can currently do:

struct Foo {
var values:[Any] = []

func get<T>(at:Int) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

func get<T>(at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

mutating func set<T>(at:Int, to:T) {
if values.indices.contains(at) { values[at] = to }
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo.get(at: 0, as: Double.self)
let b:Double = foo.get(at: 1)!
let c:Int? = foo.get(at: 2)
let d = foo.get(at: 3, as: Double.self)
let e:Int = foo.get(at: 4)!
let f = foo.get(at: 5, as: Int.self)

i.e- the type is inferred from the call-site either with an explicit
variable type, or by passing in the expected type as the second argument,
which I think is a pretty neat way to do it.

If we could do the same with subscripts we could do something like:

struct Foo {
var values:[Any] = []

subscript<T>(_ at:Int) -> T? {
get { return values.indices.contains(at) ? values[at] as? T : nil }
set { if values.indices.contains(at) { values[at] = newValue } }
}

subscript<T>(_ at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo[0, as: Double.self]
let b:Double = foo[1]!
let c:Int? = foo[2]
let d = foo[3, as: Double.self]
let e:Int = foo[4]!
let f = foo[5, as: Int.self]

Are generic constraints on subscripts part of the generics manifesto?

On 17 Nov 2016, at 20:14, Adrian Zubarev via swift-evolution < > swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {

    public func double(at index: Int) -> Double? {

        guard self.startIndex <= index && index < self.endIndex else { return nil }

        if case .double(let double) = self[index] {

            return double
        }
        return nil
    }

    …
}

This function is used to query the array and check if the element at the
given index is of a specific type. Now I would like also to implement a
semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but
after change to a subscript the API would look odd array[doubleAt: 42] =
5.0.

Don’t get me wrong here, I also have methods with larger names like public
func scopedJavaScript(at index: Int) -> …. You can easily imagine that
such subscripts would look ugly array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could
optionally give subscript a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType

This change would make my API nice and clean. array.scopedJavaScript[at:
213] = …

This also might be the opportunity to rethink the labeling rule on
subscripts, but this shall not be the main focus of this pitch.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
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


(Adrian Zubarev) #11

About COW: True but I don’t have any control about the optimization of the whole process. That’s why its unsure if the code will be optimized to reduce extra copies or not.

About read-only: This is not true in my case, because the setter must exist, otherwise the view won’t be able to mutate the parents storage. And you’ll get an error like Cannot assign through subscript: 'VIEW_PROPERTY_NAME_HERE' is a get-only property

With the pitched and simple addition, I don’t have to worry about the issues I just mentioned.

(It’s clearly something for Stage 2, but we can discuss it now.)

···

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:47:58, Xiaodi Wu (xiaodi.wu@gmail.com) schrieb:

Arrays are copy-on-write, and you don't need to allow the storage to be replaced if you don't want to, because the properties can be read-only.
On Fri, Nov 18, 2016 at 02:34 Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:
Okay this is a view, but this design also implies multiple copies of the array, for the getter and even more for the setter, which could be expensive in some cases.

However the named subscript would allow me to mutate in-place.

Plus another side-effect of such a view design is the ability of replacing the whole storage by swapping out two similar typed views.

var string1 = "hello"
var string2 = "goodbye"

let chars1 = string1.characters
let chars2 = string2.characters

string1.characters = chars2
string2.characters = chars1
// string1 == "goodbye"
// string2 == "hello"

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:19:20, Xiaodi Wu (xiaodi.wu@gmail.com) schrieb:

I believe Dave had a design more like this in mind:

struct _View<T> {
    let array: Array<Any>
    subscript(index: Int) -> T? {
        guard index >= 0 && index < array.count else { return nil }
        return array[index] as? T
    }
}

extension Array {
    var double: _View<Double> {
        return _View(array: self)
    }
}

On Fri, Nov 18, 2016 at 2:00 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
Hi Dave,

Thank you for your answer. I have to admit this is a ‘workaround’ but it will make everything even worse.

From:

public func scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
To:

public subscript(at index: Int) -> (javaScript: String, scope: Document)?

public var scopedJavaScript: Array {
    get { return self }
    set { /* implementation artifact */ }
}
Now I could write code like array.scopedJavaScript.scopedJavaScript.scopedJavaScript and so one, which makes no sense any more.

Where we could simply allow:

public subscript scopedJavaScript(at index: Int) -> (javaScript: String, scope: Document)?
This would ensure that the user can only write something like:

array.scopedJavaScript[at: 42] // get the value
array.scopedJavaScript[at: 42] = (…, …) // set the value
Is there anything that speaks against optionally named subscripts?
Technical reasons?
Swiftiness?

--
Adrian Zubarev
Sent with Airmail

Am 17. November 2016 um 23:33:44, Dave Abrahams via swift-evolution (swift-evolution@swift.org) schrieb:

on Thu Nov 17 2016, Adrian Zubarev <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {

public func double(at index: Int) -> Double? {

guard self.startIndex <= index && index < self.endIndex else { return nil }

if case .double(let double) = self[index] {

return double
}
return nil
}


}
This function is used to query the array and check if the element at the given index is of a
specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript
the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at
index: Int) -> …. You can easily imagine that such subscripts would look ugly
array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript
a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and
clean. array.scopedJavaScript[at: 213] = …

You do that by giving your Array a scopedJavaScript property, and
making that indexable.

--
-Dave

_______________________________________________
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


(Rien) #12

Thank you guys for all your suggestions so far.

I understand the idea behind the generic subscript here, they are neat and highly needed, but even this approach won’t solve my issue of clarity here.

The Array I extend here has an Element of type Value which is an enum that wraps other types around (part of BSON).

I’d have to insert a huge pattern matching switch into that generic subscript and unwrap every possible type. Don’t get me wrong, this would work, because the result type is an optional, where I just can return nil if nothing matches.

But again I lose the clarity from the readers prospective, because I don’t know by reading code like array[at: 123] = someValue what kind of subscript I’m using here.

Is that necessary?
Either the user knows the type of his ‘someValue’ or he does not. If not then I assume that he does not need to know. If he needs to know then inspection functions can make the discovery process simple.
In general I try to hide implementation details that do not matter to the user.

Rien.

···

On 18 Nov 2016, at 10:25, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

As already suggested, the view workaround would result in the exact the same syntax I look for, but it has it own downsides as I already mentioned (+ every time you’d need to instantiate a new view).

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:55:00, Haravikk (swift-evolution@haravikk.me) schrieb:

Could this be addressed by allowing generic constraints on subscripts?
For example, with methods we can currently do:

struct Foo {
var values:[Any] = []

func get<T>(at:Int) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

func get<T>(at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

mutating func set<T>(at:Int, to:T) {
if values.indices.contains(at) { values[at] = to }
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo.get(at: 0, as: Double.self)
let b:Double = foo.get(at: 1)!
let c:Int? = foo.get(at: 2)
let d = foo.get(at: 3, as: Double.self)
let e:Int = foo.get(at: 4)!
let f = foo.get(at: 5, as: Int.self)
i.e- the type is inferred from the call-site either with an explicit variable type, or by passing in the expected type as the second argument, which I think is a pretty neat way to do it.

If we could do the same with subscripts we could do something like:

struct Foo {
var values:[Any] = []

subscript<T>(_ at:Int) -> T? {
get { return values.indices.contains(at) ? values[at] as? T : nil }
set { if values.indices.contains(at) { values[at] = newValue } }
}

subscript<T>(_ at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo[0, as: Double.self]
let b:Double = foo[1]!
let c:Int? = foo[2]
let d = foo[3, as: Double.self]
let e:Int = foo[4]!
let f = foo[5, as: Int.self]

Are generic constraints on subscripts part of the generics manifesto?

On 17 Nov 2016, at 20:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {
          
    public func double(at index: Int) -> Double? {
          
        guard self.startIndex <= index && index < self.endIndex else { return nil }
          
        if case .double(let double) = self[index] {
              
            return double
        }
        return nil
    }
      
    …
}

This function is used to query the array and check if the element at the given index is of a specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at index: Int) -> …. You can easily imagine that such subscripts would look ugly array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType

This change would make my API nice and clean. array.scopedJavaScript[at: 213] = …

This also might be the opportunity to rethink the labeling rule on subscripts, but this shall not be the main focus of this pitch.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
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


(Adrian Zubarev) #13

The reason here is because the setter acts like semi-schema setter.

For the given array example:

array.double[at: 42] // would return a nil, if the index is out of bounds, or if the wrapped `Value` instance at the given index is not `.double(Double)`

array.double[at: 42] = 2.0 // would update the value iff the wrapped `Value` instance is `.double(Double)`, otherwise the setter will do nothing
This is a semi-schema approach and different from overriding any existing value at the given index like array[42] = .double(2.0)

About the mutation problem you can find the short talk here, right at the bottom.

If there is no setter for your view, array.double[at: 42] = 2.0 simply won’t work.

Please proof me wrong here, if there is a better way to solve the problem. :slight_smile:

I appreciate any suggestions.

So far we had no concrete arguments agains optionally named subscripts. Anything you dislike about that?

Personally I don’t think they hurt any Swiftiness at all.

···

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 10:39:17, Xiaodi Wu (xiaodi.wu@gmail.com) schrieb:

Sorry, can you explain what you
mean when you say you must have a setter? Why would you mutate the
view and not the array itself (`foo[42] = .double(42)` as opposed
to `foo.double[42] = 42`)?

On Fri, Nov 18, 2016 at 03:25 Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
Thank you guys for all your suggestions so far.

I understand the idea behind the generic subscript here, they are neat and highly needed, but even this approach won’t solve my issue of clarity here.

The Array I extend here has an Element of type Value which is an enum that wraps other types around (part of BSON).

I’d have to insert a huge pattern matching switch into that generic subscript and unwrap every possible type. Don’t get me wrong, this would work, because the result type is an optional, where I just can return nil if nothing matches.

But again I lose the clarity from the readers prospective, because I don’t know by reading code like array[at: 123] = someValue what kind of subscript I’m using here.

As already suggested, the view workaround would result in the exact the same syntax I look for, but it has it own downsides as I already mentioned (+ every time you’d need to instantiate a new view).

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:55:00, Haravikk (swift-evolution@haravikk.me) schrieb:

Could this be addressed by allowing generic constraints on subscripts?
For example, with methods we can currently do:

struct Foo {
var values:[Any] = []

func get<T>(at:Int) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

func get<T>(at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

mutating func set<T>(at:Int, to:T) {
if values.indices.contains(at) { values[at] = to }
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo.get(at: 0, as: Double.self)
let b:Double = foo.get(at: 1)!
let c:Int? = foo.get(at: 2)
let d = foo.get(at: 3, as: Double.self)
let e:Int = foo.get(at: 4)!
let f = foo.get(at: 5, as: Int.self)
i.e- the type is inferred from the call-site either with an explicit variable type, or by passing in the expected type as the second argument, which I think is a pretty neat way to do it.

If we could do the same with subscripts we could do something like:

struct Foo {
var values:[Any] = []

subscript<T>(_ at:Int) -> T? {
get { return values.indices.contains(at) ? values[at] as? T : nil }
set { if values.indices.contains(at) { values[at] = newValue } }
}

subscript<T>(_ at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo[0, as: Double.self]
let b:Double = foo[1]!
let c:Int? = foo[2]
let d = foo[3, as: Double.self]
let e:Int = foo[4]!
let f = foo[5, as: Int.self]

Are generic constraints on subscripts part of the generics manifesto?

On 17 Nov 2016, at 20:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {
           
    public func double(at index: Int) -> Double? {
           
        guard self.startIndex <= index && index < self.endIndex else { return nil }
           
        if case .double(let double) = self[index] {
               
            return double
        }
        return nil
    }
       
    …
}
This function is used to query the array and check if the element at the given index is of a specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at index: Int) -> …. You can easily imagine that such subscripts would look ugly array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType
This change would make my API nice and clean. array.scopedJavaScript[at: 213] = …

This also might be the opportunity to rethink the labeling rule on subscripts, but this shall not be the main focus of this pitch.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
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


(Rien) #14

The reason here is because the setter acts like semi-schema setter.

Ah, ok.
Just wondered because I had a similar case in SwifterJSON. Eventually I settled for the absence of user-supplied type information while creating the JSON object. On read however, type information must be specified for safety (semi-schema read).

For the given array example:

array.double[at: 42] // would return a nil, if the index is out of bounds, or if the wrapped `Value` instance at the given index is not `.double(Double)`

array.double[at: 42] = 2.0 // would update the value iff the wrapped `Value` instance is `.double(Double)`, otherwise the setter will do nothing

This is a semi-schema approach and different from overriding any existing value at the given index like array[42] = .double(2.0)

About the mutation problem you can find the short talk here, right at the bottom.

If there is no setter for your view, array.double[at: 42] = 2.0 simply won’t work.

Please proof me wrong here, if there is a better way to solve the problem. :slight_smile:

I appreciate any suggestions.

So far we had no concrete arguments agains optionally named subscripts. Anything you dislike about that?

No, in fact +1

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

···

On 18 Nov 2016, at 11:45, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Personally I don’t think they hurt any Swiftiness at all.

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 10:39:17, Xiaodi Wu (xiaodi.wu@gmail.com) schrieb:

Sorry, can you explain what you
mean when you say you must have a setter? Why would you mutate the
view and not the array itself (`foo[42] = .double(42)` as opposed
to `foo.double[42] = 42`)?

On Fri, Nov 18, 2016 at 03:25 Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
Thank you guys for all your suggestions so far.

I understand the idea behind the generic subscript here, they are neat and highly needed, but even this approach won’t solve my issue of clarity here.

The Array I extend here has an Element of type Value which is an enum that wraps other types around (part of BSON).

I’d have to insert a huge pattern matching switch into that generic subscript and unwrap every possible type. Don’t get me wrong, this would work, because the result type is an optional, where I just can return nil if nothing matches.

But again I lose the clarity from the readers prospective, because I don’t know by reading code like array[at: 123] = someValue what kind of subscript I’m using here.

As already suggested, the view workaround would result in the exact the same syntax I look for, but it has it own downsides as I already mentioned (+ every time you’d need to instantiate a new view).

--
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 09:55:00, Haravikk (swift-evolution@haravikk.me) schrieb:

Could this be addressed by allowing generic constraints on subscripts?
For example, with methods we can currently do:

struct Foo {
var values:[Any] = []

func get<T>(at:Int) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

func get<T>(at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}

mutating func set<T>(at:Int, to:T) {
if values.indices.contains(at) { values[at] = to }
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo.get(at: 0, as: Double.self)
let b:Double = foo.get(at: 1)!
let c:Int? = foo.get(at: 2)
let d = foo.get(at: 3, as: Double.self)
let e:Int = foo.get(at: 4)!
let f = foo.get(at: 5, as: Int.self)
i.e- the type is inferred from the call-site either with an explicit variable type, or by passing in the expected type as the second argument, which I think is a pretty neat way to do it.

If we could do the same with subscripts we could do something like:

struct Foo {
var values:[Any] = []

subscript<T>(_ at:Int) -> T? {
get { return values.indices.contains(at) ? values[at] as? T : nil }
set { if values.indices.contains(at) { values[at] = newValue } }
}

subscript<T>(_ at:Int, as theType:T.Type) -> T? {
return values.indices.contains(at) ? values[at] as? T : nil
}
}

let foo = Foo(values: [1.5, 2.5, 3.5, 1, 2, 3])
let a = foo[0, as: Double.self]
let b:Double = foo[1]!
let c:Int? = foo[2]
let d = foo[3, as: Double.self]
let e:Int = foo[4]!
let f = foo[5, as: Int.self]

Are generic constraints on subscripts part of the generics manifesto?

On 17 Nov 2016, at 20:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Dear Swift community,

while building a framework for BSON I had the following idea.

Here is a snippet of some code I do have in my module:

extension Array where Element == Document.Value {
           
    public func double(at index: Int) -> Double? {
           
        guard self.startIndex <= index && index < self.endIndex else { return nil }
           
        if case .double(let double) = self[index] {
               
            return double
        }
        return nil
    }
       
    …
}

This function is used to query the array and check if the element at the given index is of a specific type. Now I would like also to implement a semi-schema setter.

The problem that I see, is the ugliness of the subscript I’d create.

Currently the code would read nicely let d = array.double(at: 42), but after change to a subscript the API would look odd array[doubleAt: 42] = 5.0.

Don’t get me wrong here, I also have methods with larger names like public func scopedJavaScript(at index: Int) -> …. You can easily imagine that such subscripts would look ugly array[scopedJavaScriptAt: 123] = ….

I propose to align the design of subscript with functions where one could optionally give subscript a name.

func name(label parameter: Type) -> ReturnType

subscript optionalName(label parameter: Type) -> ReturnType

This change would make my API nice and clean. array.scopedJavaScript[at: 213] = …

This also might be the opportunity to rethink the labeling rule on subscripts, but this shall not be the main focus of this pitch.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
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

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