Generic Subscripts

Why are subscripts so different, anyway? One would think they are basically sugared functions, but they don’t support so many things that regular functions support. Not just syntax stuff, either - they also don’t support @inline(__always) for some reason…

Where generic subscripts are concerned, there are a couple of different things to express:
- Generic parameter (I can understand various co-ordinates for the data)
- Generic return type (I can construct your preferred representation of the data)
- Generic setter type (I can set the data using various compatible types):

protocol MeaningfulToFoo {}
protocol ConstructableFromFoo {}

struct Foo {
    subscript<Index>(index: Index) where Index: SignedInteger {
        get<T> where T: ConstructableFromFoo { return T(self) }
        set<U> where T: MeaningfulToFoo { self.someProperty = newValue.someData }
    }
}

The syntax looks a bit awkward, though, IMO. I’m wondering if it might be better to have some kind of combined subscript + property behaviours (remember those?) and allow those to be generic instead. Subscripts and properties are very similar anyway - they are both bundles of functions to represent getting and setting data (not just regular-old “get” and “set”, either, but also magic stuff like “mutableAddressWithPinnedNativeOwner”). The only difference is that property getters can’t have parameters — which is something I would also like to see lifted one day (I believe I’ve even seen people asking for “named subscripts” due to the lack of this =P)

- Karl

···

On 12 Jan 2017, at 22:37, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 12, 2017, at 9:53 AM, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Ok, I've got a draft up as a gist: generic-subscripts.md · GitHub

Before I submit it, could someone let me know if adding generics to subscripts would influence the ABI? ( still feel pretty clueless in that area).

It won’t change the ABI of existing subscript calls, but if the standard library introduces new generic subscripts that replace older non-generic subscripts, it will impact ABI.

Slava

Specifically, we currently have to have a _Hashable protocol that has a _toAnyHashable() method in order to implement Dictionary<AnyHashable,*> subscripting using concrete types as keys. A replacement subscript that was generic over all Hashable types would solve this more neatly, and would be source-compatible, but affects the ABI of the stdlib.

Generic subscripts would also make it easier to implement one-sided ranges e.g.

  myCollection[i...] // slice from i to the end

···

On Jan 12, 2017, at 1:37 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

Before I submit it, could someone let me know if adding generics to subscripts would influence the ABI? ( still feel pretty clueless in that area).

It won’t change the ABI of existing subscript calls, but if the standard library introduces new generic subscripts that replace older non-generic subscripts, it will impact ABI.

Hello Chris, thanks for this draft!

May I suggest that the introduction mentions genericity on return type as well?

    subscript<T>(_ index: Int) -> T

(I have database rows in mind.)

Yeah, there’s no reason to restrict it to either just the index or element type.

Slava

···

On Jan 12, 2017, at 9:56 AM, Gwendal Roué via swift-evolution <swift-evolution@swift.org> wrote:

Gwendal

Le 12 janv. 2017 à 18:53, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Ok, I've got a draft up as a gist: generic-subscripts.md · GitHub

Before I submit it, could someone let me know if adding generics to subscripts would influence the ABI? ( still feel pretty clueless in that area).

Thanks!

On Thu, Jan 12, 2017 at 8:57 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Jan 11, 2017, at 11:05 PM, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Okay,

I agree that throwing subscripts would be great to have. Likewise, generic(and maybe even throwing) properties could be useful. However, I think that for this proposal, it makes more sense to focus on just generic subscripts, and mention throwing subscripts as "future improvements"?

There's already a draft proposal covering throwing subscripts. You can mention it's existence, but I don't see a reason to say much.

- Doug

On Wed, Jan 11, 2017 at 8:52 PM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 11, 2017, at 1:32 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 11, 2017, at 12:26 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 10, 2017, at 11:40 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 10, 2017, at 10:34 AM, Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

[Forgot to CC swift-evolution the first time]

When this came up last, it was seen as more so a bug in the current implementation, rather than an explicit choice. There's no need for a proposal, just a JIRA: [SR-115] Support generic constraints in subscripts · Issue #42737 · apple/swift · GitHub <[SR-115] Support generic constraints in subscripts · Issue #42737 · apple/swift · GitHub;

It’s a nontrivial new user-facing feature with new syntax in the language, so it’ll need a proposal. ‘twould be good for the proposal to link to the JIRA ticket.

I’ve only heard positive reactions toward this feature, and it’s something that the standard library could make good use of.

+1, this would be clearly great to happen.

-Chris

I apologize for adding to this topic rather than starting a new one, but I figure people interested in subscripts would be more likely to see my question:

Is there a good reason subscripts cannot throw? Right now you can create a [safe: index] subscript to return an optional but you can't create one that returns an unwrapped value or throws.

Throwing accessors are mostly straightforward, but there is a big conceptual question: what happens if an accessor is called during error propagation? For example:

  objectWithThrowingSubscriptSetter[index].mutatingMethodThatCanThrow()

If the method throws, we currently still call the setter in order to finish the access. If the setter can throw, then, we might end up with multiple errors being thrown at the same time, which isn't good — the language is put in the awkward position of having to invent an arbitrary resolution mechanism.

You might ask: why do we call the setter if an error is thrown? Well, it's complicated. One reason is that the implementation technique we use for generic access to subscripts and properties — accesses where we don't know how the subscript/property is implemented — doesn't know how to distinguish between *finishing* an access normally and *aborting* an access abnormally. Some kinds of property/subscript implementation — ones currently reserved for the standard library, but likely to be eventually offered to users in some form — depend on doing extra work no matter how the access is terminated, e.g. to release a buffer pointer. (In fact, in general this applies even to get/set implementations, because even if we decided not to call the setter when an error was thrown, we would at least need to destroy the index argument that we were going to pass to the setter.) In order to get consistent behavior between generic and non-generic accesses, we've just generally been finishing the access all the time.

I think it would be possible to teach this generic mechanism the difference between finishing and aborting an access, and thus to avoid calling setters or otherwise doing arbitrary work that's allowed to throw during an abort. However, we would first have to decide that those are indeed the correct semantics and that setters should not be called after a throw, and that would be a change in behavior.

John.

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

--
Chris Eidhof
_______________________________________________
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

--
Chris Eidhof
_______________________________________________
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

Ok, I've got a draft up as a gist: generic-subscripts.md · GitHub

Before I submit it, could someone let me know if adding generics to subscripts would influence the ABI? ( still feel pretty clueless in that area).

It won’t change the ABI of existing subscript calls, but if the standard library introduces new generic subscripts that replace older non-generic subscripts, it will impact ABI.

Slava

Why are subscripts so different, anyway? One would think they are basically sugared functions, but they don’t support so many things that regular functions support. Not just syntax stuff, either - they also don’t support @inline(__always) for some reason…

Nice catch with @inline(__always). Please file a JIRA issue, since I’m actively working on this stuff now.

Subscripts are a bit different from ordinary functions because they’re lvalues. You can call mutating members on the result of a subscript, or chain it with another property access or subscript. So the code path in SILGen is a little different than ordinary function calls. This is the reason subscripts cannot have default arguments also. With a bit of refactoring we can unify the code path for forming call arguments in ordinary calls and subscripts, and hopefully the default argument and generic cases will fall out naturally also.

Where generic subscripts are concerned, there are a couple of different things to express:
- Generic parameter (I can understand various co-ordinates for the data)
- Generic return type (I can construct your preferred representation of the data)
- Generic setter type (I can set the data using various compatible types):

I think all of these should be expressed with a single generic signature on the subscript itself. The element type passed to the setter and returned from the getter should be the same IMO, otherwise it’s not clear how it will work.

···

On Jan 12, 2017, at 7:05 PM, Karl Wagner <razielim@gmail.com> wrote:

On 12 Jan 2017, at 22:37, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 12, 2017, at 9:53 AM, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

protocol MeaningfulToFoo {}
protocol ConstructableFromFoo {}

struct Foo {
    subscript<Index>(index: Index) where Index: SignedInteger {
        get<T> where T: ConstructableFromFoo { return T(self) }
        set<U> where T: MeaningfulToFoo { self.someProperty = newValue.someData }
    }
}

The syntax looks a bit awkward, though, IMO. I’m wondering if it might be better to have some kind of combined subscript + property behaviours (remember those?) and allow those to be generic instead. Subscripts and properties are very similar anyway - they are both bundles of functions to represent getting and setting data (not just regular-old “get” and “set”, either, but also magic stuff like “mutableAddressWithPinnedNativeOwner”). The only difference is that property getters can’t have parameters — which is something I would also like to see lifted one day (I believe I’ve even seen people asking for “named subscripts” due to the lack of this =P)

- Karl

Ok, I've got a draft up as a gist: generic-subscripts.md · GitHub

Before I submit it, could someone let me know if adding generics to subscripts would influence the ABI? ( still feel pretty clueless in that area).

It won’t change the ABI of existing subscript calls, but if the standard library introduces new generic subscripts that replace older non-generic subscripts, it will impact ABI.

Slava

Why are subscripts so different, anyway? One would think they are basically sugared functions, but they don’t support so many things that regular functions support. Not just syntax stuff, either - they also don’t support @inline(__always) for some reason…

Nice catch with @inline(__always). Please file a JIRA issue, since I’m actively working on this stuff now.

Subscripts are a bit different from ordinary functions because they’re lvalues. You can call mutating members on the result of a subscript, or chain it with another property access or subscript. So the code path in SILGen is a little different than ordinary function calls.

I would put it a little differently. Subscripts are used to refer to a logical component of a value, the same way that a property is. Subscripts are obviously different from properties because they're parameterized by some sort of index, but otherwise they're very similar in the core sense that the expression by itself just abstractly refers to a component of another value, and it's *how that expression is used* that really determines its semantics. That is, "a[i]" would always be a very odd thing to have as a statement on its own, just like "pi" would be a very odd thing to have as a statement on its own — generally you're reading from it or writing to it, and which one exactly you're doing can result in very different code being executed. It's that contextual sensitivity that makes subscripts quite different from just "sugared functions".

This is the reason subscripts cannot have default arguments also. With a bit of refactoring we can unify the code path for forming call arguments in ordinary calls and subscripts, and hopefully the default argument and generic cases will fall out naturally also.

Right. There isn't any particular reason that subscripts don't support default arguments; it's just a little extra work that we've never done.

Where generic subscripts are concerned, there are a couple of different things to express:
- Generic parameter (I can understand various co-ordinates for the data)
- Generic return type (I can construct your preferred representation of the data)
- Generic setter type (I can set the data using various compatible types):

I think all of these should be expressed with a single generic signature on the subscript itself. The element type passed to the setter and returned from the getter should be the same IMO, otherwise it’s not clear how it will work.

Yes. It's quite important that any particular subscript reference is still a single consistent entity, even if generic; we would not want, say, a read-modify-write access to be able to somehow invoke the getter and setter at different generic arguments, or to traffic in different element types.

I'm also not sure we'd ever want the element type to be inferred from context like this. Generic subscripts as I see it are about being generic over *indexes*, not somehow about presenting a polymorphic value.

John.

···

On Jan 12, 2017, at 11:23 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 12, 2017, at 7:05 PM, Karl Wagner <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 12 Jan 2017, at 22:37, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 12, 2017, at 9:53 AM, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

protocol MeaningfulToFoo {}
protocol ConstructableFromFoo {}

struct Foo {
    subscript<Index>(index: Index) where Index: SignedInteger {
        get<T> where T: ConstructableFromFoo { return T(self) }
        set<U> where T: MeaningfulToFoo { self.someProperty = newValue.someData }
    }
}

The syntax looks a bit awkward, though, IMO. I’m wondering if it might be better to have some kind of combined subscript + property behaviours (remember those?) and allow those to be generic instead. Subscripts and properties are very similar anyway - they are both bundles of functions to represent getting and setting data (not just regular-old “get” and “set”, either, but also magic stuff like “mutableAddressWithPinnedNativeOwner”). The only difference is that property getters can’t have parameters — which is something I would also like to see lifted one day (I believe I’ve even seen people asking for “named subscripts” due to the lack of this =P)

- Karl

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

I think I have a pretty good use case for generic element types: you may want an index to carry the type of its element. For example, suppose you want to have a sort of dictionary whose keys are unique instances of a key class, and whose value type depends on the key instance:

  struct TypedDictionary {
    final class Key<T>: Hashable {
      init(of type: T.Type) {}
      
      var hashValue: Int { return ObjectIdentifier(self).hashValue }
      static func == (lhs: Key, rhs: Key) { return lhs === rhs }
    }
    
    private var storage: [AnyHashable: Any] = [:]
    
    subscript<T>(key: Key<T>) -> T? {
      get {
        return storage[key] as! T?
      }
      set {
        storage[key] = newValue as Any?
      }
    }
  }
  
  let messageKey = TypedDictionary.Key(of: String.self)
  let answerKey = TypedDictionary.Key(of: Int.self)
  
  var myDict = TypedDictionary()
  myDict[messageKey] = "Hello, world!"
  myDict[answerKey] = 42

I've wanted to do things like this in at least three or four different contexts; you could even imagine reflection being implemented this way, with typed `PropertyKey`s and a subscript available on all instances. You can work around it with methods, but it always feels unnatural, with vacuous method names like `value(for:)`.

I do agree that it is probably useless and certainly a little dangerous to have a generic parameter that's completely inferred from the return value, but you could make the same argument for methods, and yet we allow them there.

···

On Jan 13, 2017, at 9:50 AM, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

I'm also not sure we'd ever want the element type to be inferred from context like this. Generic subscripts as I see it are about being generic over *indexes*, not somehow about presenting a polymorphic value.

--
Brent Royal-Gordon
Architechies

Where generic subscripts are concerned, there are a couple of different things to express:
- Generic parameter (I can understand various co-ordinates for the data)
- Generic return type (I can construct your preferred representation of the data)
- Generic setter type (I can set the data using various compatible types):

I think all of these should be expressed with a single generic signature on the subscript itself. The element type passed to the setter and returned from the getter should be the same IMO, otherwise it’s not clear how it will work.

Yes. It's quite important that any particular subscript reference is still a single consistent entity, even if generic; we would not want, say, a read-modify-write access to be able to somehow invoke the getter and setter at different generic arguments, or to traffic in different element types.

I'm also not sure we'd ever want the element type to be inferred from context like this. Generic subscripts as I see it are about being generic over *indexes*, not somehow about presenting a polymorphic value.

This is a consequence of your vision of subscript. If interesting, it is also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Gwendal

Actually I *would* want to be able to do that, though I admit it's the
1% case (or less).

···

on Fri Jan 13 2017, John McCall <swift-evolution@swift.org> wrote:

I'm also not sure we'd ever want the element type to be inferred from
context like this. Generic subscripts as I see it are about being
generic over *indexes*, not somehow about presenting a polymorphic
value.

--
-Dave

Where generic subscripts are concerned, there are a couple of different things to express:
- Generic parameter (I can understand various co-ordinates for the data)
- Generic return type (I can construct your preferred representation of the data)
- Generic setter type (I can set the data using various compatible types):

I think all of these should be expressed with a single generic signature on the subscript itself. The element type passed to the setter and returned from the getter should be the same IMO, otherwise it’s not clear how it will work.

Yes. It's quite important that any particular subscript reference is still a single consistent entity, even if generic; we would not want, say, a read-modify-write access to be able to somehow invoke the getter and setter at different generic arguments, or to traffic in different element types.

I'm also not sure we'd ever want the element type to be inferred from context like this. Generic subscripts as I see it are about being generic over *indexes*, not somehow about presenting a polymorphic value.

This is a consequence of your vision of subscript. If interesting, it is also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Massive +1 to that

···

Sent from my iPhone

On 14 Jan 2017, at 13:50, Gwendal Roué via swift-evolution <swift-evolution@swift.org> wrote:

Gwendal

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

I have very similar needs as well to allow for things to be generic on
return type.

···

On Sat, Jan 14, 2017 at 5:50 AM Gwendal Roué via swift-evolution < swift-evolution@swift.org> wrote:

Where generic subscripts are concerned, there are a couple of different
things to express:
- Generic parameter (I can understand various co-ordinates for the data)
- Generic return type (I can construct your preferred representation of
the data)
- Generic setter type (I can set the data using various compatible types):

I think all of these should be expressed with a single generic signature
on the subscript itself. The element type passed to the setter and returned
from the getter should be the same IMO, otherwise it’s not clear how it
will work.

Yes. It's quite important that any particular subscript reference is
still a single consistent entity, even if generic; we would not want, say,
a read-modify-write access to be able to somehow invoke the getter and
setter at different generic arguments, or to traffic in different element
types.

I'm also not sure we'd ever want the element type to be inferred from
context like this. Generic subscripts as I see it are about being generic
over *indexes*, not somehow about presenting a polymorphic value.

This is a consequence of your vision of subscript. If interesting, it is
also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better
API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Gwendal

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

I’m not sure, but I think that in this case the specific type of these
values is determined at runtime.
Then a safe approach would be separate string: String?, bool: Bool?, int:
Int? computed properties, as it’s done in JSON parsers.

if let bookCount = row.value(named: "bookCount").int {
    ...
}
if let bookCount = row["bookCount"].int {
    ...
}
let bookCount = row.int("bookCount")! // crash if database is corrupt

Additionally, this is an overall bad example of generics. Fields of
database tables can only map to a limited set of static types in Swift,
which are supported by database adapter.

This is a consequence of your vision of subscript. If interesting, it is

···

2017-01-14 16:50 GMT+03:00 Gwendal Roué via swift-evolution < swift-evolution@swift.org>:

also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better
API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Gwendal

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

I agree with Dave. To me, building in a limit so that the return type can't
be inferred seems arbitrary.

···

On Sun, Jan 15, 2017 at 12:41 AM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Fri Jan 13 2017, John McCall <swift-evolution@swift.org> wrote:

> I'm also not sure we'd ever want the element type to be inferred from
> context like this. Generic subscripts as I see it are about being
> generic over *indexes*, not somehow about presenting a polymorphic
> value.

Actually I *would* want to be able to do that, though I admit it's the
1% case (or less).

--
-Dave

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

--
Chris Eidhof

1%? Wouldn't many JSON libraries, for instance, be eager to take advantage
of this?

···

On Sat, Jan 14, 2017 at 3:41 PM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Fri Jan 13 2017, John McCall <swift-evolution@swift.org> wrote:

> I'm also not sure we'd ever want the element type to be inferred from
> context like this. Generic subscripts as I see it are about being
> generic over *indexes*, not somehow about presenting a polymorphic
> value.

Actually I *would* want to be able to do that, though I admit it's the
1% case (or less).

Oh, sorry, I didn't mean to suggest that the value type shouldn't be allowed to depend on the subscript's type parameters at all, just that it should probably be determinable from the index type. I'm not sure that I have a specific technical concern, though, as long as it's a given that any particular access assigns a single type to the value; but I'll need to think about it.

John.

···

On Jan 13, 2017, at 9:33 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

On Jan 13, 2017, at 9:50 AM, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

I'm also not sure we'd ever want the element type to be inferred from context like this. Generic subscripts as I see it are about being generic over *indexes*, not somehow about presenting a polymorphic value.

I think I have a pretty good use case for generic element types: you may want an index to carry the type of its element. For example, suppose you want to have a sort of dictionary whose keys are unique instances of a key class, and whose value type depends on the key instance:

Would you actually want the value type to be inferred, or you would just want it to be allowed to vary according to the index type? Because the former sounds like a huge usability issue.

John.

···

On Jan 14, 2017, at 6:41 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Fri Jan 13 2017, John McCall <swift-evolution@swift.org> wrote:

I'm also not sure we'd ever want the element type to be inferred from
context like this. Generic subscripts as I see it are about being
generic over *indexes*, not somehow about presenting a polymorphic
value.

Actually I *would* want to be able to do that, though I admit it's the
1% case (or less).

Where generic subscripts are concerned, there are a couple of different things to express:
- Generic parameter (I can understand various co-ordinates for the data)
- Generic return type (I can construct your preferred representation of the data)
- Generic setter type (I can set the data using various compatible types):

I think all of these should be expressed with a single generic signature on the subscript itself. The element type passed to the setter and returned from the getter should be the same IMO, otherwise it’s not clear how it will work.

Yes. It's quite important that any particular subscript reference is still a single consistent entity, even if generic; we would not want, say, a read-modify-write access to be able to somehow invoke the getter and setter at different generic arguments, or to traffic in different element types.

I'm also not sure we'd ever want the element type to be inferred from context like this. Generic subscripts as I see it are about being generic over *indexes*, not somehow about presenting a polymorphic value.

This is a consequence of your vision of subscript. If interesting, it is also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

This is not typesafe at all if the type does not depend on the argument. I'd prefer keys carrying the meta information of their respective database column keeping the whole API typesafe.

-Thorsten

···

Am 14.01.2017 um 14:50 schrieb Gwendal Roué via swift-evolution <swift-evolution@swift.org>:

Gwendal

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

I have a throwing style data marshaling layer that uses throwing and return type inference to make the code clean and validation automatic with the optional ability to return default values if error or missing value, etc. This is for validating data coming from external sources into our app. Having generic return type and throwing subscripts would help make this code better for clients IMHO.

class Data {
let number: Int
let name: String
}

....
data.number = try helper.value(forKey: "baz")
data.name = try helper.value(forKey: "foo", otherwise: "bar")
....

.... or ideally something like ....

....
data.number = try helper["baz"]
data.name = try helper["foo", otherwise: "bar"]
....

This is exactly the kind of library I was mentioning. There is a lot of demand for the ability to write these kinds of libraries for various different purposes.

···

Sent from my iPad

On Jan 14, 2017, at 12:17 PM, Shawn Erickson via swift-evolution <swift-evolution@swift.org> wrote:

-Shawn

On Sat, Jan 14, 2017 at 9:45 AM Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:
I’m not sure, but I think that in this case the specific type of these values is determined at runtime.
Then a safe approach would be separate string: String?, bool: Bool?, int: Int? computed properties, as it’s done in JSON parsers.

if let bookCount = row.value(named: "bookCount").int {

    ...

}

if let bookCount = row["bookCount"].int {

    ...

}

let bookCount = row.int("bookCount")! // crash if database is corrupt

Additionally, this is an overall bad example of generics. Fields of database tables can only map to a limited set of static types in Swift, which are supported by database adapter.

2017-01-14 16:50 GMT+03:00 Gwendal Roué via swift-evolution <swift-evolution@swift.org>:

This is a consequence of your vision of subscript. If interesting, it is also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Gwendal

_______________________________________________

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

I have a throwing style data marshaling layer that uses throwing and return
type inference to make the code clean and validation automatic with the
optional ability to return default values if error or missing value, etc.
This is for validating data coming from external sources into our app.
Having generic return type and throwing subscripts would help make this
code better for clients IMHO.

class Data {
let number: Int
let name: String
}

....
data.number = try helper.value(forKey: "baz")
data.name = try helper.value(forKey: "foo", otherwise: "bar")
....

.... or ideally something like ....

....
data.number = try helper["baz"]
data.name = try helper["foo", otherwise: "bar"]
....

-Shawn

···

On Sat, Jan 14, 2017 at 9:45 AM Anton Zhilin via swift-evolution < swift-evolution@swift.org> wrote:

I’m not sure, but I think that in this case the specific type of these
values is determined at runtime.
Then a safe approach would be separate string: String?, bool: Bool?, int:
Int? computed properties, as it’s done in JSON parsers.

if let bookCount = row.value(named: "bookCount").int {

    ...

}

if let bookCount = row["bookCount"].int {

    ...

}

let bookCount = row.int("bookCount")! // crash if database is corrupt

Additionally, this is an overall bad example of generics. Fields of
database tables can only map to a limited set of static types in Swift,
which are supported by database adapter.

2017-01-14 16:50 GMT+03:00 Gwendal Roué via swift-evolution < swift-evolution@swift.org>:

This is a consequence of your vision of subscript. If interesting, it is
also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better
API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Gwendal

_______________________________________________

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

I’m not sure, but I think that in this case the specific type of these values is determined at runtime.
Then a safe approach would be separate string: String?, bool: Bool?, int: Int? computed properties, as it’s done in JSON parsers.

Yup. This is what I do:

var json = VJson()
var name: String?

json[“books”][1][“title”] &= “THHGTTG”

and the reverse

name &= json[“books”][1][“title"]

The overloaded operator ‘&=‘ takes care of the types. (‘=‘ cannot be overloaded)

if let bookCount = row.value(named: "bookCount").int {
    ...
}
if let bookCount = row["bookCount"].int {
    ...
}
let bookCount =
row.int
("bookCount")! // crash if database is corrupt

Additionally, this is an overall bad example of generics. Fields of database tables can only map to a limited set of static types in Swift, which are supported by database adapter.

This is a consequence of your vision of subscript. If interesting, it is also limiting for no real purpose.

As the developer of a Swift database library, I'd like to offer a better API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Gwendal

_______________________________________________
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

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: Swiftrien (Rien) · GitHub
Project: http://swiftfire.nl

···

On 14 Jan 2017, at 18:45, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:
2017-01-14 16:50 GMT+03:00 Gwendal Roué via swift-evolution <swift-evolution@swift.org>:

I’m not sure, but I think that in this case the specific type of these values is determined at runtime.
Then a safe approach would be separate string: String?, bool: Bool?, int: Int? computed properties, as it’s done in JSON parsers.

if let bookCount = row.value(named: "bookCount").int {
    ...
}
if let bookCount = row["bookCount"].int {
    ...
}
let bookCount = row.int <http://row.int/&gt;\(&quot;bookCount&quot;\)\! // crash if database is corrupt
Additionally, this is an overall bad example of generics. Fields of database tables can only map to a limited set of static types in Swift, which are supported by database adapter

Thanks for the compliment! I disagree, of course.

App developers may need to fetch basic database data types, obviously, but also RawRepresentable types based on those types, dates, date components, uuids, serialized JSON, and generally speaking a large and extensible set of serializable types.

This is, I believe, a very good usage of return type genericity, as well as an excellent opportunity for the open/closed principle (https://en.wikipedia.org/wiki/Open/closed_principle\).

Gwendal

···

Le 14 janv. 2017 à 18:45, Anton Zhilin <antonyzhilin@gmail.com> a écrit :

2017-01-14 16:50 GMT+03:00 Gwendal Roué via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

As the developer of a Swift database library, I'd like to offer a better API than the following:

    // Current state of affairs
    let name: String = row.value(named: "name")
    let bookCount: Int = row.value(named: "bookCount")
    let hasBooks: Bool = row.value(named: "bookCount")

Instead, I wish I could offer GRDB.swift would let its users write:

    // With improved subscripts
    let name: String = row["name"]
    let bookCount: Int = row["bookCount"]
    let hasBooks: Bool = row["bookCount"]

And this requires genericity on return type.

Gwendal

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