[swift-evolution] [Draft] Rename Sequence.elementsEqual

Adam Kemp adam_kemp at apple.com
Thu Oct 19 15:49:49 CDT 2017


> On Oct 18, 2017, at 9:58 PM, Thorsten Seitz via swift-evolution <swift-evolution at swift.org> wrote:
> 
> In your earlier emails you wrote
> 
> "To make it concrete, say you write a function that just wraps map:
> func firstNames(ofPeople people: Sequence<Person>) -> Sequence<Person> { return people.map { $0.firstName } } 
>  I want that function to work on both ordered and unordered Sequences, and if you start with an ordered Sequence you should end up with an ordered Sequence. How do I make that work?"
> Your example _won't work even today_ as `map` _returns an Array_ and _not_ a Sequence, like I have written several times now including in the very email you have been answering.

I guess it wouldn’t work exactly as written because Sequence isn’t generic (why? I don’t know…)

Something like this is closer to what I was trying to describe:

func firstNames<SeqPerson>(ofPeople people:SeqPerson) -> AnySequence<String> where SeqPerson : Sequence, SeqPerson.Element : Person {
    return AnySequence(people.map { $0.firstName })
}

(I shudder at how unnecessarily complex that is compared to .Net. I hope this can be improved in the future, or that I am missing something that would make it much simpler)

Since an array is a Sequence the (fixed) code above will work today with either an Array or Set or any other Sequence of Person objects, and its result can be fed into any function that takes in a Sequence of String.

> So you would just write the method with the most general argument type which is required by the method:
> 
> func firstNames(ofPeople people: Iterable<Person>) -> [String] {
>     return people.map { $0.firstName }
> }
> 
> The return type would still be Array _just like today_.

Let me be clearer: I want my method firstNames(ofPeople:) to work on sets just as it would work on any other Sequence. This method doesn’t care what order things are in, but it also doesn’t alter the order so it is suitable for use with both ordered and unordered inputs. Today I can write that method, and it will work for a set or an array or anything else that conforms to Sequence of Person, and it can then be chained with any other method that operates on a Sequence of Person.

In order for your proposal to actually be effective you would need to change map so that it doesn’t always give an ordered result. That means you would have to make it not always return an array. The reason for that is that if you left map alone then you could end up writing code like this:

var people:Set<Person> // comes from elsewhere
var names:[String] // comes from elsewhere
if (people.map { $0.firstName }.elementsEqual(names)) { // using the current name
    // …
}

Notice how you start with an unordered input, but then map gives you an array, which is ordered. Obviously map is useful to apply to sets so it would have to continue accepting whatever protocol Set conforms to. If it still returned Array regardless of the input type then it would convert an unordered input into an ordered output, and that loses all of the benefits you’re hoping to gain by having two types.

So clearly you would need map to not return Array when given an unordered input. It’s important that you understand this. Without changing the return type of map and other similar functions you have a huge hole in the type system that loses most of the benefits you’re going for. You may as well not do the feature if you’re not going to change this.

However, you also can’t just make map always return an unordered output type because then if you started with an ordered input you would get an unordered output, and that doesn’t make sense. That would be really tedious. So now you need two implementations of map: one that takes an unordered input and gives an unordered output, and another that takes an ordered input and returns an ordered output.

Kevin was suggesting that you could make this easier to deal with by introducing a new language feature, but it’s not clear exactly how that would work, and I’m not convinced it would actually solve the problem I’ve described.

This is what I mean when I said people weren’t thinking this all the way through. When you start to actually write out some code and thinking about how that code would work with the proposed split then things start to get a lot more complicated. It’s not just a matter of this one function. You have to think about which types other functions will take and produce, and you have to both avoid losing that new type information as well as avoid making simple code patterns much harder. I haven’t seen any clear explanation for how all of that can be satisfied.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171019/9cf1e410/attachment.html>


More information about the swift-evolution mailing list