Under Review
over 1 year ago

CIQTOOLS-388

Covariant/Contravariant issues with Array/Dictionary

The type checker tries to enforce the contained types of Arrays as specified by their type constraints. So eg

  var x = [1,2,3] as Array<Number>;
  x[1] = "foo"; // type checker error
  x.add("bar"); // type checker error

This is exactly what we want. And since it appears that every attempt to modify x results in a type checker error, it seems that we should be able to rely on x being an Array<Number> throughout its lifetime.

But I can do this:

function foo(x as Array<Number or String>) as Void {
  x.add("foo");
  x[0] = "bar";
}

function bar() as Array<Number> {
  var x = [1,2,3] as Array<Number>;
  foo(x); // <-- type checker is happy with this call
  return x; // <-- type checker is happy that x is an Array<Number>
}

And that completely subverts the type checker. The problem is that the type checker thinks it's ok to pass an Array<Number> to a function taking an Array<Number or String>. And thats true if the function only reads the array - if it can handle arrays that contain both Numbers and Strings, it can certainly handle one that only contains Numbers. So the callee is ok with the parameter. But if the callee modifies the parameter, then the caller won't necessarily be ok with the result.

I reported a similar issue relating to Object types and inheritance a couple of weeks ago, and it's basically the same thing. If a function takes a Menu2 as a parameter, and doesn't modify it, then it's perfectly ok to pass a CustomMenu, or CheckBoxMenu to it. But if it does modify it (eg calling addItem), then there's a problem.

I only see two sensible solutions. Either

  • make the type checker smarter, and have it figure out whether the callee might modify the argument, and if so, don't allow a "more derived type" to be passed in, or
  • extend the type system to indicate whether parameters may be modified. eg "x as const Array" to indicate that the function can't modify the parameter. For this to work, there would also need to be const methods (so that eg Array.size would be const, so that you can call array.size() on a const Array, but Array.add would not).
Parents Comment Children
No Data