Tech Per

22 Jul

AS3 Inconsistency - Unable To Use …(rest) Keyword in Setter

ActionScript3 has the nice feature of a get and set keyword, usable for easily defining properties on classes, while still hiding the actual implementation.

AS3 also has the varargs feature, in the form of the …(rest) keyword. This can be utilized to create methods that take an unspecified number of arguments.

Sadly, I have been unable to combine these two features in the language, as in, implementing a setter, which takes an unspecified amount of arguments. Here is a code sample, with a class Task, which has a set method for tags, which is declared to take a variable amount of arguments:


public class Task {
    private var _tags : ArrayCollection = new ArrayCollection();

    ...

    public function set tags(...tags) : void {
        for each (var tag : Object in tags) {
            if (tag is String) {
                CollectionUtil.addAll(_tags, createTagsFromString(tag as String));
            } else if (tag is Array) {
                CollectionUtil.addAll(_tags, tag as Array);
            } else {
                throw new ArgumentError("value must be either strings or array");
            }
        }

    }

    private static function createTagsFromString(tags : String) : Array {
       ...
    }
}

Now, interesting enough, this compiles (with the flex compiler in the flex3 sdk). But, If I then add the below code, which use the class above:


var myTags : String = "dude, car";
var task : Task = new Task();
task.tags = myTags;

Then it will not compile. Instead, it bails out with this message:

Implicit coercion of a value of type String to an unrelated type Array.        task.tags = tags;

So, normally, the use of the …(rest) keyword should make the compiler (or runtime?) convert arguments into an array. In this case, I would have thought it to convert myTags into a single element array.

If I then add a small method setTags, like this, into my Task class:


public class Task {
    ...

    public function setTags(...newtags) : void {
        this.tags = newtags;
    }

    ...
}

and change the assignment code to tags, to read:


var myTags : String = "dude, car";
var task : Task = new Task();
task.setTags(myTags);

it compiles. Hrmph!

Is this a compiler bug or a “works as designed” and then just misunderstood by me bug?

9 Responses to “AS3 Inconsistency - Unable To Use …(rest) Keyword in Setter”

  1. 1
    Alex Says:

    when using …rest on a method call you wouldn’t put “” around the parameters. In the examples above I see “”, which suggest just one argument. Would you expected a syntax like this to work?

    task.tags = “foo”, 1, ["foo","bar"];

  2. 2
    Ash Says:

    I’m not sure what you’re trying to achieve here.. A setter takes one argument by definition, rather than a list of arguments. How would you intend to pass a list of arguments to your setter?
    myProperty = 1, 2, 3?

    In that case you would just pass an array:
    myProperty = [1, 2, 3];

    and the argument for your setter would be of type Array.

    Otherwise you would just use a regular method, as in your last example :)

  3. 3
    senocular Says:

    This is as designed. You can’t use = to assign more than one value to any given target. Setters are defined in a way that let you create variables that convert = into a method call. So what may have been:

    target.setTags(value);

    can instead be

    target.tags = value;

    However, where you can pass any number of arguments to a function call, you can only use one value in an assignment. There’s no way to do something like

    target.tags = value, value1, valueN;

    The closest thing you have to that is assigning an array at which point you’re then back to dealing with one object (where the setter would be defined with one parameter of the type Array).

  4. 4
    Alex Bustin Says:

    Can’t you just do this… ?????

    public function set tags(value:String) : void {
    var tags:Array = value.split(”,”);
    …….
    }

    …(rest) is of the Array type, so ActionScript is right to assume that you want to set an Array.

    Getters and setters have to be matching in type. If you’re passing in a String to the setter, the getter would return a String. I know you haven’t implemented a matching getter and thats OK, but if you had, it would of had to of returned an Array to match the setter.

    Your second block of code works because you’re using
    …(rest) correctly. But your implementation is buggy, your myTags string will never be automatically split at the comma in ActionScript. So in the setTags method, your newtags Array will always contain only one String element. Passing the newtags Array to this.tags works because this.tags is expecting an Array. Before you were trying to pass a String.

    To use setTags correctly, you would say …

    setTags(”foo”, “bar”, “bob”);

  5. 5
    zwetan Says:

    nope it’s not an AS3 inconsistency and it’s certainly not a bug

    you try to do something that make no sens

    the rest parametter allow you to pass multi arguments to a function

    function foobar( …args )

    foobar( 0, 1, 2, 3 ); // args = [0,1,2,3]

    put this make no sens for a setter

    function set foobar( …args )

    obj.foobar = “you can pass only one argument here”;

    wen you try to do that

    var myTags : String = “dude, car”;
    var task : Task = new Task();
    task.tags = myTags; //here your arg is not magically put into an array

    so when you try to do that
    for each (var tag : Object in tags)

    you trying to do that on a String, not an Array
    hence the implicit coercion error

    I guess what you want is

    public function set tags(tags:Array) : void

    and

    var myTags : String = “dude,car”;
    var task : Task = new Task();
    task.tags = myTags.split(”,”);

    or

    var myTags : Array = ["dude","car"];
    var task : Task = new Task();
    task.tags = myTags;

  6. 6
    Jake Says:

    I think the inconsistency lies in the fact that the compiler allows using ‘…’ in a set method. varargs don’t make much sense in the context of properties (a property is singular by nature).

  7. 7
    polesen Says:

    Thanks for all the comments.

    @Alex: The “dude, car” string is not a try from me, to pass to strings into the setter. This is just the example here. And no, I would not expect task.tags = “foo”, 1, ["foo","bar"] to work. I would expect it to convert my single string var into a one element array.

    @Ash: At times, I am not sure myself, what I want to achive :)

    @Senocular:

    However, where you can pass any number of arguments to a function call, you can only use one value in an assignment. There’s no way to do something like

    target.tags = value, value1, valueN;

    If there is no way to pass any number of arguments to a setter, why does the setter with …rest then compile?

    I would not expect target.tags = value, value1, valueN; to work, but, given that the setter compiles, I would at least expect target.tags = value; to work.

    @Jake: EXACTLY! :-)

  8. 8
    Darren Says:

    I think the Flex compiler is right here. It’s not necessary for the Flex compiler to put in a special check to see that “…rest” is not used as a parameter in a setter because the default behaviour is just to treat it as a normal array - without the normal “…rest” qualities because of the unique nature of setter functions - and catch your error if you try to present it with something else. You seem to be complaining because in this particular case, the compiler won’t do an automatic type conversion for you but it shouldn’t because setters don’t do this - just as it shouldn’t if you had written “var arr:Array = “some_string”. Is it really that surprising that a setter function handles a special type of parameter - which you shouldn’t have given it in the first place because it makes no sense in this context - in a way that you didn’t expect? For your use case, the setTags method is the appropriate solution, not a setter function. In an ideal world, the compiler would issue a warning when you use the “…rest” parameter in a setter but I’m personally comfortable that it’s not slowed down looking for particular use cases like this when an error will be thrown if you use it incorrectly anyway.

  9. 9
    polesen Says:

    @Darren: I seems you are right. I just had an understanding of the set/get properties as just being syntactic sugar, that made the compiler generate actual methods for set and getters, and then convert calls like


    task.tags = foo

    into


    task.setter_tags(foo)

    But hey, I guess they are more special than that then.

Leave a Reply

© 2008 Tech Per | Entries (RSS) and Comments (RSS)

GPS Reviews and news from GPS Gazettewordpress logo