Crystal: Impossible to iterate over types in a Tuple

Created on 22 Jul 2017  路  4Comments  路  Source: crystal-lang/crystal

It is clear that the original intent of Tuple#types was to return a proper tuple of types that can be usefully iterated over at runtime.
However its behavior indirectly changed somewhere along the way without checking whether it is still useful (specs were updated instead, and the nuance in documentation was lost as well). Now tuple.types is literally the same as typeof(tuple), and I don't see any reason for it to exist in this state.

Strangely enough, within the codebase there is a Tuple.types method that behaves exactly as everyone would expect, but it's hidden in specs.

What I want: iterate over a Tuple's types at runtime, like so:

def create_random_tuple
  {"a" == "a" ? 5 : 5u8, "string"}
end

struct Tuple
  def self.types
    {% begin %}
    {
      {% for type in T %}
        {{type}},
      {% end %}
    }
  {% end %}
  end
end

a = create_random_tuple
typeof(a).types.each do |t|
  p t
  # (Int32 | UInt8)
  # String
end

but without adding my own extension to Tuple, and without this workaround that @Groogy showed.

At the moment this seems impossible:

def create_random_tuple
  {"a" == "a" ? 5 : 5u8, "string"}
end

a = create_random_tuple
a.types.each do |t|
  p t
end
# Error in line 6: undefined method 'each' for Tuple(Int32 | UInt8, String):Class
bug stdlib

Most helpful comment

Should we add Tuple.types then?

All 4 comments

Should we add Tuple.types then?

Something like Tuple.types seems like a good addition (or restoration).
I think that NamedTuple.types should also exist returning a named tuple of key -> type.

But then, if we think in the broader feature, we are looking for a way to get the type arguments of generic types in runtime. Tuple and NamedType are special cases because they don't exhibit in their declaration the T argument, but it is still there.

So if we add expr.class.generic_arguments for all generic types might be the more complete solution. (yet, if we add just for tuple and named tuple i'm fine).

The generic_argument would return a tuple when positional arguments are used or a named tuple when named arguments are used (in well, named tuples). For cases like StaticArray though, the returning tuple will contain the length numeric argument.

The future planning is all fine but is it correct that Tuple#types is entirely useless at the moment?

I agree @oprypin . BTW a shorter implementation would be Tuple.new(*{{T}})

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sergey-kucher picture sergey-kucher  路  66Comments

stugol picture stugol  路  70Comments

benoist picture benoist  路  59Comments

rdp picture rdp  路  112Comments

straight-shoota picture straight-shoota  路  91Comments