[Pre-proposal] Replace [Foo] With CollectionType

Firstly, the syntax is about to get a lot cleaner. Soon, your example will
be:

func doSomething<
S: Sequence, T: Sequence, U: Sequence

(foos: S, bars: T, bazzes: U)

where S.Element == Foo, T.Element == Bar, U.Element == Baz

Second, this syntax shows necessary complexity. Case in point: an array is
a Collection with randomly accessible elements, yet in your example you
chose SequenceType (aka Sequence). An instance of a type conforming to
Sequence may or may not have randomly accessible elements, it may or may
not be consumed after one pass, it may or may not have copy-on-write
behavior, and it may or may not be laid out contiguously in memory (which
is not guaranteed for all arrays but is for arrays with elements of
primitive type)--and that's if it's entirely held in memory at all.

By writing out the function declaration the way it's shown above, it's
clear what considerations that function will have to take into account.
Assuming that it can operate on arbitrary sequences just like it can an
array is a recipe for a lot of pain.

···

On Fri, May 27, 2016 at 12:05 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> 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.

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

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

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

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

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.

I think I showed both existential and generic variations didn't I? You certainly could do it either way. Placing the typealias in the protocol is not a bad idea either!

···

Sent from my iPad

On May 28, 2016, at 1:38 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

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

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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I couldn't disagree more. Generic programming is a far more advanced
concept than arrays and dictionaries, and learners should be able to use
them without contending with angle brackets they don't understand.

Fundamentally, even for an experienced user of the language, working with
Collections and Sequences requires more consideration than working with
arrays. It's not just a matter of how these things are spelled. It seems
like you want to punish users who don't have the time or inclination to
genericize their algorithms by making them write more, in the hopes that if
you make working with arrays difficult enough, people will switch to
generic containers. That's bonkers.

···

On Sat, May 28, 2016 at 14:38 Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

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) { … }

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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I couldn't disagree more. Generic programming is a far more advanced concept than arrays and dictionaries, and learners should be able to use them without contending with angle brackets they don't understand.

Fundamentally, even for an experienced user of the language, working with Collections and Sequences requires more consideration than working with arrays. It's not just a matter of how these things are spelled. It seems like you want to punish users who don't have the time or inclination to genericize their algorithms by making them write more, in the hopes that if you make working with arrays difficult enough, people will switch to generic containers. That's bonkers.

It’s not about making working with arrays difficult, but if we can make working with basic generic collections/sequences just as easy, then it makes sense to encourage their use as much as possible. Like in the example below, Sequence.of<Foo> is pretty straightforward, and far more flexible and useful than just using [Foo], as you can trivially change the type of collection you use later.

More complex generics definitely need to be explored separately, but Sequence.of<Foo> should be just as simple to use as [Foo], it’s just a little longer because of the ability to distinguish between sequence and collection.

···

On 28 May 2016, at 20:16, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sat, May 28, 2016 at 14:38 Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 27 May 2016, at 15:31, plx via swift-evolution <swift-evolution@swift.org <mailto: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) { … }

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.

Firstly, the syntax is about to get a lot cleaner. Soon, your example will be:

func doSomething<
S: Sequence, T: Sequence, U: Sequence

(foos: S, bars: T, bazzes: U)

where S.Element == Foo, T.Element == Bar, U.Element == Baz

Second, this syntax shows necessary complexity. Case in point: an array is a
Collection with randomly accessible elements, yet in your example you chose
SequenceType (aka Sequence). An instance of a type conforming to Sequence may or
may not have randomly accessible elements, it may or may not be consumed after
one pass, it may or may not have copy-on-write behavior,

I know it's not really the point you're making, but “has copy-on-write
behavior” is not something we capture in a protocol constraint, and
it probably shouldn't be.

···

on Fri May 27 2016, Xiaodi Wu <swift-evolution@swift.org> wrote:

and it may or may not be laid out contiguously in memory (which is not
guaranteed for all arrays but is for arrays with elements of primitive
type)--and that's if it's entirely held in memory at all.

By writing out the function declaration the way it's shown above, it's clear
what considerations that function will have to take into account. Assuming that
it can operate on arbitrary sequences just like it can an array is a recipe for
a lot of pain.

On Fri, May 27, 2016 at 12:05 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> 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.

    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

    _______________________________________________
    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

Firstly, the syntax is about to get a lot cleaner. Soon, your example will be:

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

I wonder if it would be possible to introduce angle brackets on protocols to enumerate associated-type requirements? AFAIK, currently the angle brackets are not allowed at all, so it would be purely additive. Then, you could do something like:

func doSomething(foos: Sequence<Element == Foo>, bars: Sequence<Element == Bar>, bazzes: Sequence<Element == Baz>)

I don’t know about you, but that looks so much cleaner, for a function that doesn’t itself need to be generic.

(The generalized existentials solution is good, too. However, it requires making a separate typealias for every protocol you want to do this with, whereas the above idea wouldn’t.)

Second, this syntax shows necessary complexity. Case in point: an array is a Collection with randomly accessible elements, yet in your example you chose SequenceType (aka Sequence). An instance of a type conforming to Sequence may or may not have randomly accessible elements, it may or may not be consumed after one pass, it may or may not have copy-on-write behavior, and it may or may not be laid out contiguously in memory (which is not guaranteed for all arrays but is for arrays with elements of primitive type)--and that's if it's entirely held in memory at all.

By writing out the function declaration the way it's shown above, it's clear what considerations that function will have to take into account. Assuming that it can operate on arbitrary sequences just like it can an array is a recipe for a lot of pain.

Yeah, I defaulted to Sequence just because I’ve been writing stuff lately that just iterates through a collection that’s passed in. If what the OP is proposing came to pass, it would need to map to Collection instead. Of course, sometimes all you need is a Sequence, so your point is well-taken. We do need *some* way to simplify this syntax, though.

Charles

···

On May 27, 2016, at 1:53 PM, Xiaodi Wu <xiaodi.wu@gmail.com> 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.

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>)

That’s a really nice solution, which gets even nicer with plx suggestion of putting the typealias within the protocol like this:

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

-Thorsten

···

Am 27.05.2016 um 20:47 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:

On May 27, 2016, at 12:05 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto: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:

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)

Charles

_______________________________________________
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

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>)

That’s a really nice solution, which gets even nicer with plx suggestion of putting the typealias within the protocol like this:

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

That is very nice!

···

Sent from my iPad

On May 28, 2016, at 12:40 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 27.05.2016 um 20:47 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:

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> wrote:

-Thorsten

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)

Charles

_______________________________________________
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