Description
Today we have:
func BinarySearchFunc[E, T any](x []E, target T, cmp func(E, T) int) (int, bool)
The proposal is to switch it to:
func BinarySearchFunc[E, T any](x []E, target T, cmp func(T, E) int) (int, bool)
Why?
tldr: the current ordering feels like sort.Search, which is awkward because "you have to think in terms of greater-than rather than less-than"
The original BinarySearchFunc
was:
func BinarySearchFunc[E any](x []E, target E, cmp func(E, E) int) (int, bool)
Meaning that if I wanted to implement an inline closure, it was not obvious which argument my target would be. I would most likely implement this as general less function: given left and right, return whether left is less than right.
#57348 proposed allowing the target to be a different type. @rsc asked whether E, T is the standard order here, and @aclements mentioned that it is, given Python's bisect
and Java's binarySearch
. I think the @aclements reply was actually talking about the first two arguments to the binary search function, not the closure's arguments: the first two arguments are always array, then target.
No other language that has a binary search function uses a comparison function similar to BinarySearchFunc's closure: no other language explicitly has the target being the first argument or the second argument. C++'s std::binary_search
existed before lambda expressions and the Compare function never calls out argument ordering (in fact, it flips: here then here). C# is similar to C++, C is just completely type blind through void *
. Java's Arrays.binarySearch
existed since 1.6, before lambdas were introduced in 1.8, and the input Comparator never mentions argument ordering. Rust and Ruby do not even have something similar to slices.BinarySearchFunc
-- they have something similar to sort.Find
.
In #50340, @rogpeppe mentions that sort.Search
is awkward and one of the reasons is that "you have to think in terms of greater-than rather than less-than". This is the basis of the proposal.
When the comparison function uses identical types a person never needs to consider the target in the comparison function. Now that the comparison function supports a different type, if you use a different type, you are thinking backwards. If a person wants to implement the comparator function against a target whose type T
is different from the input slice element type E
, they now have to think in terms of greater-than rather than less-than. The new comparison function feels like sort.Search
.
Example of how I want to write slices.BinarySearchFunc
using sort.Find
, following with how I always actually do write a buggy comparison function and then spend too much time thinking about conditionals: https://go.dev/play/p/RLsTqohcXcA.
Also as a last unimportant aside that might be in a later proposal, I'd actually prefer:
- making it much clearer why I'd use
sort.Find
vs.slices.BinarySearchFunc
- revert x/exp/slices: Allow different types for haystack/needle in BinarySearchFunc #57348 because these multi-type use cases seems better suited for
sort.Find
- mention
sort.Find
in the docs ofslices.BinarySearchFunc
(mostly because I completely forgot about sort.Find and personally prefer the closure way of doing things) - switch
BinarySearchFunc
to be similar tosort.Find
with an index and no target -- the current form seems tailored for primitive types, not large structs