crystal icon indicating copy to clipboard operation
crystal copied to clipboard

`Tuple.new` isn't considered an `Enumerable`

Open mattrberry opened this issue 2 years ago • 4 comments

foo : Enumerable(String) = Tuple.new # => Error: type must be Enumerable(String), not Tuple()

I'd expect an empty tuple to be considered an enumerable of any type. Compare this to the following, which works as expected

foo : Enumerable(String) = Tuple(String).new("")

Tested on Crystal 1.5.1

mattrberry avatar Sep 15 '22 22:09 mattrberry

Enumerable is invariant, so there is no relationship between Enumerable(String) and Enumerable(NoReturn), which Tuple() is a subtype of. See #3803. Similarly it can be shown that the subtyping relationship isn't transitive:

class Foo; end
class Bar < Foo; end

{% Tuple(Bar) <= Tuple(Foo) %}      # => true
{% Tuple(Foo) <= Enumerable(Foo) %} # => true
{% Tuple(Bar) <= Enumerable(Foo) %} # => false

This is actually quite problematic.

HertzDevil avatar Sep 15 '22 22:09 HertzDevil

For Crystal 2.0 I'd like it if we could remove the special subtyping rules of Tuple and NamedTuple. They just make things too complex and inconsistent.

asterite avatar Sep 15 '22 23:09 asterite

How would unions between them work if they are no longer covariant?

HertzDevil avatar Sep 16 '22 05:09 HertzDevil

They would be normal unions. Their types wouldn't merge.

asterite avatar Sep 16 '22 09:09 asterite