[Pre-proposal] Replace [Foo] With CollectionType

One thing that I see a lot in code, and sometimes have to stop myself from doing, is using shorthand array types, such as [Foo], in function declarations where CollectionType could just as easily be used. For example, the following two declarations can take collections of values, but the first will only take them in the form of an Array:

  func doSomething(values:[Foo]) { … }
  func doSomething<C:CollectionType where C.Generator.Element:Foo>(values:C) { … }

The latter form is something that new users of Swift tend not to know they can do, and which even experienced Swift developers may not use for the sake of brevity, but it can come up quite a lot. What I’d like to propose is that [Foo], when used in a function, should produce the latter form behind the scenes, requiring the developer to specify Array<Foo> if they actually need it to be an Array for some reason. Though this would become inconsistent with variables/properties which would still have to be Array<Foo> since a type is required.

An alternative would be if we could specify protocol generics in a more succinct form, for example:

  func doSomething(values:Collection<Foo>) { … }
  func doSomething(values:Sequence<Foo>) { … } // Many array functions are linear anyway so could just as easily take sequences

Note: This would not be the same as type-erased wrappers such as AnySequence<Foo>, but rather a shorthand for "Sequence where Generator.Element:Foo"

In essence I’m hoping to discuss whether we should try to remove the temptation to limit functions to arrays only or not, or if there are other ways to encourage more use of sequence and collection for flexibility; I try wherever possible to have my methods take sequences if they can, and only take collections if they need to (and never arrays only), but I can understand why not everyone does this, as it’s not the friendliest thing to add and declaring [Foo] looks so much neater.

Better support for existentials (see the generics manifesto,
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md\)
should obviate the need for any sort of sugar or compiler magic to do this
kind of thing.

typealias AnyCollection<T> = Any<Collection where .Element == T, ...>
func doSomething(collection: AnyCollection<Foo>)

Austin

···

On Tue, May 24, 2016 at 10:57 AM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

One thing that I see a lot in code, and sometimes have to stop myself from
doing, is using shorthand array types, such as [Foo], in function
declarations where CollectionType could just as easily be used. For
example, the following two declarations can take collections of values, but
the first will only take them in the form of an Array:

func doSomething(values:[Foo]) { … }
func doSomething<C:CollectionType where C.Generator.Element:Foo>(values:C)
{ … }

The latter form is something that new users of Swift tend not to know they
can do, and which even experienced Swift developers may not use for the
sake of brevity, but it can come up quite a lot. What I’d like to propose
is that [Foo], when used in a function, should produce the latter form
behind the scenes, requiring the developer to specify Array<Foo> if they
actually need it to be an Array for some reason. Though this would become
inconsistent with variables/properties which would still have to be
Array<Foo> since a type is required.

An alternative would be if we could specify protocol generics in a more
succinct form, for example:

func doSomething(values:Collection<Foo>) { … }
func doSomething(values:Sequence<Foo>) { … } // Many array functions are
linear anyway so could just as easily take sequences

Note: This would not be the same as type-erased wrappers such as
AnySequence<Foo>, but rather a shorthand for "Sequence where
Generator.Element:Foo"

In essence I’m hoping to discuss whether we should try to remove the
temptation to limit functions to arrays only or not, or if there are other
ways to encourage more use of sequence and collection for flexibility; I
try wherever possible to have my methods take sequences if they can, and
only take collections if they need to (and never arrays only), but I can
understand why not everyone does this, as it’s not the friendliest thing to
add and declaring [Foo] looks so much neater.

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

Better support for existentials (see the generics manifesto, https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md\) should obviate the need for any sort of sugar or compiler magic to do this kind of thing.

typealias AnyCollection<T> = Any<Collection where .Element == T, ...>
func doSomething(collection: AnyCollection<Foo>)

That isn't really the same thing, though. Any<Collection> is an existential; it introduces indirection which would not be present in the generic version and, in this case, it erases several associated types, potentially introducing type-unsafety as well.

···

--
Brent Royal-Gordon
Architechies

"that [Foo], when used in a function, should produce the latter form behind
the scenes, requiring the developer to specify Array<Foo> if they actually
need it to be an Array for some reason."

I disagree with this as a solution. I agree that Sequence or Collection is
what is more often 'meant' but what you propose is an awkward to
explain/defend betrayal, in my opinion. If I were learning the language
and found out that a change so fundamental was made and that I could avoid
the change by saying–so literally–the same thing but in a different way, I
would not trust anything. "does something dramatically different happen if
I use the other syntax" becomes a reasonable question.

···

On Tue, May 24, 2016 at 1:57 PM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

One thing that I see a lot in code, and sometimes have to stop myself from
doing, is using shorthand array types, such as [Foo], in function
declarations where CollectionType could just as easily be used. For
example, the following two declarations can take collections of values, but
the first will only take them in the form of an Array:

func doSomething(values:[Foo]) { … }
func doSomething<C:CollectionType where C.Generator.Element:Foo>(values:C)
{ … }

The latter form is something that new users of Swift tend not to know they
can do, and which even experienced Swift developers may not use for the
sake of brevity, but it can come up quite a lot. What I’d like to propose
is that [Foo], when used in a function, should produce the latter form
behind the scenes, requiring the developer to specify Array<Foo> if they
actually need it to be an Array for some reason. Though this would become
inconsistent with variables/properties which would still have to be
Array<Foo> since a type is required.

An alternative would be if we could specify protocol generics in a more
succinct form, for example:

func doSomething(values:Collection<Foo>) { … }
func doSomething(values:Sequence<Foo>) { … } // Many array functions are
linear anyway so could just as easily take sequences

Note: This would not be the same as type-erased wrappers such as
AnySequence<Foo>, but rather a shorthand for "Sequence where
Generator.Element:Foo"

In essence I’m hoping to discuss whether we should try to remove the
temptation to limit functions to arrays only or not, or if there are other
ways to encourage more use of sequence and collection for flexibility; I
try wherever possible to have my methods take sequences if they can, and
only take collections if they need to (and never arrays only), but I can
understand why not everyone does this, as it’s not the friendliest thing to
add and declaring [Foo] looks so much neater.

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

I’m not sure what you mean about introducing type unsafely.

What I mean is that once you do this:

  let x: AnyCollection<Character> = myArrayOfCharacters
  let y: AnyCollection<Character> = myString.characters

Both `x` and `y` have indices of type `Any<Comparable>`, and will now accept each others' indices:

  for i in x.indices {
    print(y[i]) // Oops!
  }

If this rule:

The generalized existentials proposal goes out of its way to be explicit about the fact that only type safe operations would be visible through the existential.

Is trying to say that this isn't the case because APIs using the collection's `Index` are not exposed on an `AnyCollection`, well, then I'm not sure what `AnyCollection` is actually supposed to be used for.

···

--
Brent Royal-Gordon
Architechies

My first question here would be "what's the gain?" I understand
CollectionTypes and arrays may be something different but I myself
admit I don't know and wonder how that change would be a benefit to
us?

···

On 24 May 2016 at 14:57, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

One thing that I see a lot in code, and sometimes have to stop myself from
doing, is using shorthand array types, such as [Foo], in function
declarations where CollectionType could just as easily be used. For example,
the following two declarations can take collections of values, but the first
will only take them in the form of an Array:

func doSomething(values:[Foo]) { … }
func doSomething<C:CollectionType where C.Generator.Element:Foo>(values:C) {
… }

The latter form is something that new users of Swift tend not to know they
can do, and which even experienced Swift developers may not use for the sake
of brevity, but it can come up quite a lot. What I’d like to propose is that
[Foo], when used in a function, should produce the latter form behind the
scenes, requiring the developer to specify Array<Foo> if they actually need
it to be an Array for some reason. Though this would become inconsistent
with variables/properties which would still have to be Array<Foo> since a
type is required.

An alternative would be if we could specify protocol generics in a more
succinct form, for example:

func doSomething(values:Collection<Foo>) { … }
func doSomething(values:Sequence<Foo>) { … } // Many array functions are
linear anyway so could just as easily take sequences

Note: This would not be the same as type-erased wrappers such as
AnySequence<Foo>, but rather a shorthand for "Sequence where
Generator.Element:Foo"

In essence I’m hoping to discuss whether we should try to remove the
temptation to limit functions to arrays only or not, or if there are other
ways to encourage more use of sequence and collection for flexibility; I try
wherever possible to have my methods take sequences if they can, and only
take collections if they need to (and never arrays only), but I can
understand why not everyone does this, as it’s not the friendliest thing to
add and declaring [Foo] looks so much neater.

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

Better support for existentials (see the generics manifesto, https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md\) should obviate the need for any sort of sugar or compiler magic to do this kind of thing.

typealias AnyCollection<T> = Any<Collection where .Element == T, ...>
func doSomething(collection: AnyCollection<Foo>)

That isn't really the same thing, though. Any<Collection> is an existential; it introduces indirection which would not be present in the generic version and, in this case, it erases several associated types, potentially introducing type-unsafety as well.

You are right about the indirection. I mentioned that Joe has talked about making generic arguments have identical behavior to existentials. I just realized that his comment was with respect to current existentials which don’t have associated types. It might also be possible to extend this to existentials which bind all associated types to concrete types, but it would not apply to something like AnyCollection<Foo> which leaves several associated types unbound. So as I mentioned, if we do introduce shorthand for something like AnyCollection we would need to decide whether the shorthand produces an implicit generic parameter or uses the existential.

I’m not sure what you mean about introducing type unsafely. The generalized existentials proposal goes out of its way to be explicit about the fact that only type safe operations would be visible through the existential.

···

On May 24, 2016, at 5:55 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

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

I regret mentioning existentials; I am aware that an existential that isn't
sufficiently constrained will never be able to provide the same guarantees
as a generic type variable. My argument is that even a partially
constrained existential is useful if there is a useful set of APIs that
don't touch the associated types in question. I do not believe that they
fulfill the same role as generics, and I apologize for writing a response
that strongly implied they were.

That being said, my opinion regarding this suggestion is still this: [Foo]
is not an array, Collection<Foo> is not a generic protocol, and I am
strongly against any sort of compiler magic that makes those types mean
anything other than what they appear to be.

Austin

If this rule:

···

> The generalized existentials proposal goes out of its way to be explicit
about the fact that only type safe operations would be visible through the
existential.

Is trying to say that this isn't the case because APIs using the
collection's `Index` are not exposed on an `AnyCollection`, well, then I'm
not sure what `AnyCollection` is actually supposed to be used for.

--
Brent Royal-Gordon
Architechies

> I’m not sure what you mean about introducing type unsafely.

What I mean is that once you do this:

        let x: AnyCollection<Character> = myArrayOfCharacters
        let y: AnyCollection<Character> = myString.characters

Both `x` and `y` have indices of type `Any<Comparable>`, and will now
accept each others' indices:

        for i in x.indices {
                print(y[i]) // Oops!
        }

If this rule:

> The generalized existentials proposal goes out of its way to be explicit
about the fact that only type safe operations would be visible through the
existential.

Is trying to say that this isn't the case because APIs using the
collection's `Index` are not exposed on an `AnyCollection`, well, then I'm
not sure what `AnyCollection` is actually supposed to be used for.

If there's any way that the rules that I've proposed can be relaxed without
sacrificing type safety, I would love to hear it. I think the difference in
'power' in this regard between a function that uses generic types and a
function that uses existentials is something inherent to how each works,
though.

···

On Tue, May 24, 2016 at 4:24 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

--
Brent Royal-Gordon
Architechies

I’m not sure what you mean about introducing type unsafely.

What I mean is that once you do this:

  let x: AnyCollection<Character> = myArrayOfCharacters
  let y: AnyCollection<Character> = myString.characters

Both `x` and `y` have indices of type `Any<Comparable>`, and will now accept each others' indices:

  for i in x.indices {
    print(y[i]) // Oops!
  }

If this rule:

The generalized existentials proposal goes out of its way to be explicit about the fact that only type safe operations would be visible through the existential.

Is trying to say that this isn't the case because APIs using the collection's `Index` are not exposed on an `AnyCollection`, well, then I'm not sure what `AnyCollection` is actually supposed to be used for.

Yeah, this is actually a good point. We will probably have to implement the type-erased wrappers manually if we want this behavior. I don’t know for sure but I imagine maybe it is considered acceptable in this case because you can already hit a fatal error with a bad array index anyway, so it isn’t totally unexpected.

···

On May 24, 2016, at 6:24 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

--
Brent Royal-Gordon
Architechies

I don’t see much usefulness in this shorthand.

···

On 24 May 2016, at 19:57, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

One thing that I see a lot in code, and sometimes have to stop myself from doing, is using shorthand array types, such as [Foo], in function declarations where CollectionType could just as easily be used. For example, the following two declarations can take collections of values, but the first will only take them in the form of an Array:

  func doSomething(values:[Foo]) { … }
  func doSomething<C:CollectionType where C.Generator.Element:Foo>(values:C) { … }

The latter form is something that new users of Swift tend not to know they can do, and which even experienced Swift developers may not use for the sake of brevity, but it can come up quite a lot. What I’d like to propose is that [Foo], when used in a function, should produce the latter form behind the scenes, requiring the developer to specify Array<Foo> if they actually need it to be an Array for some reason. Though this would become inconsistent with variables/properties which would still have to be Array<Foo> since a type is required.

An alternative would be if we could specify protocol generics in a more succinct form, for example:

  func doSomething(values:Collection<Foo>) { … }
  func doSomething(values:Sequence<Foo>) { … } // Many array functions are linear anyway so could just as easily take sequences

Note: This would not be the same as type-erased wrappers such as AnySequence<Foo>, but rather a shorthand for "Sequence where Generator.Element:Foo"

In essence I’m hoping to discuss whether we should try to remove the temptation to limit functions to arrays only or not, or if there are other ways to encourage more use of sequence and collection for flexibility; I try wherever possible to have my methods take sequences if they can, and only take collections if they need to (and never arrays only), but I can understand why not everyone does this, as it’s not the friendliest thing to add and declaring [Foo] looks so much neater.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

My first question here would be "what's the gain?" I understand
CollectionTypes and arrays may be something different but I myself
admit I don't know and wonder how that change would be a benefit to
us?

Instead of functions only accepting an Array, which is a specific type, they would accept any collection type, including all the lazy collections.

I agree that Sequence or Collection is what is more often 'meant' but what you propose is an awkward to explain/defend betrayal, in my opinion.

I’m not sure I’d call it a “betrayal”, there aren’t many methods on Array that aren’t covered by CollectionType, and those that are aren’t available as an immutable type anyway (which all function parameters are now), so to do anything array-specific requires extra steps that a fix-it might be able to cover if CollectionType is too general for your method.

The problem is that when there’s a shorthand, people tend to use it, but the problem is that most of the time any collection or sequence type will do, and that an array isn’t actually required at all. After seeing a bunch of examples of [Foo] before I knew about how to handle generics properly I found it a tricky habit to break. Could be more of a failing in the way Swift is being taught right now perhaps, but then generics can’t really be taught any sooner either, it’s tricky.

It may be too much of a breaking change I’ll grant, but at the same time it seems better to shake this up and get people using the correct method for passing/receiving collections by default.

···

On 24 May 2016, at 21:14, Leonardo Pessoa <me@lmpessoa.com> wrote:
On 24 May 2016, at 21:16, T.J. Usiyan <griotspeak@gmail.com> wrote:

On 24 May 2016, at 21:15, Austin Zheng <austinzheng@gmail.com> wrote:

Better support for existentials (see the generics manifesto, https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md\) should obviate the need for any sort of sugar or compiler magic to do this kind of thing.

typealias AnyCollection<T> = Any<Collection where .Element == T, ...>
func doSomething(collection: AnyCollection<Foo>)

Hmm, that does look like it could cover it, thanks for linking this! Just to double check, but Any is a new root for all types then, so this isn’t the same as a type-erased wrapper?

Late response!

As-specified, although I understand the *motivation* for this suggestion, I can’t support the specific proposal whatsoever.

That said, I do think there’d be a lot of value in a way to add “convenience type predicates” (terrible name, please improve!) to declarations, so that e.g.:

  protocol Sequence {

    // one possibility for a declaration:
    typealias of<E> == S: Self where S.Iterator.Element == E

  }

…would allow you to write your examples like this:

  // sequence-accepting variant
  func doSomething<S:Sequence.of<Foo>>(values: S) { … }

  // hopefully, this would work also:
  func doSomething<C:Collection.of<Foo>>

…either as an extension of the existing generic-typealias syntax or as a separate-but-similar feature.

For the Sequence/Collection it’s a lot of work for IMHO a rather minor convenience, but for more-complex type associated-type relationships it could start to pay its own way.

···

On May 24, 2016, at 12:57 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

One thing that I see a lot in code, and sometimes have to stop myself from doing, is using shorthand array types, such as [Foo], in function declarations where CollectionType could just as easily be used. For example, the following two declarations can take collections of values, but the first will only take them in the form of an Array:

  func doSomething(values:[Foo]) { … }
  func doSomething<C:CollectionType where C.Generator.Element:Foo>(values:C) { … }

The latter form is something that new users of Swift tend not to know they can do, and which even experienced Swift developers may not use for the sake of brevity, but it can come up quite a lot. What I’d like to propose is that [Foo], when used in a function, should produce the latter form behind the scenes, requiring the developer to specify Array<Foo> if they actually need it to be an Array for some reason. Though this would become inconsistent with variables/properties which would still have to be Array<Foo> since a type is required.

An alternative would be if we could specify protocol generics in a more succinct form, for example:

  func doSomething(values:Collection<Foo>) { … }
  func doSomething(values:Sequence<Foo>) { … } // Many array functions are linear anyway so could just as easily take sequences

Note: This would not be the same as type-erased wrappers such as AnySequence<Foo>, but rather a shorthand for "Sequence where Generator.Element:Foo"

In essence I’m hoping to discuss whether we should try to remove the temptation to limit functions to arrays only or not, or if there are other ways to encourage more use of sequence and collection for flexibility; I try wherever possible to have my methods take sequences if they can, and only take collections if they need to (and never arrays only), but I can understand why not everyone does this, as it’s not the friendliest thing to add and declaring [Foo] looks so much neater.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

My first question here would be "what's the gain?" I understand
CollectionTypes and arrays may be something different but I myself
admit I don't know and wonder how that change would be a benefit to
us?

Instead of functions only accepting an Array, which is a specific type, they would accept any collection type, including all the lazy collections.

I agree that Sequence or Collection is what is more often 'meant' but what you propose is an awkward to explain/defend betrayal, in my opinion.

I’m not sure I’d call it a “betrayal”, there aren’t many methods on Array that aren’t covered by CollectionType, and those that are aren’t available as an immutable type anyway (which all function parameters are now), so to do anything array-specific requires extra steps that a fix-it might be able to cover if CollectionType is too general for your method.

The problem is that when there’s a shorthand, people tend to use it, but the problem is that most of the time any collection or sequence type will do, and that an array isn’t actually required at all. After seeing a bunch of examples of [Foo] before I knew about how to handle generics properly I found it a tricky habit to break. Could be more of a failing in the way Swift is being taught right now perhaps, but then generics can’t really be taught any sooner either, it’s tricky.

It may be too much of a breaking change I’ll grant, but at the same time it seems better to shake this up and get people using the correct method for passing/receiving collections by default.

The idea of shorthand for commonly used generics and / or existentials is interesting. I’m not sure stealing the array syntax which is both familiar and similar to other languages is the right approach but it is worth considering.

If we do go down this path I think an important part of the discussion is whether the shorthand introduces an implicit generic parameter, whether it uses an existential, or whether the user has control over this. Joe Groff has mentioned that he would like these to have the same behavior eventually, but that is not the case today. I’m not sure what is involved in making the behavior the same or what the timeframe for that might look like.

Better support for existentials (see the generics manifesto, https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md\) should obviate the need for any sort of sugar or compiler magic to do this kind of thing.

typealias AnyCollection<T> = Any<Collection where .Element == T, ...>
func doSomething(collection: AnyCollection<Foo>)

Hmm, that does look like it could cover it, thanks for linking this! Just to double check, but Any is a new root for all types then, so this isn’t the same as a type-erased wrapper?

Yes, generalized existentials are roughly equivalent to type-erased wrappers except you don’t have to write them manually.

···

On May 24, 2016, at 5:45 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 24 May 2016, at 21:14, Leonardo Pessoa <me@lmpessoa.com <mailto:me@lmpessoa.com>> wrote:
On 24 May 2016, at 21:16, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:
On 24 May 2016, at 21:15, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

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

Among other things, one practical advantage of this is that since ArraySlice conforms to CollectionType, one could simply subscript an array if they wanted to send only a subset of its contents to a method, without having to incur the performance cost of creating a new array.

Charles

···

On May 24, 2016, at 5:45 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 24 May 2016, at 21:14, Leonardo Pessoa <me@lmpessoa.com <mailto:me@lmpessoa.com>> wrote:

My first question here would be "what's the gain?" I understand
CollectionTypes and arrays may be something different but I myself
admit I don't know and wonder how that change would be a benefit to
us?

Instead of functions only accepting an Array, which is a specific type, they would accept any collection type, including all the lazy collections.

Sorry. That sentence should read: [Foo] is an array. Collection<Foo> means
nothing in Swift right now, but it really does look awfully like a generic
protocol by analogy.

Since I'm here, I might as well keep on digging...

Right now, we have good syntax delineation. Generic types and functions are
immediately obvious because of the characteristic angle brackets placed
after the function or type name (with the exception of the three big
sugared generic types in the stdlib - Array, Dictionary, and Optional). We
also have protocol<...> to build an existential out of one or more
protocols, although there has been some discussion in other threads about
whether people might confuse that syntax with generic type syntax and
whether it should be changed as a result. Associated types don't use the
angle brackets, but they aren't really generic types - they can be used to
constrain generic types, but they also show up in places like defining
constrained protocol extensions, whose methods don't necessarily need to be
used in a generic function.

I think the fact that it's reasonably easy to tell these three loosely
defined families of type features apart is a valuable aspect of the
language's design, and loosening this ability should be done very carefully.

Austin

···

On Tue, May 24, 2016 at 7:19 PM, Austin Zheng <austinzheng@gmail.com> wrote:

I regret mentioning existentials; I am aware that an existential that
isn't sufficiently constrained will never be able to provide the same
guarantees as a generic type variable. My argument is that even a partially
constrained existential is useful if there is a useful set of APIs that
don't touch the associated types in question. I do not believe that they
fulfill the same role as generics, and I apologize for writing a response
that strongly implied they were.

That being said, my opinion regarding this suggestion is still this: [Foo]
is not an array, Collection<Foo> is not a generic protocol, and I am
strongly against any sort of compiler magic that makes those types mean
anything other than what they appear to be.

Austin

If this rule:

> The generalized existentials proposal goes out of its way to be
explicit about the fact that only type safe operations would be visible
through the existential.

Is trying to say that this isn't the case because APIs using the
collection's `Index` are not exposed on an `AnyCollection`, well, then I'm
not sure what `AnyCollection` is actually supposed to be used for.

--
Brent Royal-Gordon
Architechies

> I’m not sure what you mean about introducing type unsafely.

What I mean is that once you do this:

        let x: AnyCollection<Character> = myArrayOfCharacters
        let y: AnyCollection<Character> = myString.characters

Both `x` and `y` have indices of type `Any<Comparable>`, and will now accept each others' indices:

        for i in x.indices {
                print(y[i]) // Oops!
        }

If this rule:

> The generalized existentials proposal goes out of its way to be explicit about the fact that only type safe operations would be visible through the existential.

Is trying to say that this isn't the case because APIs using the collection's `Index` are not exposed on an `AnyCollection`, well, then I'm not sure what `AnyCollection` is actually supposed to be used for.

If there's any way that the rules that I've proposed can be relaxed without sacrificing type safety, I would love to hear it. I think the difference in 'power' in this regard between a function that uses generic types and a function that uses existentials is something inherent to how each works, though.

The primary difference is the freedom to accept an index which is invalid and call fatalError when you get it. We don't want existentials to do something that. But with user-defined types we have the flexibility to do that if we decide it is the right design.

···

Sent from my iPad

On May 24, 2016, at 9:35 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Tue, May 24, 2016 at 4:24 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

--
Brent Royal-Gordon
Architechies

Is it really that minor, though? For something so commonly encountered as methods that take sequences/collections, this:

func doSomething(foos: [Foo], bars: [Bar], bazzes: [Baz])

is not only a whole lot easier to type, but is worlds clearer to read than:

func doSomething<S: SequenceType, T: SequenceType, U: SequenceType where S.Generator.Element == Foo, T.Generator.Element == Bar, U.Generator.Element == Baz>(foos: S, bars: T, bazzes: U)

Not only is the latter line intimidating to look at as is, but it separates the contained type from the parameters themselves. Given how unwieldy this second form is, it seems almost certain that the former line will be used more frequently in the real world.

Charles

···

On May 27, 2016, at 9:31 AM, plx via swift-evolution <swift-evolution@swift.org> wrote:

For the Sequence/Collection it’s a lot of work for IMHO a rather minor convenience, but for more-complex type associated-type relationships it could start to pay its own way.

This is a nice alternative; would it actually need to be declared in the angle brackets? Could it be done as:

  func doSomething(values:Sequence.of<Foo>) { … }

As long as it would work the same as a generic declaration this could be a good way to do it, either that or a SequenceOf type alias as mentioned.

Still, I kind of feel like we need to do something with the array type shorthand, but I wonder if perhaps we could just get rid of it altogether, to prevent its use entirely? i.e- all instances of [Foo] can be replaced with Array<Foo>, but we would encourage the use of Sequence.of/SequenceOf/Collection.of/CollectionOf first wherever possible.

As more types become available that are ArrayLiteralConvertible it seems like we should discourage restriction of a parameter to Array except when a developer explicitly chooses it. This problem will come up with the Dictionary type shorthand as well if Swift gets some kind of Map protocol to abstract it, and we don’t even have a Set-specific syntax so it seems like it may be fairer to remove these shorthands.

···

On 27 May 2016, at 15:31, plx via swift-evolution <swift-evolution@swift.org> wrote:

  protocol Sequence {
    typealias of<E> == S: Self where S.Iterator.Element == E
  }

  // sequence-accepting variant
  func doSomething<S:Sequence.of<Foo>>(values: S) { … }

For the Sequence/Collection it’s a lot of work for IMHO a rather minor convenience, but for more-complex type associated-type relationships it could start to pay its own way.

Is it really that minor, though? For something so commonly encountered as methods that take sequences/collections, this:

func doSomething(foos: [Foo], bars: [Bar], bazzes: [Baz])

is not only a whole lot easier to type, but is worlds clearer to read than:

func doSomething<S: SequenceType, T: SequenceType, U: SequenceType where S.Generator.Element == Foo, T.Generator.Element == Bar, U.Generator.Element == Baz>(foos: S, bars: T, bazzes: U)

Not only is the latter line intimidating to look at as is, but it separates the contained type from the parameters themselves. Given how unwieldy this second form is, it seems almost certain that the former line will be used more frequently in the real world.

When generalized existentials are introduced (Austin Zheng has a proposal for this) you will be able to do this (assuming we switch to the `&` syntax:

typealias SequenceOf<T> = Sequence where .Element == T

func doSomething(foos: SequenceOf<Foo>, bars: SequenceOf<Bar>, bazzes: SequenceOf<Baz>)

It’s still slightly more verbose than the array shorthand, but it’s a far cry from what you have to do with generics today.

If you wanted it to be generic you could write it as:

func doSomething<S: SequenceOf<Foo>, T: SequenceOf<Bar>, SequenceOf<Baz>(foos: S, bars: T, bazzes: U)

···

On May 27, 2016, at 12:05 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On May 27, 2016, at 9:31 AM, plx via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Charles

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