0

I have a method that might return either a Map or a Vector, and since both types implement KeyedIterableVector<T> specifically implementing KeyedIterable<int, T> — I figured I could cover both cases with a KeyedIterable<arraykey, T> return type. However, even though arraykey is a more general type than int, this doesn't work. For example, the typechecker complains on the following code:

<?hh // strict
class A {
   public static function foo(): KeyedIterable<arraykey, mixed> {
      return Vector{};
      /*
      Invalid return type (Typing[4110])
      This is an array key (int/string)
      It is incompatible with an int
      Considering that this type argument is invariant with respect to KeyedIterable
      */
   }
}

Why can't I do this?

concat
  • 3,107
  • 16
  • 30

1 Answers1

1

This is because KeyedIterable isn't read-only, so can't take a subtype.

For instance, A::foo()->toMap() will have the type Map<arraykey, mixed> from the type signatures, but the actual type Map<int, mixed>.

  • By [the example provided in the docs](http://hhvm.com/blog/9215/covariance-contravariance-and-super-type-constraints) I've just found now, I can understand why generally only read-only entities can be covariant, but I don't understand why `A::foo()->toMap()` actually being `Map` is a problem. I can't imagine anything illegal I can do with it, since anything I can do with the `int` keys I should be able to do with the `arraykey` keys, right? – concat Apr 03 '16 at 01:48
  • 1
    @concat You can add a string key value to the `Map`, which is invalid for `Map` (`$map['foo'] = 'baz';` is valid for `arraykey`, but not `int`) –  Apr 03 '16 at 07:01