Convert empty tuple to an array of a specific type

Is there a way to convert an empty Tuple into an array of a specified type?

I’m generating documentation based on user-definable commands. The argument declaration for the commands must be a Tuple because they’re splatted and passed to a method on the command. I need to be able to turn that tuple into an array, but if the command doesn’t take any arguments, the Tuple is empty, and tuple.map { |e| foo(e) }.to_a returns an Array(NoReturn) no matter what I do, including manual .as inside the block and on the array.

Is there a way to go from Tuple() to Array(T) where T isn’t NoReturn? Or do I have to do the empty? check manually?

This makes sense since the tuple is empty the map technically never runs at all and just returns another empty tuple.

I think your best bet is use a method with a freevar and check the size of the tuple type and key off that. E.g.

tup = Tuple.new
tup2 = {0, "foo"}

def tup_to_array(tup : Tuple(*T)) : Array forall T
  {% if 0 == T.size %}
    [] of String
  {% else %}
    [] of Union(*T)
  {% end %}
end

pp tup_to_array(tup).class  # => Array(String)
pp tup_to_array(tup2).class # => Array(Int32 | String)

But what’s the reasoning for needing it to be an array?

Explicitly creating the array may be helpful:

ary = [] of T
tuple.each do |e|
  ary << foo(e)
end
1 Like

I’m passing the result to an object constructor that only accepts an Array because it’s a JSON::Serializable type. This object is part of a shard, but creating the data structure happens in my application.

Good call. I get so used to using map that I sometimes forget that we can just do it the old-fashioned way.

1 Like

One thing I did for Athena is keep a reference to the Tuple type itself, but store the values as an Array, then use Tuple(*T) - Crystal 1.14.0 to build out the tuple to splat. It’s a pretty slick method. Not sure if it makes sense for your use case tho.