Skip to content
Dec 16 2009 / alecmce

The Problem With Vector.<T>

I thought I’d found a bug with respect to Adobe’s new Vector class. I logged it in JIRA, but the answer came back that it is Not A Bug. Respectfully, I disagree. Here’s why.

UPDATE: I may be wrong about this. My brother has written a response: http://bit.ly/9OotIS. For further discussion, please also see the comments below.

The Problem

// create an interface and implementing class
public interface Fruit {}
public class Apple implements Fruit {}

// create a vector of Apple objects and try to cast it to a vector of Fruit objects
public class TheProblemWithVectors
{
	public var fruits:Vector.<Fruit>;

	public function TheProblemWithVectors()
	{
		var apples:Vector.<Apple> = new Vector.<Apple>();
		apples.push(new Apple());

		// THIS THROWS AN ERROR!
		fruits = apples as Vector.<Fruit>;
	}
}

The following is indisputable:

  • All (elements in) apples must be an Apple;
  • All Apples are Fruits;
  • Therefore, (elements in) apples are Fruit.

In which case, why can’t apples be cast as fruits? The Flash player does not accept that a Vector of Fruit could be described as a Vector of Apple. To me this feels like a bug. Does it to you?

Not A Bug?

To Adobe it is Not A Bug:

http://bugs.adobe.com/jira/browse/ASC-3836

According to Chris Peyer who works at Adobe:

This is the expected behavior. Vector. and Vector. are unrelated types no matter what the relation is between Example and ExampleA.

It seems to me that this argument can only stem from the idea that if one object is composed of Fruits and another composed of Apples, that does not mean that the two objects are themselves related:

public class MarketStall
{
	public var forSale:Apple;
}

public class Supermarket
{
	public var forSale:Fruit;
}

There is no relationship between MarketStall and Supermarket in this code.

Surely though the relationship between two Vectors defined by a generic is much stronger than that? The Vector is nothing more than an indexed plurality of the objects that compose it. We know it doesn’t have other unrelated functionality. We can do this:

var fruits:Vector.<Fruit> = new Vector.<Fruit>();

for each (var apple:Apple in apples)
	fruits.push(apple);

without problem, but not this:

var fruits:Vector.<Fruit> = apples as Vector.<Fruit>;

Most importantly however, can’t the Adobe engineers see how damned useful it would be if we could cast Vectors like this? Languages should not get in the way of what is intuitively right. You should not have to struggle with the language like this, or find workarounds. Flash developers are almost always looking for improvements in speed and memory, but here you the language makes you waste memory and take time. Or, use the other workaround and avoid Vector and use Array instead. That’s what I did; what a triumph, Adobe!

Please Vote On This Bug

I’ve been here before! My former Colleague Vizio360 rediscovered this bug about buttonMode=true preventing unloading of SWF files, but it too was resolved by being called Not A Bug. The clamour for it to be reopened has so-far fallen on deaf ears, but the only way anyone will ever take any notice is if you vote and comment.

Currently Adobe consider this issue closed. Pehaps if you comment on the post they will give it second thoughts. (this is edited text after it was pointed out to me that you can’t vote for closed bugs)

http://bugs.adobe.com/jira/browse/ASC-3836

  • Zorglug

    Well, they closed the bug… we can’t vote on it.

    • http://alecmce.com Alec McEachran

      Ah, you spotted a pretty fundamental flaw there Zorglug. Yes, in lieu of voting, comment and perhaps even follow?

  • http://www.lazylady.se/ Ted

    I tried to vote. But since it’s marked closed I couldn’t :/

  • http://noiseandheat.com/ mnem

    It’s a tricky one. Intuition is very subjective. Intuitively, I would have created a vector of the Fruit interface and added the Apples to it, which works, as you mention.

    You suggest that not being able to cast the Vector in the way you wish decreases performance and increases memory usage. However, I’m not certain where you see these losses occuring. So, as a guess, I’m going to assume (probably entirely incorrectly) that what you want to avoid is casting Fruits to Apples by having both a Fruit vector and an Apple vector, both pointing at the same blob of memory containing contiguous references to Apples. Is that close to the mark? If it is, I have further observations :)

    However, regardless of what you are doing, it may be worth bearing in mind what you are actual saying in the original lines.

    Firstly, you are creating a binding contractual agreement that fruits:Vector contains, and will only ever contain, fruits. Then you are creating a binding contractual agreement that apples:Vector contains, and will only ever contain, apples.

    Now, assuming the cast had worked, each time you set a value in the fruits:Vector, the VM would have to ensure the item you were setting was an Apple, so it would need to have a way to connect all cast references to that original source apples:Vector. This would be slow, or more accurately, slower than just making sure in the case of setting an element in foo:Vector. was of a compatible type. Also, when the Vector type is immutable, you can push the checking work up into the compile stage of the process (I think), resulting in faster checking code at runtime, because it omits the check.

    So, from that point of view, Vectors behave as they do for the sake of performance, rather than in spite of performance.

    Or I may be talking rubbish and have missed something fundamental :)

    • http://alecmce.com Alec McEachran

      Yes, you make a very good point. This might be an occasion where I have to eat humble pie. The shift in perspective you offer us to do with mutability: I want to construct a vector of apples in one context and a vector of oranges in another and then create a sell method that takes a vector of fruit as input without having to create a duplicate vector.

      You rightly point out however that I might want to mutate the vector in that utility method by adding a pear. Then I would expect a runtime error because the vector of apples is still “of apples” even though I’m using it as “of fruit” in the context (just as an Apple is still an Apple when referenced as Fruit)

      I will have to think harder, it seems! Thanks for the great comment.

  • Zorglug

    Well, they closed the bug… we can't vote on it.

  • http://alecmce.com alecmce

    Ah, you spotted a pretty fundamental flaw there Zorglug. Yes, in lieu of voting, comment and perhaps even follow?

  • http://www.lazylady.se/ Ted

    I tried to vote. But since it's marked closed I couldn't :/

  • http://increasingbiologicalentropy.wordpress.com/ mnem

    It's a tricky one. Intuition is very subjective. Intuitively, I would have created a vector of the Fruit interface and added the Apples to it, which works, as you mention.

    You suggest that not being able to cast the Vector in the way you wish decreases performance and increases memory usage. However, I'm not certain where you see these losses occuring. So, as a guess, I'm going to assume (probably entirely incorrectly) that what you want to avoid is casting Fruits to Apples by having both a Fruit vector and an Apple vector, both pointing at the same blob of memory containing contiguous references to Apples. Is that close to the mark? If it is, I have further observations :)

    However, regardless of what you are doing, it may be worth bearing in mind what you are actual saying in the original lines.

    Firstly, you are creating a binding contractual agreement that fruits:Vector contains, and will only ever contain, fruits. Then you are creating a binding contractual agreement that apples:Vector contains, and will only ever contain, apples.

    Now, assuming the cast had worked, each time you set a value in the fruits:Vector, the VM would have to ensure the item you were setting was an Apple, so it would need to have a way to connect all cast references to that original source apples:Vector. This would be slow, or more accurately, slower than just making sure in the case of setting an element in foo:Vector.<bar> was of a <bar> compatible type. Also, when the Vector type is immutable, you can push the checking work up into the compile stage of the process (I think), resulting in faster checking code at runtime, because it omits the check.

    So, from that point of view, Vectors behave as they do for the sake of performance, rather than in spite of performance.

    Or I may be talking rubbish and have missed something fundamental :)

  • Alecmce

    Yes, you make a very good point. This might be an occasion where I have to eat humble pie. The shift in perspective you offer us to do with mutability: I want to construct a vector of apples in one context and a vector of oranges in another and then create a sell method that takes a vector of fruit as input without having to create a duplicate vector.

    You rightly point out however that I might want to mutate the vector in that utility method by adding a pear. Then I would expect a runtime error because the vector of apples is still “of apples” even though I'm using it as “of fruit” in the context (just as an Apple is still an Apple when referenced as Fruit)

    I will have to think harder, it seems! Thanks for the great comment.

  • Anonymous

    Sometimes I wish it would work this way, too. FWIW, it doesn’t work in Java either:

    import java.util.*;
    public class TestConvert
    {
    interface Fruit {}
    class Apple implements Fruit {}
    public TestConvert()
    {
    List apples = new ArrayList();
    List fruits = apples;
    }
    }

    The error is on the “fruits = apples” line:

    Type mismatch: cannot convert from List to List

    Bummer!

  • jacksondunstan

    Sometimes I wish it would work this way, too. FWIW, it doesn't work in Java either:

    import java.util.*;
    public class TestConvert
    {
    interface Fruit {}
    class Apple implements Fruit {}
    public TestConvert()
    {
    List<Apple> apples = new ArrayList<Apple>();
    List<Fruit> fruits = apples;
    }
    }

    The error is on the “fruits = apples” line:

    Type mismatch: cannot convert from List<TestConvert.Apple> to List<TestConvert.Fruit>

    Bummer!

  • http://alecmce.com Alec McEachran

    My brother has written a follow up on his blog: http://bit.ly/9OotIS. He says I’m wrong, and I think I agree with him.

  • http://alecmce.com alecmce

    My brother has written a follow up on his blog: http://bit.ly/9OotIS. He says I'm wrong, and I think I agree with him.

  • Pingback: Gotcha – transform.matrix and scaleX/Y at AlecMcE.com

  • Pingback: Why we need Generics in AS3 at AlecMcE.com