Splatting in `#values_at`

What about on the caller’s side? If the method has a splat parameter, it should be possible to call it “splatting” any Enumerable as the argument.

The use case is #values_at. I find that method mostly useless. You can be collecting indices or keys in an array, but you can’t retrieve their values with collection.values_at(*indices) because only tuples are splattable.

Good point :+1:
I suppose splatting an argument into a splat parameter that expects should be entirely possible.

def foo(*x : *Array(String | Int32))
  x
end

foo(*[1, true])

For #values_at specifically, I believe it should actually have an overload that receives Enumerable and returns mapped values:

class Hash
  def values_at(keys : Enumerable)
    keys.map { |key| self[key] }
  end
end

How would you differentiate that to a values_at that pick out an enumerable used as a key?

Sigh. It’s always a dilemma with splat args overloads. :sad_but_relieved_face:

I suppose we could make keys a required named argument:

class Hash
  def values_at(*, keys : Enumerable)
    keys.map { |key| self[key] }
  end
end

Since the signatures are #values_at(*indices : Int) for Indexable and #values_at(*keys : K) for Hash, #values_at(indices : Enumerable) and #values_at(keys : Enumerable(K)) should work without conflict, no?

Indexable#values_at is fine. But Hash#values_at has is potential for collision if K matches Enumerable.

We could restrict further to Enumerable(K). That should be safe then. But it would be more limiting. Hash key lookup is normally not restricted to K.

Hash key lookup is normally not restricted to K.

It is now in the current implementation of #values_at:

Indeed. But not for Hash#[key].

Regardless it is not free from collisions - if nothing else you can do `Hash(K | Enumerable(K), X)`

Edit: Or at least I assume you can. Generics can be a bit wonky sometimes :sweat_smile:

Yeah. If the Hash key type is K | Enumerable(K), the type restriction for values_at would be Enumerable(K | Enumerable(K)). So Enumerable(K) would match both overloads.

The desired behaviour for #values_at actually already exists in Hash#select(keys : Enumerable) and #select(*keys).

And yeah the splat overload may cause conflicts, including with Enumerable#select(pattern) :see_no_evil_monkey:

So it seems Hash#values_at is intended explicitly for tuples.

And I’d apply the same for Indexable. We could add #select overloads for indexes as well. Please create an issue for that if they seem useful.

It’s a mess, because Hash#select/#reject work on keys, while the same methods in Enumerable work on values. I wouldn’t create an overload in Indexable#select that unexpectedly works on indexes.

How about overloading #[] and #[]??