I'm not sure reaching for PHP to dunk on functional languages is the win you think it is?
> Visually, it is basically the same, with clear visual messaging of what has changed.
In order to do this you had to make edits to all but a single line of logic. Literally only one line in your implementation didn't change.
Compare, with Ruby:
# strings
uniq = strings.uniq()
# structs, if they are considered equal based on the
# field in question (strings are just structs and they
# implement equality, so of course this is the same)
uniq = structs.uniq()
# structs, if you want to unique by some mechanism
# other than natural equality
let uniq = structs.uniq(&:id)
With Rust:
# strings
let uniq = strings.unique();
# structs, if they are considered equal based on the
# field in question (strings are just structs and they
# implement equality, so of course this is the same)
let uniq = structs.unique();
# structs, if you want to unique by some mechanism
# other than natural equality
let uniq = structs.unique_by(|s| s.id);
You cannot tell me with a straight face that your version is clearer. You'll note that both languages have essentially the exact same code, which is to say: nearly none at all.
> Distinct could sort and look for consecutive, it could use a hashmap. It can even probe the size of the array to pick the performance-optimal approach. I don't have to care.
which says that you just use an abstraction and it transparently "does the right thing". The C# example showed how abstractions actually don't do that, and instead simply provide a lowest common denominator implementation. The rust examples you used also uses a hashmap unconditionally. The PHP example was showing how when abstractions attempt to do that it ends up even worse - when you move from strings to structs, you get a slowdown.
In practice, different strategies are always implemented as different functions (see python `moreitertools` `unique_justseen` and `unique_everseen`), at which point its no longer an abstraction (which by definition serves multiple purposes) and it just becomes a matter of whether or not the set of disparate helper functions is written by you, the standard library, or a third party. In rust, you would do `vec.sort()`, `vec.dedup()` for one strategy, and call `v.into_iter().unique().collect()` for the other strategy. That is not an "abstraction" [which achieves what you claimed they do].
> Visually, it is basically the same, with clear visual messaging of what has changed.
In order to do this you had to make edits to all but a single line of logic. Literally only one line in your implementation didn't change.
Compare, with Ruby:
With Rust: You cannot tell me with a straight face that your version is clearer. You'll note that both languages have essentially the exact same code, which is to say: nearly none at all.