views:

63

answers:

5

Hi, is it posible to have callable objects on ActionScript? For example:

class Foo extends EventDispatcher
{
  Foo() { super(); }

  call(world:String):String
  {
    return "Hello, " + world;
  }
}

And later...

var foo:Foo = new Foo();
trace( foo("World!") );    // Will NOT work
A: 

note: both the constructor and the method declaration miss the keywords public function to even compile, but I suppose that's not the original code. :)

the answer is: you can't.
my question is: what do you want to accomplish?

Functions are the only callable values. And Functions are primitives in ActionScript, much as ints, or Booleans, so there is no meaningful way to extend them.

If you want it to be an object, do it the Java way, defining an ICallable interface, and actually call a method, or just really use a function. closures provide the most simple and flexible possibility to create stateful functions, if that is what you want.

edit: well, you can do this (as an example):

private var fooInst:Foo = new Foo();
protected var foo:Function = fooInst.call;

and then the following workst as you wish:

<mx:Label text="{foo('Whatever')}"/>

its maybe even a little more flexible, although you lose the benefits of strict typing.

greetz
back2dos

back2dos
You are right about the public keyword on the class! But for the constructor is optional (constructors are always public).The reason for this is that I want to simplify an API, so instead of: <mx:Label text="{foo.call('Whatever')}"/>have it to: <mx:Label text="{foo('Whatever')}"/>
CodexDraco
@CodexDraco: so basically you just want a syntactic sugar. well, that is understandable. lazyness is every developer's trait, and sometimes it even yields good results. in this case you'll just have to live with it. also, personally I advise you not to omit "public", even if you can. a proper IDE reduces things like these to few keystrokes. while this "lack of syntactic features" (if you will) requires more typing, it makes things signifficantly clearer. If I read `<mx:Label text="{foo('Whatever')}"/>` I expect foo to be a method of the declaring MXML-Component.
back2dos
Yeah, syntactic sugar. But since the calls to the function were going to be in almost every component there were readability concerns too.
CodexDraco
@CodexDraco: just had an idea, that might help. see my update. :)
back2dos
@back2dos nice! =)
CodexDraco
A: 

Why would you need to do this? (I'm not criticising, just interested!) Functions in AS3 are themselves first-class citizens, and can be passed around as arguments.

e.g.

public function main(foo:Function):void
{
    trace(foo("World!")); // Will work, assuming foo = function(str:String):String {...}
}
danyal
I need the foo 'function' to keep state. Also I want to make foo bindable depending on it's internal state (hence why it inherits from EventDispatcher).
CodexDraco
I've since tried a couple of approaches including one using "extends Function implements IEventDispatcher". But it isn't possible to override the call method. I can't think of a way to make your desired syntax possible but will keep an eye on this thread in case anybody else comes up with something.
danyal
A: 

No, only functions/methods can be called in this way. If the only reason is you want to type fewer characters, then you should shorten the length of the instance names and method names.

davr
Actually, I solved the "problem" using package functions and singletons. I just wanted to see if it was possible to have callable objects as in C++. Thanks.
CodexDraco
A: 

As others had said, you can't have callable objects. However, if for some reason you want to have stateful functions, you can achieve it with help of static class variables and package level functions. For example:

// com/example/foo/Helper.as
package com.example.foo {
    public class Helper {
        private static var _instance:Foo;

        public static var data:String;

        public static function get instance():Helper
        {
            if(!_instance) { _instance = new Helper(); }
            return _instance;
        }
    }
}

// com/example/foo/hello.as
package com.example.foo {
    public function hello(world:String):void
    {
        if(Helper.instance.data)
        {
            trace("Bye, " + Helper.instance.data);
        }
        trace("Hello, " + world);
        Helper.instance.data = world;
    }
}

When used, it will print different things.

hello("World!");   // traces "Hello, World!"
hello("People");   // traces "Bye, World!" and "Hello, People"
CodexDraco
A: 

One option is to use a closure:

public function Foo():Function {
    var bar:String;
    return function (world:String):String {
        var msg:String;
        if (bar) {
            msg = bar + ' says "Hello, ' + world + '"';
        } else {
            msg = "Hello, " + world;
        }
        bar = world;
        return msg;
    }
}
...
var foo = Foo();
trace( foo("World!") );

This is a much simplified case of the larger pattern of implementing objects as functions. As such, it's more useful in languages that support FP but not OOP, but does technically give you a callable "object". The syntax may be a little off, but:

public function createFoo(barInit, ...):Function {
    var slots = {
        greeter: barInit, ...
    };
    var methods = {
        'get': function(name) { return slots[name]; }
        'set': function(name, value) { slots[name] = value; }
        greet: function(whom) {
            var msg = slots.greeter + ' says "Hello, ' + whom + '"'
            slots.greeter = whom;
            return msg;
        },
        ...
    };
    return function (method:String):* {
        args = Array.splice.call(arguments, 1);
        return methods[method].apply(null, args);
    }
}

var foo = createFoo('Kermit');
trace(foo('greet', "World"));
trace(foo('greet', "Sailor"));

You probably don't want to do it in AS.

outis