> On Oct 18, 2017, at 9:58 PM, Thorsten Seitz via swift-evolution 
> <swift-evolution@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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to