Provide map and flatMap for Set<A> that return a Set

Quite often I use map or flatMap on a Set and need a Set as result again. Both methods return an Array though.

While it is quite easy to turn an Array back into a Set using its initializer init<S>(_ sequence: S) it is still a bit inconvenient.

There are a few questions though: map and flatMap might both return elements that are not Hashable which is a requirement to put them into a Set.
Would it make sense to use Conditional Conformance here and have both methods only return a Set if the result actually can be a Set? Would that be confusing to the developer and/or hard for the type-checker?

Am I missing something obvious here? Maybe all of that is a bad idea after all and there is a better solution?
Thank you in advance for your input.

Since this would require modifying Sequence and it's related hierarchy, we have to be clear about two things:

  • How to implement it so that we can return some arbitrary Collection or more precise data structure instead of Array.

  • Is it worth doing so, considering you can write two additional methods and forget about the inconvenience.

I agree though that it's more natural for map to return a Set if called on a Set.

Personally, I feel that map(_:) should return the same number of elements as it was called with. That is,

let s: Set<Int> = [1, 2, 3]
s.count == s.map { $0 % 2 }.count

Notice that if map(_:) returned a Set this would not necessarily hold true. If this isn't the behavior you're looking for, you're always free to pass the result through the sequence initializer.

3 Likes

Interesting point! I agree with you, but is that actually specified somewhere?

Return Value

An array containing the transformed elements of this sequence.

I guess this is somewhat an allusion to that. Although a mapping in general doesn't follow that rule of course.

According to Wikipedia:

map is the name of a higher-order function that applies a given function to each element of a list, returning a list of results in the same order

map is usually "defined" to be "apply a function on each element in a list", which would make changing the size of the list during the operation highly surprising.

1 Like

This topic is covered in depth in this blog post: Superpowered Sequences. Sequences and Collections in Swift are… | by Jonathan @ Strictly Swift | Medium.

1 Like

I'm curious: if we ever get higher kinded types, would we convert map(_:) or provide a version of map that returned a sequence of the same type?

@anandabits that's a fantastic article. Thank you for sharing. I am not yet through, but right off the bat I can tell that

What’s happening is that that map (and filter, and others in the standard Sequence definition) return Arrays.

is not true anymore with Swift 4.2 – at least for filter. I think courtesy of SE-0174

filter can get away with this because doesn't need to change the type of Element. map and flatMap do need to change the type of Element so they would need a type system feature that doesn't exist yet (highed-kinder types).

3 Likes