When you have a collection-of-collections (eg. [[T]]
) and want to perform in-place mutation of the inner elements. With a map-in-place (I like the spelling mutateEach
) that takes its argument inout
, you can simply do a double-map:
outerCollection.mutateEach { innerCollection in
innerCollection.mutateEach { $0 += 1 }
}
If you write the equivalent with out-of-place map, then youāll end up allocating new space for every inner collection as well as the outer collection. The in-place version allocates nothing.
Agreed. The form
prefixes are explicitly stated as being used when the base name is a noun (eg. formUnion
). The names map
and filter
are verbs.
⢠⢠ā¢
I am a strong advocate of the position that functional-programming terminology is almost entirely terrible. The names for operations are abysmal. The meanings of the words that functional programmers use, do not correspond to the actual concepts which they are applied to.
However, the one advantage of those existing names, is that they are terms of art. People know them, and have written about them, and explanations exist for them.
If we think the names are bad enough to warrant changing them, then we should choose ones that we think are better. And that means choosing names that are actually meaningful.
In particular, small changes to the existing names will be even worse than no change. That would lose the benefit of term-of-art recognizability, while retaining the problem of the words meaning the wrong thing.
The existing names are locally-optimal because they are terms of art, so if we want something better that will necessarily involve moving to a different āhillā entirely.
⢠⢠ā¢
If we are going to try to improve these method names, then we should come at it from the perspective of applying the best-practice Swift naming guidelines to replace the current term-of-art spellings:
Reduce
reduce
is a bad name for its job. The operation is about combining elements. A more accurate name would be combine
. As in:
let sum = values.combine(0, +)
.
Map
map
is a reasonable name for its job, because mathematically a map is a transformation and colloquially we talk about mapping one thing to another, but it has a verb-tense that would imply mutation.
Unfortunately, mapped
and mapping
are also wrong. The problem is subject-object ordering. If the mapper were the subject, it would sound fine:
let topoMap = cartographer.mapping(mountainRange)
But we have a situation more like:
let topoMap = mountainRange.mapped(by: cartographer)
An alternative would be transformed
:
let newValues = values.transformed{ $0 + 1 }
This also gives a nice spelling for the in-place variant:
values.transform{ $0 += 1 }
I think I like that even better than mutateEach
.
Filter
Filter has a parity problem. In common parlance, we speak of both filtering out and filtering in. For example a content filter blocks things, and a high-pass filter allows things. However, the concept of filtering a list, such as search results, has become so widely understood even among non-programmers that I think filter
has the strongest case of any as a term of art.
If we really want to use an unambiguous term, I would suggest selectAll
. As in:
let passing = students.selectAll{ $0.grade >= threshold }
Moreover, sometimes we want to get both parities, so it might be worth considering:
let (passing, failing) = students.partitioned{ $0.grade >= threshold }
FlatMap
This is a really clunky name. Even people who know what it means have to stop and think about it. If we want to change it, Iād go with mapAndConcatenate
since itās really about concatenating.
Or, if we change map
to transformed
, then this would be transformedAndConcatenated
.
Yes, itās a lot longer than flatMap
, but itās meaningful. When someone reads transformedAndConcatenated
, they will know what it does.
Heck, even keeping map
as map
, it still a lot easier to understand mapAndConcatenate
than flatMap
.
⢠⢠ā¢
Thinking about map
some more, Iām actually no longer convinced that it implies mutation. I see a similarity between āmapā and mathematical operations like āplusā and āminusā.
⢠⢠ā¢
So after writing all this out, I find that, even though I have significant misgivings about the names given to functional programming concepts, I donāt have a strong desire to change these methods in Swift.
map
is fine.
reduce
isnāt great, but itās not actively harmful.
filter
is technically ambiguous, but people know what filtering a list mean.
flatMap
is weird, so I wouldnāt mind having a more clear spelling, but I can live with it.
The one thing I did decide though, is that Iād like the mutating in-place version of map
to be spelled transform
.
And that it would be nice to have a partition
method for two-sided filtering.